mirror of
https://github.com/bitwarden/android.git
synced 2024-12-25 02:18:27 +03:00
multibutton alert, autofill and save new uri
This commit is contained in:
parent
be11933c60
commit
10df9e7cd5
12 changed files with 313 additions and 40 deletions
33
src/Android/Resources/Resource.Designer.cs
generated
33
src/Android/Resources/Resource.Designer.cs
generated
|
@ -6506,17 +6506,17 @@ namespace Bit.Android
|
|||
// aapt resource value: 0x7f0a0051
|
||||
public const int ApplicationName = 2131361873;
|
||||
|
||||
// aapt resource value: 0x7f0a00ab
|
||||
public const int AutoFillServiceDescription = 2131361963;
|
||||
// aapt resource value: 0x7f0a00b2
|
||||
public const int AutoFillServiceDescription = 2131361970;
|
||||
|
||||
// aapt resource value: 0x7f0a00aa
|
||||
public const int AutoFillServiceSummary = 2131361962;
|
||||
// aapt resource value: 0x7f0a00b1
|
||||
public const int AutoFillServiceSummary = 2131361969;
|
||||
|
||||
// aapt resource value: 0x7f0a0050
|
||||
public const int Hello = 2131361872;
|
||||
|
||||
// aapt resource value: 0x7f0a00ac
|
||||
public const int MyVault = 2131361964;
|
||||
// aapt resource value: 0x7f0a00b3
|
||||
public const int MyVault = 2131361971;
|
||||
|
||||
// aapt resource value: 0x7f0a0027
|
||||
public const int abc_action_bar_home_description = 2131361831;
|
||||
|
@ -6671,6 +6671,27 @@ namespace Bit.Android
|
|||
// aapt resource value: 0x7f0a000f
|
||||
public const int common_signin_button_text_long = 2131361807;
|
||||
|
||||
// aapt resource value: 0x7f0a00ac
|
||||
public const int default_web_client_id = 2131361964;
|
||||
|
||||
// aapt resource value: 0x7f0a00ad
|
||||
public const int firebase_database_url = 2131361965;
|
||||
|
||||
// aapt resource value: 0x7f0a00aa
|
||||
public const int gcm_defaultSenderId = 2131361962;
|
||||
|
||||
// aapt resource value: 0x7f0a00ae
|
||||
public const int google_api_key = 2131361966;
|
||||
|
||||
// aapt resource value: 0x7f0a00ab
|
||||
public const int google_app_id = 2131361963;
|
||||
|
||||
// aapt resource value: 0x7f0a00af
|
||||
public const int google_crash_reporting_api_key = 2131361967;
|
||||
|
||||
// aapt resource value: 0x7f0a00b0
|
||||
public const int google_storage_bucket = 2131361968;
|
||||
|
||||
// aapt resource value: 0x7f0a0052
|
||||
public const int hockeyapp_crash_dialog_app_name_fallback = 2131361874;
|
||||
|
||||
|
|
|
@ -514,5 +514,78 @@ namespace Bit.Android.Services
|
|||
alert.Show();
|
||||
return result.Task;
|
||||
}
|
||||
|
||||
public Task<string> DisplayAlertAsync(string title, string message, string cancel, params string[] buttons)
|
||||
{
|
||||
var activity = (MainActivity)CurrentContext;
|
||||
if(activity == null)
|
||||
{
|
||||
return Task.FromResult<string>(null);
|
||||
}
|
||||
|
||||
var result = new TaskCompletionSource<string>();
|
||||
var alertBuilder = new AlertDialog.Builder(activity);
|
||||
alertBuilder.SetTitle(title);
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(message))
|
||||
{
|
||||
if(buttons != null && buttons.Length > 2)
|
||||
{
|
||||
if(!string.IsNullOrWhiteSpace(title))
|
||||
{
|
||||
alertBuilder.SetTitle($"{title}: {message}");
|
||||
}
|
||||
else
|
||||
{
|
||||
alertBuilder.SetTitle(message);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
alertBuilder.SetMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
if(buttons != null)
|
||||
{
|
||||
if(buttons.Length > 2)
|
||||
{
|
||||
alertBuilder.SetItems(buttons, (sender, args) =>
|
||||
{
|
||||
result.TrySetResult(buttons[args.Which]);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
if(buttons.Length > 0)
|
||||
{
|
||||
alertBuilder.SetPositiveButton(buttons[0], (sender, args) =>
|
||||
{
|
||||
result.TrySetResult(buttons[0]);
|
||||
});
|
||||
}
|
||||
if(buttons.Length > 1)
|
||||
{
|
||||
alertBuilder.SetNeutralButton(buttons[1], (sender, args) =>
|
||||
{
|
||||
result.TrySetResult(buttons[1]);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(cancel))
|
||||
{
|
||||
alertBuilder.SetNegativeButton(cancel, (sender, args) =>
|
||||
{
|
||||
result.TrySetResult(cancel);
|
||||
});
|
||||
}
|
||||
|
||||
var alert = alertBuilder.Create();
|
||||
alert.CancelEvent += (o, args) => { result.TrySetResult(null); };
|
||||
alert.Show();
|
||||
return result.Task;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,5 +23,6 @@ namespace Bit.App.Abstractions
|
|||
void OpenAutofillSettings();
|
||||
Task LaunchAppAsync(string appName, Page page);
|
||||
Task<string> DisplayPromptAync(string title = null, string description = null, string text = null);
|
||||
Task<string> DisplayAlertAsync(string title, string message, string cancel, params string[] buttons);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ using Bit.App.Models;
|
|||
using System.Collections.Generic;
|
||||
using Bit.App.Enums;
|
||||
using static Bit.App.Models.Page.VaultListPageModel;
|
||||
using Plugin.Connectivity.Abstractions;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
|
@ -22,6 +23,7 @@ namespace Bit.App.Pages
|
|||
private readonly IDeviceInfoService _deviceInfoService;
|
||||
private readonly ISettingsService _settingsService;
|
||||
private readonly IAppSettingsService _appSettingsService;
|
||||
public readonly IConnectivity _connectivity;
|
||||
private CancellationTokenSource _filterResultsCancellationTokenSource;
|
||||
private readonly string _name;
|
||||
private readonly AppOptions _appOptions;
|
||||
|
@ -47,6 +49,7 @@ namespace Bit.App.Pages
|
|||
_settingsService = Resolver.Resolve<ISettingsService>();
|
||||
_appSettingsService = Resolver.Resolve<IAppSettingsService>();
|
||||
GoogleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>();
|
||||
_connectivity = Resolver.Resolve<IConnectivity>();
|
||||
|
||||
Init();
|
||||
}
|
||||
|
@ -237,17 +240,57 @@ namespace Bit.App.Pages
|
|||
}
|
||||
else
|
||||
{
|
||||
bool doAutofill = true;
|
||||
var autofillResponse = AppResources.Yes;
|
||||
if(cipher.Fuzzy)
|
||||
{
|
||||
doAutofill = await DisplayAlert(null,
|
||||
string.Format(AppResources.BitwardenAutofillServiceMatchConfirm, _name),
|
||||
AppResources.Yes, AppResources.No);
|
||||
var options = new List<string> { AppResources.Yes };
|
||||
if(cipher.Type == CipherType.Login && _connectivity.IsConnected)
|
||||
{
|
||||
options.Add(AppResources.YesAndSave);
|
||||
}
|
||||
|
||||
if(doAutofill)
|
||||
autofillResponse = await DeviceActionService.DisplayAlertAsync(null,
|
||||
string.Format(AppResources.BitwardenAutofillServiceMatchConfirm, _name), AppResources.No,
|
||||
options.ToArray());
|
||||
}
|
||||
|
||||
if(autofillResponse == AppResources.YesAndSave && cipher.Type == CipherType.Login)
|
||||
{
|
||||
GoogleAnalyticsService.TrackExtensionEvent("AutoFilled", Uri.StartsWith("http") ? "Website" : "App");
|
||||
if(!_connectivity.IsConnected)
|
||||
{
|
||||
Helpers.AlertNoConnection(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
var uris = cipher.CipherModel.Login?.Uris?.ToList();
|
||||
if(uris == null)
|
||||
{
|
||||
uris = new List<LoginUri>();
|
||||
}
|
||||
|
||||
uris.Add(new LoginUri
|
||||
{
|
||||
Uri = Uri.Encrypt(cipher.CipherModel.OrganizationId),
|
||||
Match = null
|
||||
});
|
||||
|
||||
cipher.CipherModel.Login.Uris = uris;
|
||||
|
||||
await DeviceActionService.ShowLoadingAsync(AppResources.Saving);
|
||||
var saveTask = await _cipherService.SaveAsync(cipher.CipherModel);
|
||||
await DeviceActionService.HideLoadingAsync();
|
||||
|
||||
if(saveTask.Succeeded)
|
||||
{
|
||||
GoogleAnalyticsService.TrackAppEvent("AddedLoginUriDuringAutofill");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(autofillResponse == AppResources.Yes || autofillResponse == AppResources.YesAndSave)
|
||||
{
|
||||
GoogleAnalyticsService.TrackExtensionEvent("AutoFilled",
|
||||
Uri.StartsWith("http") ? "Website" : "App");
|
||||
DeviceActionService.Autofill(cipher);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -403,8 +403,14 @@ namespace Bit.App.Pages
|
|||
string selection = null;
|
||||
if(!string.IsNullOrWhiteSpace(_uri))
|
||||
{
|
||||
var options = new List<string> { AppResources.Autofill };
|
||||
if(cipher.Type == Enums.CipherType.Login && _connectivity.IsConnected)
|
||||
{
|
||||
options.Add(AppResources.AutofillAndSave);
|
||||
}
|
||||
options.Add(AppResources.View);
|
||||
selection = await DisplayActionSheet(AppResources.AutofillOrView, AppResources.Cancel, null,
|
||||
AppResources.Autofill, AppResources.View);
|
||||
options.ToArray());
|
||||
}
|
||||
|
||||
if(selection == AppResources.View || string.IsNullOrWhiteSpace(_uri))
|
||||
|
@ -412,8 +418,40 @@ namespace Bit.App.Pages
|
|||
var page = new VaultViewCipherPage(cipher.Type, cipher.Id);
|
||||
await Navigation.PushForDeviceAsync(page);
|
||||
}
|
||||
else if(selection == AppResources.Autofill)
|
||||
else if(selection == AppResources.Autofill || selection == AppResources.AutofillAndSave)
|
||||
{
|
||||
if(selection == AppResources.AutofillAndSave)
|
||||
{
|
||||
if(!_connectivity.IsConnected)
|
||||
{
|
||||
Helpers.AlertNoConnection(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
var uris = cipher.CipherModel.Login?.Uris?.ToList();
|
||||
if(uris == null)
|
||||
{
|
||||
uris = new List<Models.LoginUri>();
|
||||
}
|
||||
|
||||
uris.Add(new Models.LoginUri
|
||||
{
|
||||
Uri = _uri.Encrypt(cipher.CipherModel.OrganizationId),
|
||||
Match = null
|
||||
});
|
||||
|
||||
cipher.CipherModel.Login.Uris = uris;
|
||||
|
||||
await _deviceActionService.ShowLoadingAsync(AppResources.Saving);
|
||||
var saveTask = await _cipherService.SaveAsync(cipher.CipherModel);
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
if(saveTask.Succeeded)
|
||||
{
|
||||
_googleAnalyticsService.TrackAppEvent("AddedLoginUriDuringAutofill");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(_deviceInfoService.Version < 21)
|
||||
{
|
||||
Helpers.CipherMoreClickedAsync(this, cipher, !string.IsNullOrWhiteSpace(_uri));
|
||||
|
|
18
src/App/Resources/AppResources.Designer.cs
generated
18
src/App/Resources/AppResources.Designer.cs
generated
|
@ -330,6 +330,15 @@ namespace Bit.App.Resources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Auto-fill and save.
|
||||
/// </summary>
|
||||
public static string AutofillAndSave {
|
||||
get {
|
||||
return ResourceManager.GetString("AutofillAndSave", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Do you want to auto-fill or view this item?.
|
||||
/// </summary>
|
||||
|
@ -3291,6 +3300,15 @@ namespace Bit.App.Resources {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Yes, and Save.
|
||||
/// </summary>
|
||||
public static string YesAndSave {
|
||||
get {
|
||||
return ResourceManager.GetString("YesAndSave", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to To continue, hold your YubiKey NEO against the back of the device or insert your YubiKey into your device's USB port, then touch its button..
|
||||
/// </summary>
|
||||
|
|
|
@ -1283,4 +1283,10 @@
|
|||
<value>Match Detection</value>
|
||||
<comment>URI match detection for auto-fill.</comment>
|
||||
</data>
|
||||
<data name="YesAndSave" xml:space="preserve">
|
||||
<value>Yes, and Save</value>
|
||||
</data>
|
||||
<data name="AutofillAndSave" xml:space="preserve">
|
||||
<value>Auto-fill and save</value>
|
||||
</data>
|
||||
</root>
|
|
@ -93,7 +93,8 @@ namespace Bit.App.Services
|
|||
return ciphers.Where(c => cipherIds.Contains(c.Id));
|
||||
}
|
||||
|
||||
public async Task<Tuple<IEnumerable<Cipher>, IEnumerable<Cipher>, IEnumerable<Cipher>>> GetAllAsync(string uriString)
|
||||
public async Task<Tuple<IEnumerable<Cipher>, IEnumerable<Cipher>, IEnumerable<Cipher>>> GetAllAsync(
|
||||
string uriString)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(uriString))
|
||||
{
|
||||
|
@ -173,42 +174,42 @@ namespace Bit.App.Services
|
|||
break;
|
||||
}
|
||||
|
||||
var added = false;
|
||||
var match = false;
|
||||
switch(u.Match)
|
||||
{
|
||||
case null:
|
||||
case Enums.UriMatchType.Domain:
|
||||
added = CheckDefaultUriMatch(cipher, loginUriString, matchingLogins, matchingFuzzyLogins,
|
||||
match = CheckDefaultUriMatch(cipher, loginUriString, matchingLogins, matchingFuzzyLogins,
|
||||
matchingDomainsArray, matchingFuzzyDomainsArray, mobileApp, mobileAppSearchTerms);
|
||||
break;
|
||||
case Enums.UriMatchType.Host:
|
||||
var urlHost = Helpers.GetUrlHost(uriString);
|
||||
added = urlHost != null && urlHost == Helpers.GetUrlHost(loginUriString);
|
||||
if(added)
|
||||
match = urlHost != null && urlHost == Helpers.GetUrlHost(loginUriString);
|
||||
if(match)
|
||||
{
|
||||
matchingLogins.Add(cipher);
|
||||
AddMatchingLogin(cipher, matchingLogins, matchingFuzzyLogins);
|
||||
}
|
||||
break;
|
||||
case Enums.UriMatchType.Exact:
|
||||
added = uriString == loginUriString;
|
||||
if(added)
|
||||
match = uriString == loginUriString;
|
||||
if(match)
|
||||
{
|
||||
matchingLogins.Add(cipher);
|
||||
AddMatchingLogin(cipher, matchingLogins, matchingFuzzyLogins);
|
||||
}
|
||||
break;
|
||||
case Enums.UriMatchType.StartsWith:
|
||||
added = uriString.StartsWith(loginUriString);
|
||||
if(added)
|
||||
match = uriString.StartsWith(loginUriString);
|
||||
if(match)
|
||||
{
|
||||
matchingLogins.Add(cipher);
|
||||
AddMatchingLogin(cipher, matchingLogins, matchingFuzzyLogins);
|
||||
}
|
||||
break;
|
||||
case Enums.UriMatchType.RegularExpression:
|
||||
var regex = new Regex(loginUriString, RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1));
|
||||
added = regex.IsMatch(uriString);
|
||||
if(added)
|
||||
match = regex.IsMatch(uriString);
|
||||
if(match)
|
||||
{
|
||||
matchingLogins.Add(cipher);
|
||||
AddMatchingLogin(cipher, matchingLogins, matchingFuzzyLogins);
|
||||
}
|
||||
break;
|
||||
case Enums.UriMatchType.Never:
|
||||
|
@ -216,7 +217,7 @@ namespace Bit.App.Services
|
|||
break;
|
||||
}
|
||||
|
||||
if(added)
|
||||
if(match)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
@ -436,21 +437,21 @@ namespace Bit.App.Services
|
|||
{
|
||||
if(Array.IndexOf(matchingDomainsArray, loginUriString) >= 0)
|
||||
{
|
||||
matchingLogins.Add(cipher);
|
||||
AddMatchingLogin(cipher, matchingLogins, matchingFuzzyLogins);
|
||||
return true;
|
||||
}
|
||||
else if(mobileApp && Array.IndexOf(matchingFuzzyDomainsArray, loginUriString) >= 0)
|
||||
{
|
||||
matchingFuzzyLogins.Add(cipher);
|
||||
return true;
|
||||
AddMatchingFuzzyLogin(cipher, matchingLogins, matchingFuzzyLogins);
|
||||
return false;
|
||||
}
|
||||
else if(!mobileApp)
|
||||
{
|
||||
var info = InfoFromMobileAppUri(loginUriString);
|
||||
if(info?.Item1 != null && Array.IndexOf(matchingDomainsArray, info.Item1) >= 0)
|
||||
{
|
||||
matchingFuzzyLogins.Add(cipher);
|
||||
return true;
|
||||
AddMatchingFuzzyLogin(cipher, matchingLogins, matchingFuzzyLogins);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -462,13 +463,13 @@ namespace Bit.App.Services
|
|||
|
||||
if(Array.IndexOf(matchingDomainsArray, loginDomainName) >= 0)
|
||||
{
|
||||
matchingLogins.Add(cipher);
|
||||
AddMatchingLogin(cipher, matchingLogins, matchingFuzzyLogins);
|
||||
return true;
|
||||
}
|
||||
else if(mobileApp && Array.IndexOf(matchingFuzzyDomainsArray, loginDomainName) >= 0)
|
||||
{
|
||||
matchingFuzzyLogins.Add(cipher);
|
||||
return true;
|
||||
AddMatchingFuzzyLogin(cipher, matchingLogins, matchingFuzzyLogins);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -491,13 +492,31 @@ namespace Bit.App.Services
|
|||
|
||||
if(addedFromSearchTerm)
|
||||
{
|
||||
matchingFuzzyLogins.Add(cipher);
|
||||
return true;
|
||||
AddMatchingFuzzyLogin(cipher, matchingLogins, matchingFuzzyLogins);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void AddMatchingLogin(Cipher cipher, List<Cipher> matchingLogins, List<Cipher> matchingFuzzyLogins)
|
||||
{
|
||||
if(matchingFuzzyLogins.Contains(cipher))
|
||||
{
|
||||
matchingFuzzyLogins.Remove(cipher);
|
||||
}
|
||||
|
||||
matchingLogins.Add(cipher);
|
||||
}
|
||||
|
||||
private void AddMatchingFuzzyLogin(Cipher cipher, List<Cipher> matchingLogins, List<Cipher> matchingFuzzyLogins)
|
||||
{
|
||||
if(!matchingFuzzyLogins.Contains(cipher) && !matchingLogins.Contains(cipher))
|
||||
{
|
||||
matchingFuzzyLogins.Add(cipher);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -521,5 +521,11 @@ namespace Bit.App.Utilities
|
|||
|
||||
return host;
|
||||
}
|
||||
|
||||
public static void AlertNoConnection(Page page)
|
||||
{
|
||||
page.DisplayAlert(AppResources.InternetConnectionRequiredTitle,
|
||||
AppResources.InternetConnectionRequiredMessage, AppResources.Ok);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -182,5 +182,22 @@ namespace Bit.UWP.Services
|
|||
|
||||
return result.Ok ? result.Value ?? string.Empty : null;
|
||||
}
|
||||
|
||||
public async Task<string> DisplayAlertAsync(string title, string message, string cancel, params string[] buttons)
|
||||
{
|
||||
if(!string.IsNullOrWhiteSpace(message))
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(title))
|
||||
{
|
||||
title = message;
|
||||
}
|
||||
else
|
||||
{
|
||||
title = $"{title}: {message}";
|
||||
}
|
||||
}
|
||||
|
||||
return await _userDialogs.ActionSheetAsync(title, cancel, null, null, buttons.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,11 @@ namespace Bit.iOS.Core.Services
|
|||
// do nothing
|
||||
}
|
||||
|
||||
public Task<string> DisplayAlertAsync(string title, string message, string cancel, params string[] buttons)
|
||||
{
|
||||
return Task.FromResult<string>(null);
|
||||
}
|
||||
|
||||
public Task<string> DisplayPromptAync(string title = null, string description = null, string text = null)
|
||||
{
|
||||
return Task.FromResult<string>(null);
|
||||
|
|
|
@ -351,6 +351,32 @@ namespace Bit.iOS.Services
|
|||
return result.Task;
|
||||
}
|
||||
|
||||
public Task<string> DisplayAlertAsync(string title, string message, string cancel, params string[] buttons)
|
||||
{
|
||||
var result = new TaskCompletionSource<string>();
|
||||
var alert = UIAlertController.Create(title ?? string.Empty, message, UIAlertControllerStyle.Alert);
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(cancel))
|
||||
{
|
||||
alert.AddAction(UIAlertAction.Create(cancel, UIAlertActionStyle.Cancel, x =>
|
||||
{
|
||||
result.TrySetResult(cancel);
|
||||
}));
|
||||
}
|
||||
|
||||
foreach(var button in buttons)
|
||||
{
|
||||
alert.AddAction(UIAlertAction.Create(button, UIAlertActionStyle.Default, x =>
|
||||
{
|
||||
result.TrySetResult(button);
|
||||
}));
|
||||
}
|
||||
|
||||
var vc = GetPresentedViewController();
|
||||
vc?.PresentViewController(alert, true, null);
|
||||
return result.Task;
|
||||
}
|
||||
|
||||
private UIViewController GetPresentedViewController()
|
||||
{
|
||||
var window = UIApplication.SharedApplication.KeyWindow;
|
||||
|
|
Loading…
Reference in a new issue