mirror of
https://github.com/bitwarden/mobile.git
synced 2024-11-22 11:35:21 +01:00
Share-to-Send for Android (#1343)
* Android implementation * remove iOS attempt for now
This commit is contained in:
parent
ce0b8bc62d
commit
d926565358
@ -30,6 +30,16 @@ namespace Bit.Droid
|
||||
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation |
|
||||
ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden |
|
||||
ConfigChanges.Navigation)]
|
||||
[IntentFilter(
|
||||
new[] { Intent.ActionSend },
|
||||
Categories = new[] { Intent.CategoryDefault },
|
||||
DataMimeTypes = new[]
|
||||
{
|
||||
@"application/*",
|
||||
@"image/*",
|
||||
@"video/*",
|
||||
@"text/*"
|
||||
})]
|
||||
[Register("com.x8bit.bitwarden.MainActivity")]
|
||||
public class MainActivity : Xamarin.Forms.Platform.Android.FormsAppCompatActivity
|
||||
{
|
||||
@ -169,7 +179,7 @@ namespace Bit.Droid
|
||||
_appOptions.GeneratorTile = true;
|
||||
}
|
||||
}
|
||||
if (intent.GetBooleanExtra("myVaultTile", false))
|
||||
else if (intent.GetBooleanExtra("myVaultTile", false))
|
||||
{
|
||||
_messagingService.Send("popAllAndGoToTabMyVault");
|
||||
if (_appOptions != null)
|
||||
@ -177,6 +187,14 @@ namespace Bit.Droid
|
||||
_appOptions.MyVaultTile = true;
|
||||
}
|
||||
}
|
||||
else if (intent.Action == Intent.ActionSend && intent.Type != null)
|
||||
{
|
||||
if (_appOptions != null)
|
||||
{
|
||||
_appOptions.CreateSend = GetCreateSendRequest(intent);
|
||||
}
|
||||
_messagingService.Send("popAllAndGoToTabSend");
|
||||
}
|
||||
else
|
||||
{
|
||||
ParseYubiKey(intent.DataString);
|
||||
@ -298,7 +316,8 @@ namespace Bit.Droid
|
||||
Uri = Intent.GetStringExtra("uri") ?? Intent.GetStringExtra("autofillFrameworkUri"),
|
||||
MyVaultTile = Intent.GetBooleanExtra("myVaultTile", false),
|
||||
GeneratorTile = Intent.GetBooleanExtra("generatorTile", false),
|
||||
FromAutofillFramework = Intent.GetBooleanExtra("autofillFramework", false)
|
||||
FromAutofillFramework = Intent.GetBooleanExtra("autofillFramework", false),
|
||||
CreateSend = GetCreateSendRequest(Intent)
|
||||
};
|
||||
var fillType = Intent.GetIntExtra("autofillFrameworkFillType", 0);
|
||||
if (fillType > 0)
|
||||
@ -320,6 +339,37 @@ namespace Bit.Droid
|
||||
return options;
|
||||
}
|
||||
|
||||
private Tuple<SendType, string, byte[], string> GetCreateSendRequest(Intent intent)
|
||||
{
|
||||
if (intent.Action == Intent.ActionSend && intent.Type != null)
|
||||
{
|
||||
var type = intent.Type;
|
||||
if (type.Contains("text/"))
|
||||
{
|
||||
var subject = intent.GetStringExtra(Intent.ExtraSubject);
|
||||
var text = intent.GetStringExtra(Intent.ExtraText);
|
||||
return new Tuple<SendType, string, byte[], string>(SendType.Text, subject, null, text);
|
||||
}
|
||||
else
|
||||
{
|
||||
var data = intent.ClipData?.GetItemAt(0);
|
||||
var uri = data?.Uri;
|
||||
var filename = AndroidHelpers.GetFileName(ApplicationContext, uri);
|
||||
try
|
||||
{
|
||||
using (var stream = ContentResolver.OpenInputStream(uri))
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
stream.CopyTo(memoryStream);
|
||||
return new Tuple<SendType, string, byte[], string>(SendType.File, filename, memoryStream.ToArray(), null);
|
||||
}
|
||||
}
|
||||
catch (Java.IO.FileNotFoundException) { }
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void ParseYubiKey(string data)
|
||||
{
|
||||
if (data == null)
|
||||
|
@ -760,6 +760,17 @@ namespace Bit.Droid.Services
|
||||
// ref: https://developer.android.com/reference/android/os/SystemClock#elapsedRealtime()
|
||||
return SystemClock.ElapsedRealtime();
|
||||
}
|
||||
|
||||
public void CloseMainApp()
|
||||
{
|
||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||
if (activity == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
activity.Finish();
|
||||
_messagingService.Send("finishMainActivity");
|
||||
}
|
||||
|
||||
private bool DeleteDir(Java.IO.File dir)
|
||||
{
|
||||
|
@ -44,5 +44,6 @@ namespace Bit.App.Abstractions
|
||||
void OpenAutofillSettings();
|
||||
bool UsingDarkTheme();
|
||||
long GetActiveTime();
|
||||
void CloseMainApp();
|
||||
}
|
||||
}
|
||||
|
@ -131,7 +131,8 @@ namespace Bit.App
|
||||
await SetMainPageAsync();
|
||||
}
|
||||
else if (message.Command == "popAllAndGoToTabGenerator" ||
|
||||
message.Command == "popAllAndGoToTabMyVault")
|
||||
message.Command == "popAllAndGoToTabMyVault" ||
|
||||
message.Command == "popAllAndGoToTabSend")
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
@ -146,11 +147,15 @@ namespace Bit.App
|
||||
Options.MyVaultTile = false;
|
||||
tabsPage.ResetToVaultPage();
|
||||
}
|
||||
else
|
||||
else if (message.Command == "popAllAndGoToTabGenerator")
|
||||
{
|
||||
Options.GeneratorTile = false;
|
||||
tabsPage.ResetToGeneratorPage();
|
||||
}
|
||||
else if (message.Command == "popAllAndGoToTabSend")
|
||||
{
|
||||
tabsPage.ResetToSendPage();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -274,6 +279,10 @@ namespace Bit.App
|
||||
{
|
||||
Current.MainPage = new NavigationPage(new AutofillCiphersPage(Options));
|
||||
}
|
||||
else if (Options.CreateSend != null)
|
||||
{
|
||||
Current.MainPage = new NavigationPage(new SendAddEditPage(Options));
|
||||
}
|
||||
else
|
||||
{
|
||||
Current.MainPage = new TabsPage(Options);
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Bit.Core.Enums;
|
||||
using System;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.App.Models
|
||||
{
|
||||
@ -19,6 +20,7 @@ namespace Bit.App.Models
|
||||
public string SaveCardExpYear { get; set; }
|
||||
public string SaveCardCode { get; set; }
|
||||
public bool IosExtension { get; set; }
|
||||
public Tuple<SendType, string, byte[], string> CreateSend { get; set; }
|
||||
|
||||
public void SetAllFrom(AppOptions o)
|
||||
{
|
||||
@ -41,6 +43,7 @@ namespace Bit.App.Models
|
||||
SaveCardExpYear = o.SaveCardExpYear;
|
||||
SaveCardCode = o.SaveCardCode;
|
||||
IosExtension = o.IosExtension;
|
||||
CreateSend = o.CreateSend;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -117,7 +117,7 @@
|
||||
</StackLayout>
|
||||
<StackLayout
|
||||
StyleClass="box-row"
|
||||
IsVisible="{Binding EditMode, Converter={StaticResource inverseBool}}">
|
||||
IsVisible="{Binding ShowTypeButtons}">
|
||||
<Label
|
||||
Text="{u:I18n Type}"
|
||||
StyleClass="box-label" />
|
||||
@ -222,6 +222,7 @@
|
||||
HorizontalTextAlignment="Center" />
|
||||
<Button
|
||||
Text="{u:I18n ChooseFile}"
|
||||
IsVisible="{Binding IsAddFromShare, Converter={StaticResource inverseBool}}"
|
||||
IsEnabled="{Binding SendEnabled}"
|
||||
StyleClass="box-button-row"
|
||||
Clicked="ChooseFile_Clicked" />
|
||||
@ -234,7 +235,7 @@
|
||||
</StackLayout>
|
||||
<Label
|
||||
Text="{u:I18n TypeFileInfo}"
|
||||
IsVisible="{Binding EditMode, Converter={StaticResource inverseBool}}"
|
||||
IsVisible="{Binding ShowTypeButtons}"
|
||||
StyleClass="box-footer-label"
|
||||
Margin="0,5,0,0" />
|
||||
</StackLayout>
|
||||
|
@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
@ -15,18 +17,21 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
|
||||
private AppOptions _appOptions;
|
||||
private SendAddEditPageViewModel _vm;
|
||||
|
||||
public SendAddEditPage(
|
||||
AppOptions appOptions = null,
|
||||
string sendId = null,
|
||||
SendType? type = null)
|
||||
{
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_appOptions = appOptions;
|
||||
InitializeComponent();
|
||||
_vm = BindingContext as SendAddEditPageViewModel;
|
||||
_vm.Page = this;
|
||||
_vm.SendId = sendId;
|
||||
_vm.Type = type;
|
||||
_vm.Type = appOptions?.CreateSend?.Item1 ?? type;
|
||||
SetActivityIndicator();
|
||||
if (Device.RuntimePlatform == Device.Android)
|
||||
{
|
||||
@ -95,6 +100,7 @@ namespace Bit.App.Pages
|
||||
await Navigation.PopModalAsync();
|
||||
return;
|
||||
}
|
||||
await HandleCreateRequest();
|
||||
if (!_vm.EditMode && string.IsNullOrWhiteSpace(_vm.Send?.Name))
|
||||
{
|
||||
RequestFocus(_nameEntry);
|
||||
@ -271,5 +277,33 @@ namespace Bit.App.Pages
|
||||
ToolbarItems.Remove(_shareLink);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleCreateRequest()
|
||||
{
|
||||
if (_appOptions?.CreateSend == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_vm.IsAddFromShare = true;
|
||||
|
||||
var name = _appOptions.CreateSend.Item2;
|
||||
_vm.Send.Name = name;
|
||||
|
||||
var type = _appOptions.CreateSend.Item1;
|
||||
if (type == SendType.File)
|
||||
{
|
||||
_vm.FileData = _appOptions.CreateSend.Item3;
|
||||
_vm.FileName = name;
|
||||
FileType_Clicked(null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var text = _appOptions.CreateSend.Item4;
|
||||
_vm.Send.Text.Text = text;
|
||||
TextType_Clicked(null, null);
|
||||
}
|
||||
_appOptions.CreateSend = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -95,6 +95,7 @@ namespace Bit.App.Pages
|
||||
public string NewPassword { get; set; }
|
||||
public bool ShareOnSave { get; set; }
|
||||
public bool DisableHideEmailControl { get; set; }
|
||||
public bool IsAddFromShare { get; set; }
|
||||
public List<KeyValuePair<string, SendType>> TypeOptions { get; }
|
||||
public List<KeyValuePair<string, string>> DeletionTypeOptions { get; }
|
||||
public List<KeyValuePair<string, string>> ExpirationTypeOptions { get; }
|
||||
@ -208,6 +209,7 @@ namespace Bit.App.Pages
|
||||
get => _sendOptionsPolicyInEffect;
|
||||
set => SetProperty(ref _sendOptionsPolicyInEffect, value);
|
||||
}
|
||||
public bool ShowTypeButtons => !EditMode && !IsAddFromShare;
|
||||
public bool EditMode => !string.IsNullOrWhiteSpace(SendId);
|
||||
public bool IsText => Send?.Type == SendType.Text;
|
||||
public bool IsFile => Send?.Type == SendType.File;
|
||||
@ -335,7 +337,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (!_canAccessPremium)
|
||||
{
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.PremiumRequired);
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.SendFilePremiumRequired);
|
||||
return false;
|
||||
}
|
||||
if (!EditMode)
|
||||
@ -374,10 +376,6 @@ namespace Bit.App.Pages
|
||||
var sendId = await _sendService.SaveWithServerAsync(send, encryptedFileData);
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
|
||||
_platformUtilsService.ShowToast("success", null,
|
||||
EditMode ? AppResources.SendUpdated : AppResources.NewSendCreated);
|
||||
await Page.Navigation.PopModalAsync();
|
||||
|
||||
if (Device.RuntimePlatform == Device.Android && IsFile)
|
||||
{
|
||||
// Workaround for https://github.com/xamarin/Xamarin.Forms/issues/5418
|
||||
@ -395,7 +393,21 @@ namespace Bit.App.Pages
|
||||
await AppHelpers.ShareSendUrlAsync(savedSendView);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_platformUtilsService.ShowToast("success", null,
|
||||
EditMode ? AppResources.SendUpdated : AppResources.NewSendCreated);
|
||||
}
|
||||
|
||||
if (IsAddFromShare && Device.RuntimePlatform == Device.Android)
|
||||
{
|
||||
_deviceActionService.CloseMainApp();
|
||||
}
|
||||
else
|
||||
{
|
||||
await Page.Navigation.PopModalAsync();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (ApiException e)
|
||||
@ -432,11 +444,29 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task TypeChangedAsync(SendType type)
|
||||
{
|
||||
if (!SendEnabled)
|
||||
{
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.SendDisabledWarning);
|
||||
if (IsAddFromShare && Device.RuntimePlatform == Device.Android)
|
||||
{
|
||||
_deviceActionService.CloseMainApp();
|
||||
}
|
||||
else
|
||||
{
|
||||
await Page.Navigation.PopModalAsync();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (Send != null)
|
||||
{
|
||||
if (!EditMode && type == SendType.File && !_canAccessPremium)
|
||||
{
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.PremiumRequired);
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.SendFilePremiumRequired);
|
||||
if (IsAddFromShare && Device.RuntimePlatform == Device.Android)
|
||||
{
|
||||
_deviceActionService.CloseMainApp();
|
||||
return;
|
||||
}
|
||||
type = SendType.Text;
|
||||
}
|
||||
Send.Type = type;
|
||||
|
@ -19,10 +19,11 @@ namespace Bit.App.Pages
|
||||
private readonly SendGroupingsPageViewModel _vm;
|
||||
private readonly string _pageName;
|
||||
|
||||
private AppOptions _appOptions;
|
||||
private PreviousPageInfo _previousPage;
|
||||
|
||||
public SendGroupingsPage(bool mainPage, SendType? type = null, string pageTitle = null,
|
||||
PreviousPageInfo previousPage = null)
|
||||
AppOptions appOptions = null, PreviousPageInfo previousPage = null)
|
||||
{
|
||||
_pageName = string.Concat(nameof(GroupingsPage), "_", DateTime.UtcNow.Ticks);
|
||||
InitializeComponent();
|
||||
@ -35,6 +36,7 @@ namespace Bit.App.Pages
|
||||
_vm.Page = this;
|
||||
_vm.MainPage = mainPage;
|
||||
_vm.Type = type;
|
||||
_appOptions = appOptions;
|
||||
_previousPage = previousPage;
|
||||
if (pageTitle != null)
|
||||
{
|
||||
@ -109,8 +111,8 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
await ShowPreviousPageAsync();
|
||||
AdjustToolbar();
|
||||
await CheckAddRequest();
|
||||
}, _mainContent);
|
||||
}
|
||||
|
||||
@ -122,6 +124,18 @@ namespace Bit.App.Pages
|
||||
_vm.DisableRefreshing();
|
||||
}
|
||||
|
||||
private async Task CheckAddRequest()
|
||||
{
|
||||
if (_appOptions?.CreateSend != null)
|
||||
{
|
||||
if (DoOnce())
|
||||
{
|
||||
var page = new SendAddEditPage(_appOptions);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void RowSelected(object sender, SelectedItemChangedEventArgs e)
|
||||
{
|
||||
((ListView)sender).SelectedItem = null;
|
||||
@ -172,28 +186,11 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (DoOnce())
|
||||
{
|
||||
var page = new SendAddEditPage(null, _vm.Type);
|
||||
var page = new SendAddEditPage(null, null, _vm.Type);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ShowPreviousPageAsync()
|
||||
{
|
||||
if (_previousPage == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_previousPage.Page == "view" && !string.IsNullOrWhiteSpace(_previousPage.SendId))
|
||||
{
|
||||
await Navigation.PushModalAsync(new NavigationPage(new ViewPage(_previousPage.SendId)));
|
||||
}
|
||||
else if (_previousPage.Page == "edit" && !string.IsNullOrWhiteSpace(_previousPage.SendId))
|
||||
{
|
||||
await Navigation.PushModalAsync(new NavigationPage(new AddEditPage(_previousPage.SendId)));
|
||||
}
|
||||
_previousPage = null;
|
||||
}
|
||||
|
||||
private void AdjustToolbar()
|
||||
{
|
||||
_addItem.IsEnabled = _vm.SendEnabled;
|
||||
|
@ -208,7 +208,7 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task SelectSendAsync(SendView send)
|
||||
{
|
||||
var page = new SendAddEditPage(send.Id);
|
||||
var page = new SendAddEditPage(null, send.Id);
|
||||
await Page.Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,7 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task SelectSendAsync(SendView send)
|
||||
{
|
||||
var page = new SendAddEditPage(send.Id);
|
||||
var page = new SendAddEditPage(null, send.Id);
|
||||
await Page.Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ namespace Bit.App.Pages
|
||||
};
|
||||
Children.Add(_groupingsPage);
|
||||
|
||||
_sendGroupingsPage = new NavigationPage(new SendGroupingsPage(true))
|
||||
_sendGroupingsPage = new NavigationPage(new SendGroupingsPage(true, null, null, appOptions))
|
||||
{
|
||||
Title = AppResources.Send,
|
||||
IconImageSource = "paper_plane.png",
|
||||
@ -60,6 +60,10 @@ namespace Bit.App.Pages
|
||||
{
|
||||
appOptions.MyVaultTile = false;
|
||||
}
|
||||
else if (appOptions?.CreateSend != null)
|
||||
{
|
||||
ResetToSendPage();
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetToVaultPage()
|
||||
@ -71,6 +75,11 @@ namespace Bit.App.Pages
|
||||
{
|
||||
CurrentPage = _generatorPage;
|
||||
}
|
||||
|
||||
public void ResetToSendPage()
|
||||
{
|
||||
CurrentPage = _sendGroupingsPage;
|
||||
}
|
||||
|
||||
protected async override void OnCurrentPageChanged()
|
||||
{
|
||||
|
6
src/App/Resources/AppResources.Designer.cs
generated
6
src/App/Resources/AppResources.Designer.cs
generated
@ -3496,5 +3496,11 @@ namespace Bit.App.Resources {
|
||||
return ResourceManager.GetString("SendOptionsPolicyInEffect", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
public static string SendFilePremiumRequired {
|
||||
get {
|
||||
return ResourceManager.GetString("SendFilePremiumRequired", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1979,4 +1979,8 @@
|
||||
<value>One or more organization policies are affecting your Send options.</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
<data name="SendFilePremiumRequired" xml:space="preserve">
|
||||
<value>Free accounts are restricted to sharing text only. A premium membership is required to use files with Send.</value>
|
||||
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
@ -154,7 +154,7 @@ namespace Bit.App.Utilities
|
||||
}
|
||||
else if (selection == AppResources.Edit)
|
||||
{
|
||||
await page.Navigation.PushModalAsync(new NavigationPage(new SendAddEditPage(send.Id)));
|
||||
await page.Navigation.PushModalAsync(new NavigationPage(new SendAddEditPage(null, send.Id)));
|
||||
}
|
||||
else if (selection == AppResources.CopyLink)
|
||||
{
|
||||
@ -409,6 +409,11 @@ namespace Bit.App.Utilities
|
||||
Application.Current.MainPage = new NavigationPage(new AutofillCiphersPage(appOptions));
|
||||
return true;
|
||||
}
|
||||
if (appOptions.CreateSend != null)
|
||||
{
|
||||
Application.Current.MainPage = new NavigationPage(new SendAddEditPage(appOptions));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -437,6 +437,11 @@ namespace Bit.iOS.Core.Services
|
||||
return iOSHelpers.GetSystemUpTimeMilliseconds() ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
}
|
||||
|
||||
public void CloseMainApp()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void ImagePicker_FinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs e)
|
||||
{
|
||||
if (sender is UIImagePickerController picker)
|
||||
|
Loading…
Reference in New Issue
Block a user