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);
if(mimeType == null)
{
if(extension == "json")
{
// Explicit support for json since older versions of Android don't recognize the extension
mimeType = "text/json";
}
else
{
return false;
}
// Unable to identify so fall back to generic "any" type
mimeType = "*/*";
}
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.Utilities;
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 () =>
{

View File

@ -34,6 +34,8 @@ namespace Bit.App.Pages
private bool _totpLow;
private DateTime? _totpInterval = null;
private string _previousCipherId;
private byte[] _attachmentData;
private string _attachmentFilename;
public ViewPageViewModel()
{
@ -405,10 +407,19 @@ namespace Bit.App.Pages
return;
}
}
var canOpenFile = true;
if(!_deviceActionService.CanOpenFile(attachment.FileName))
{
await _platformUtilsService.ShowDialogAsync(AppResources.UnableToOpenFile);
return;
if(Device.RuntimePlatform == Device.iOS)
{
// 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);
@ -421,10 +432,23 @@ namespace Bit.App.Pages
await _platformUtilsService.ShowDialogAsync(AppResources.UnableToDownloadFile);
return;
}
if(!_deviceActionService.OpenFile(data, attachment.Id, attachment.FileName))
if(Device.RuntimePlatform == Device.Android)
{
await _platformUtilsService.ShowDialogAsync(AppResources.UnableToOpenFile);
return;
if(canOpenFile)
{
// 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
@ -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)
{
string name = null;

View File

@ -2841,5 +2841,23 @@ namespace Bit.App.Resources {
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">
<value>One or more organization policies are affecting your generator settings</value>
</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>