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:
parent
70c49922b0
commit
2b1d186611
@ -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);
|
||||||
|
@ -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 () =>
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
|
18
src/App/Resources/AppResources.Designer.cs
generated
18
src/App/Resources/AppResources.Designer.cs
generated
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>
|
Loading…
x
Reference in New Issue
Block a user