1
0
mirror of https://github.com/bitwarden/mobile synced 2025-01-28 17:29:18 +01:00

New Android attachment handling to support saving or opening attachments (#751)

* New Android attachment handling to support saving or opening (when available) attachments

* Simplified options dialog logic & changed error text
This commit is contained in:
Matt Portune 2020-03-02 22:14:14 -05:00 committed by GitHub
parent 70c49922b0
commit 2b1d186611
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 126 additions and 15 deletions

View File

@ -230,15 +230,8 @@ namespace Bit.Droid.Services
string mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension); string mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension);
if(mimeType == null) if(mimeType == null)
{ {
if(extension == "json") // Unable to identify so fall back to generic "any" type
{ mimeType = "*/*";
// Explicit support for json since older versions of Android don't recognize the extension
mimeType = "text/json";
}
else
{
return false;
}
} }
var intent = new Intent(Intent.ActionCreateDocument); var intent = new Intent(Intent.ActionCreateDocument);

View File

@ -1,4 +1,5 @@
using Bit.App.Resources; using System;
using Bit.App.Resources;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using System.Collections.Generic; using System.Collections.Generic;
@ -76,6 +77,18 @@ namespace Bit.App.Pages
} }
}); });
} }
else if(message.Command == "selectSaveFileResult")
{
Device.BeginInvokeOnMainThread(() =>
{
var data = message.Data as Tuple<string, string>;
if(data == null)
{
return;
}
_vm.SaveFileSelected(data.Item1, data.Item2);
});
}
}); });
await LoadOnAppearedAsync(_scrollView, true, async () => await LoadOnAppearedAsync(_scrollView, true, async () =>
{ {

View File

@ -34,6 +34,8 @@ namespace Bit.App.Pages
private bool _totpLow; private bool _totpLow;
private DateTime? _totpInterval = null; private DateTime? _totpInterval = null;
private string _previousCipherId; private string _previousCipherId;
private byte[] _attachmentData;
private string _attachmentFilename;
public ViewPageViewModel() public ViewPageViewModel()
{ {
@ -405,10 +407,19 @@ namespace Bit.App.Pages
return; return;
} }
} }
var canOpenFile = true;
if(!_deviceActionService.CanOpenFile(attachment.FileName)) if(!_deviceActionService.CanOpenFile(attachment.FileName))
{ {
await _platformUtilsService.ShowDialogAsync(AppResources.UnableToOpenFile); if(Device.RuntimePlatform == Device.iOS)
return; {
// iOS is currently hardcoded to always return CanOpenFile == true, but should it ever return false
// for any reason we want to be sure to catch it here.
await _platformUtilsService.ShowDialogAsync(AppResources.UnableToOpenFile);
return;
}
canOpenFile = false;
} }
await _deviceActionService.ShowLoadingAsync(AppResources.Downloading); await _deviceActionService.ShowLoadingAsync(AppResources.Downloading);
@ -421,10 +432,23 @@ namespace Bit.App.Pages
await _platformUtilsService.ShowDialogAsync(AppResources.UnableToDownloadFile); await _platformUtilsService.ShowDialogAsync(AppResources.UnableToDownloadFile);
return; return;
} }
if(!_deviceActionService.OpenFile(data, attachment.Id, attachment.FileName))
if(Device.RuntimePlatform == Device.Android)
{ {
await _platformUtilsService.ShowDialogAsync(AppResources.UnableToOpenFile); if(canOpenFile)
return; {
// We can open this attachment directly, so give the user the option to open or save
PromptOpenOrSave(data, attachment);
}
else
{
// We can't open this attachment so go directly to save
SaveAttachment(data, attachment);
}
}
else
{
OpenAttachment(data, attachment);
} }
} }
catch catch
@ -433,6 +457,59 @@ namespace Bit.App.Pages
} }
} }
public async void PromptOpenOrSave(byte[] data, AttachmentView attachment)
{
var selection = await Page.DisplayActionSheet(attachment.FileName, AppResources.Cancel, null,
AppResources.Open, AppResources.Save);
if(selection == AppResources.Open)
{
OpenAttachment(data, attachment);
}
else if(selection == AppResources.Save)
{
SaveAttachment(data, attachment);
}
}
public async void OpenAttachment(byte[] data, AttachmentView attachment)
{
if(!_deviceActionService.OpenFile(data, attachment.Id, attachment.FileName))
{
await _platformUtilsService.ShowDialogAsync(AppResources.UnableToOpenFile);
return;
}
}
public async void SaveAttachment(byte[] data, AttachmentView attachment)
{
_attachmentData = data;
_attachmentFilename = attachment.FileName;
if(!_deviceActionService.SaveFile(_attachmentData, null, _attachmentFilename, null))
{
ClearAttachmentData();
await _platformUtilsService.ShowDialogAsync(AppResources.UnableToSaveAttachment);
}
}
public async void SaveFileSelected(string contentUri, string filename)
{
if(_deviceActionService.SaveFile(_attachmentData, null, filename ?? _attachmentFilename, contentUri))
{
ClearAttachmentData();
_platformUtilsService.ShowToast("success", null, AppResources.SaveAttachmentSuccess);
return;
}
ClearAttachmentData();
await _platformUtilsService.ShowDialogAsync(AppResources.UnableToSaveAttachment);
}
private void ClearAttachmentData()
{
_attachmentData = null;
_attachmentFilename = null;
}
private async void CopyAsync(string id, string text = null) private async void CopyAsync(string id, string text = null)
{ {
string name = null; string name = null;

View File

@ -2841,5 +2841,23 @@ namespace Bit.App.Resources {
return ResourceManager.GetString("PasswordGeneratorPolicyInEffect", resourceCulture); return ResourceManager.GetString("PasswordGeneratorPolicyInEffect", resourceCulture);
} }
} }
public static string Open {
get {
return ResourceManager.GetString("Open", resourceCulture);
}
}
public static string UnableToSaveAttachment {
get {
return ResourceManager.GetString("UnableToSaveAttachment", resourceCulture);
}
}
public static string SaveAttachmentSuccess {
get {
return ResourceManager.GetString("SaveAttachmentSuccess", resourceCulture);
}
}
} }
} }

View File

@ -1615,4 +1615,14 @@
<data name="PasswordGeneratorPolicyInEffect" xml:space="preserve"> <data name="PasswordGeneratorPolicyInEffect" xml:space="preserve">
<value>One or more organization policies are affecting your generator settings</value> <value>One or more organization policies are affecting your generator settings</value>
</data> </data>
<data name="Open" xml:space="preserve">
<value>Open</value>
<comment>Button text for an open operation (verb).</comment>
</data>
<data name="UnableToSaveAttachment" xml:space="preserve">
<value>There was a problem saving this attachment. If the problem persists, you can save it from the web vault.</value>
</data>
<data name="SaveAttachmentSuccess" xml:space="preserve">
<value>Attachment saved successfully</value>
</data>
</root> </root>