mirror of
https://github.com/bitwarden/mobile.git
synced 2024-09-29 04:07:37 +02:00
add other cipher type support to vault listings
This commit is contained in:
parent
0020bd0fb7
commit
74ac9cbbbe
@ -104,7 +104,7 @@ namespace Bit.Android
|
|||||||
OpenAccessibilitySettings();
|
OpenAccessibilitySettings();
|
||||||
});
|
});
|
||||||
|
|
||||||
MessagingCenter.Subscribe<Xamarin.Forms.Application, VaultListPageModel.Login>(
|
MessagingCenter.Subscribe<Xamarin.Forms.Application, VaultListPageModel.Cipher>(
|
||||||
Xamarin.Forms.Application.Current, "Autofill", (sender, args) =>
|
Xamarin.Forms.Application.Current, "Autofill", (sender, args) =>
|
||||||
{
|
{
|
||||||
ReturnCredentials(args);
|
ReturnCredentials(args);
|
||||||
@ -128,10 +128,10 @@ namespace Bit.Android
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReturnCredentials(VaultListPageModel.Login login)
|
private void ReturnCredentials(VaultListPageModel.Cipher cipher)
|
||||||
{
|
{
|
||||||
Intent data = new Intent();
|
Intent data = new Intent();
|
||||||
if(login == null)
|
if(cipher == null)
|
||||||
{
|
{
|
||||||
data.PutExtra("canceled", "true");
|
data.PutExtra("canceled", "true");
|
||||||
}
|
}
|
||||||
@ -139,14 +139,14 @@ namespace Bit.Android
|
|||||||
{
|
{
|
||||||
var isPremium = Resolver.Resolve<ITokenService>()?.TokenPremium ?? false;
|
var isPremium = Resolver.Resolve<ITokenService>()?.TokenPremium ?? false;
|
||||||
var autoCopyEnabled = !_settings.GetValueOrDefault(Constants.SettingDisableTotpCopy, false);
|
var autoCopyEnabled = !_settings.GetValueOrDefault(Constants.SettingDisableTotpCopy, false);
|
||||||
if(isPremium && autoCopyEnabled && _deviceActionService != null && login.Totp.Value != null)
|
if(isPremium && autoCopyEnabled && _deviceActionService != null && cipher.Totp.Value != null)
|
||||||
{
|
{
|
||||||
_deviceActionService.CopyToClipboard(App.Utilities.Crypto.Totp(login.Totp.Value));
|
_deviceActionService.CopyToClipboard(App.Utilities.Crypto.Totp(cipher.Totp.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
data.PutExtra("uri", login.Uri.Value);
|
data.PutExtra("uri", cipher.Uri.Value);
|
||||||
data.PutExtra("username", login.Username);
|
data.PutExtra("username", cipher.Username);
|
||||||
data.PutExtra("password", login.Password.Value);
|
data.PutExtra("password", cipher.Password.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Parent == null)
|
if(Parent == null)
|
||||||
|
@ -68,7 +68,7 @@ namespace Bit.App
|
|||||||
|
|
||||||
if(authService.IsAuthenticated && _uri != null)
|
if(authService.IsAuthenticated && _uri != null)
|
||||||
{
|
{
|
||||||
MainPage = new ExtendedNavigationPage(new VaultAutofillListLoginsPage(_uri));
|
MainPage = new ExtendedNavigationPage(new VaultAutofillListCiphersPage(_uri));
|
||||||
}
|
}
|
||||||
else if(authService.IsAuthenticated)
|
else if(authService.IsAuthenticated)
|
||||||
{
|
{
|
||||||
|
@ -180,7 +180,7 @@
|
|||||||
<Compile Include="Pages\Settings\SettingsPage.cs" />
|
<Compile Include="Pages\Settings\SettingsPage.cs" />
|
||||||
<Compile Include="Pages\Settings\SettingsListFoldersPage.cs" />
|
<Compile Include="Pages\Settings\SettingsListFoldersPage.cs" />
|
||||||
<Compile Include="Pages\Vault\VaultCustomFieldsPage.cs" />
|
<Compile Include="Pages\Vault\VaultCustomFieldsPage.cs" />
|
||||||
<Compile Include="Pages\Vault\VaultAutofillListLoginsPage.cs" />
|
<Compile Include="Pages\Vault\VaultAutofillListCiphersPage.cs" />
|
||||||
<Compile Include="Pages\Vault\VaultAttachmentsPage.cs" />
|
<Compile Include="Pages\Vault\VaultAttachmentsPage.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Abstractions\Repositories\ICipherRepository.cs" />
|
<Compile Include="Abstractions\Repositories\ICipherRepository.cs" />
|
||||||
@ -357,7 +357,7 @@
|
|||||||
<Compile Include="Pages\Vault\VaultAddLoginPage.cs" />
|
<Compile Include="Pages\Vault\VaultAddLoginPage.cs" />
|
||||||
<Compile Include="Pages\Vault\VaultViewLoginPage.cs" />
|
<Compile Include="Pages\Vault\VaultViewLoginPage.cs" />
|
||||||
<Compile Include="Pages\Vault\VaultEditLoginPage.cs" />
|
<Compile Include="Pages\Vault\VaultEditLoginPage.cs" />
|
||||||
<Compile Include="Pages\Vault\VaultListLoginsPage.cs" />
|
<Compile Include="Pages\Vault\VaultListCiphersPage.cs" />
|
||||||
<Compile Include="Services\PasswordGenerationService.cs" />
|
<Compile Include="Services\PasswordGenerationService.cs" />
|
||||||
<Compile Include="Utilities\Base32.cs" />
|
<Compile Include="Utilities\Base32.cs" />
|
||||||
<Compile Include="Utilities\Crypto.cs" />
|
<Compile Include="Utilities\Crypto.cs" />
|
||||||
|
@ -7,15 +7,15 @@ namespace Bit.App.Controls
|
|||||||
public class VaultListViewCell : LabeledDetailCell
|
public class VaultListViewCell : LabeledDetailCell
|
||||||
{
|
{
|
||||||
public static readonly BindableProperty LoginParameterProperty = BindableProperty.Create(nameof(LoginParameter),
|
public static readonly BindableProperty LoginParameterProperty = BindableProperty.Create(nameof(LoginParameter),
|
||||||
typeof(VaultListPageModel.Login), typeof(VaultListViewCell), null);
|
typeof(VaultListPageModel.Cipher), typeof(VaultListViewCell), null);
|
||||||
|
|
||||||
public VaultListViewCell(Action<VaultListPageModel.Login> moreClickedAction)
|
public VaultListViewCell(Action<VaultListPageModel.Cipher> moreClickedAction)
|
||||||
{
|
{
|
||||||
SetBinding(LoginParameterProperty, new Binding("."));
|
SetBinding(LoginParameterProperty, new Binding("."));
|
||||||
Label.SetBinding(Label.TextProperty, nameof(VaultListPageModel.Login.Name));
|
Label.SetBinding(Label.TextProperty, nameof(VaultListPageModel.Cipher.Name));
|
||||||
Detail.SetBinding(Label.TextProperty, nameof(VaultListPageModel.Login.Username));
|
Detail.SetBinding(Label.TextProperty, nameof(VaultListPageModel.Cipher.Subtitle));
|
||||||
LabelIcon.SetBinding(VisualElement.IsVisibleProperty, nameof(VaultListPageModel.Login.Shared));
|
LabelIcon.SetBinding(VisualElement.IsVisibleProperty, nameof(VaultListPageModel.Cipher.Shared));
|
||||||
LabelIcon2.SetBinding(VisualElement.IsVisibleProperty, nameof(VaultListPageModel.Login.HasAttachments));
|
LabelIcon2.SetBinding(VisualElement.IsVisibleProperty, nameof(VaultListPageModel.Cipher.HasAttachments));
|
||||||
|
|
||||||
Button.Image = "more";
|
Button.Image = "more";
|
||||||
Button.Command = new Command(() => moreClickedAction?.Invoke(LoginParameter));
|
Button.Command = new Command(() => moreClickedAction?.Invoke(LoginParameter));
|
||||||
@ -27,9 +27,9 @@ namespace Bit.App.Controls
|
|||||||
BackgroundColor = Color.White;
|
BackgroundColor = Color.White;
|
||||||
}
|
}
|
||||||
|
|
||||||
public VaultListPageModel.Login LoginParameter
|
public VaultListPageModel.Cipher LoginParameter
|
||||||
{
|
{
|
||||||
get { return GetValue(LoginParameterProperty) as VaultListPageModel.Login; }
|
get { return GetValue(LoginParameterProperty) as VaultListPageModel.Cipher; }
|
||||||
set { SetValue(LoginParameterProperty, value); }
|
set { SetValue(LoginParameterProperty, value); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,24 +2,51 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Bit.App.Enums;
|
||||||
|
|
||||||
namespace Bit.App.Models.Page
|
namespace Bit.App.Models.Page
|
||||||
{
|
{
|
||||||
public class VaultListPageModel
|
public class VaultListPageModel
|
||||||
{
|
{
|
||||||
public class Login
|
public class Cipher
|
||||||
{
|
{
|
||||||
public Login(Models.Cipher cipher)
|
public Cipher(Models.Cipher cipher)
|
||||||
{
|
{
|
||||||
Id = cipher.Id;
|
Id = cipher.Id;
|
||||||
Shared = !string.IsNullOrWhiteSpace(cipher.OrganizationId);
|
Shared = !string.IsNullOrWhiteSpace(cipher.OrganizationId);
|
||||||
HasAttachments = cipher.Attachments?.Any() ?? false;
|
HasAttachments = cipher.Attachments?.Any() ?? false;
|
||||||
FolderId = cipher.FolderId;
|
FolderId = cipher.FolderId;
|
||||||
Name = cipher.Name?.Decrypt(cipher.OrganizationId);
|
Name = cipher.Name?.Decrypt(cipher.OrganizationId);
|
||||||
Username = cipher.Login?.Username?.Decrypt(cipher.OrganizationId) ?? " ";
|
Type = cipher.Type;
|
||||||
Password = new Lazy<string>(() => cipher.Login?.Password?.Decrypt(cipher.OrganizationId));
|
|
||||||
Uri = new Lazy<string>(() => cipher.Login?.Uri?.Decrypt(cipher.OrganizationId));
|
switch(cipher.Type)
|
||||||
Totp = new Lazy<string>(() => cipher.Login?.Totp?.Decrypt(cipher.OrganizationId));
|
{
|
||||||
|
case CipherType.Login:
|
||||||
|
Username = cipher.Login?.Username?.Decrypt(cipher.OrganizationId) ?? " ";
|
||||||
|
Password = new Lazy<string>(() => cipher.Login?.Password?.Decrypt(cipher.OrganizationId));
|
||||||
|
Uri = new Lazy<string>(() => cipher.Login?.Uri?.Decrypt(cipher.OrganizationId));
|
||||||
|
Totp = new Lazy<string>(() => cipher.Login?.Totp?.Decrypt(cipher.OrganizationId));
|
||||||
|
|
||||||
|
Subtitle = Username;
|
||||||
|
break;
|
||||||
|
case CipherType.SecureNote:
|
||||||
|
Subtitle = " ";
|
||||||
|
break;
|
||||||
|
case CipherType.Card:
|
||||||
|
var cardNumber = cipher.Card?.Number?.Decrypt(cipher.OrganizationId) ?? " ";
|
||||||
|
var cardBrand = cipher.Card?.Brand?.Decrypt(cipher.OrganizationId) ?? " ";
|
||||||
|
CardCode = new Lazy<string>(() => cipher.Card?.Code?.Decrypt(cipher.OrganizationId));
|
||||||
|
|
||||||
|
Subtitle = $"{cardBrand}, *{cardNumber}";
|
||||||
|
break;
|
||||||
|
case CipherType.Identity:
|
||||||
|
var firstName = cipher.Identity?.FirstName?.Decrypt(cipher.OrganizationId) ?? " ";
|
||||||
|
var lastName = cipher.Identity?.LastName?.Decrypt(cipher.OrganizationId) ?? " ";
|
||||||
|
Subtitle = $"{firstName} {lastName}";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
@ -27,16 +54,24 @@ namespace Bit.App.Models.Page
|
|||||||
public bool HasAttachments { get; set; }
|
public bool HasAttachments { get; set; }
|
||||||
public string FolderId { get; set; }
|
public string FolderId { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
public string Subtitle { get; set; }
|
||||||
|
public CipherType Type { get; set; }
|
||||||
|
|
||||||
|
// Login metadata
|
||||||
public string Username { get; set; }
|
public string Username { get; set; }
|
||||||
public Lazy<string> Password { get; set; }
|
public Lazy<string> Password { get; set; }
|
||||||
public Lazy<string> Uri { get; set; }
|
public Lazy<string> Uri { get; set; }
|
||||||
public Lazy<string> Totp { get; set; }
|
public Lazy<string> Totp { get; set; }
|
||||||
|
|
||||||
|
// Login metadata
|
||||||
|
public string CardNumber { get; set; }
|
||||||
|
public Lazy<string> CardCode { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AutofillLogin : Login
|
public class AutofillCipher : Cipher
|
||||||
{
|
{
|
||||||
public AutofillLogin(Models.Cipher login, bool fuzzy = false)
|
public AutofillCipher(Models.Cipher cipher, bool fuzzy = false)
|
||||||
: base(login)
|
: base(cipher)
|
||||||
{
|
{
|
||||||
Fuzzy = fuzzy;
|
Fuzzy = fuzzy;
|
||||||
}
|
}
|
||||||
@ -44,7 +79,7 @@ namespace Bit.App.Models.Page
|
|||||||
public bool Fuzzy { get; set; }
|
public bool Fuzzy { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Folder : List<Login>
|
public class Folder : List<Cipher>
|
||||||
{
|
{
|
||||||
public Folder(Models.Folder folder)
|
public Folder(Models.Folder folder)
|
||||||
{
|
{
|
||||||
@ -52,18 +87,18 @@ namespace Bit.App.Models.Page
|
|||||||
Name = folder.Name?.Decrypt();
|
Name = folder.Name?.Decrypt();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Folder(List<Login> logins)
|
public Folder(List<Cipher> ciphers)
|
||||||
{
|
{
|
||||||
AddRange(logins);
|
AddRange(ciphers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
public string Name { get; set; } = AppResources.FolderNone;
|
public string Name { get; set; } = AppResources.FolderNone;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AutofillGrouping : List<AutofillLogin>
|
public class AutofillGrouping : List<AutofillCipher>
|
||||||
{
|
{
|
||||||
public AutofillGrouping(List<AutofillLogin> logins, string name)
|
public AutofillGrouping(List<AutofillCipher> logins, string name)
|
||||||
{
|
{
|
||||||
AddRange(logins);
|
AddRange(logins);
|
||||||
Name = name;
|
Name = name;
|
||||||
|
@ -12,8 +12,8 @@ namespace Bit.App.Pages
|
|||||||
TintColor = Color.FromHex("3c8dbc");
|
TintColor = Color.FromHex("3c8dbc");
|
||||||
|
|
||||||
var settingsNavigation = new ExtendedNavigationPage(new SettingsPage());
|
var settingsNavigation = new ExtendedNavigationPage(new SettingsPage());
|
||||||
var favoritesNavigation = new ExtendedNavigationPage(new VaultListLoginsPage(true, uri));
|
var favoritesNavigation = new ExtendedNavigationPage(new VaultListCiphersPage(true, uri));
|
||||||
var vaultNavigation = new ExtendedNavigationPage(new VaultListLoginsPage(false, uri));
|
var vaultNavigation = new ExtendedNavigationPage(new VaultListCiphersPage(false, uri));
|
||||||
var toolsNavigation = new ExtendedNavigationPage(new ToolsPage());
|
var toolsNavigation = new ExtendedNavigationPage(new ToolsPage());
|
||||||
|
|
||||||
favoritesNavigation.Title = AppResources.Favorites;
|
favoritesNavigation.Title = AppResources.Favorites;
|
||||||
|
@ -15,7 +15,7 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
public class VaultAutofillListLoginsPage : ExtendedContentPage
|
public class VaultAutofillListCiphersPage : ExtendedContentPage
|
||||||
{
|
{
|
||||||
private readonly ICipherService _cipherService;
|
private readonly ICipherService _cipherService;
|
||||||
private readonly IDeviceInfoService _deviceInfoService;
|
private readonly IDeviceInfoService _deviceInfoService;
|
||||||
@ -24,7 +24,7 @@ namespace Bit.App.Pages
|
|||||||
private CancellationTokenSource _filterResultsCancellationTokenSource;
|
private CancellationTokenSource _filterResultsCancellationTokenSource;
|
||||||
private readonly string _name;
|
private readonly string _name;
|
||||||
|
|
||||||
public VaultAutofillListLoginsPage(string uriString)
|
public VaultAutofillListCiphersPage(string uriString)
|
||||||
: base(true)
|
: base(true)
|
||||||
{
|
{
|
||||||
Uri = uriString;
|
Uri = uriString;
|
||||||
@ -50,13 +50,13 @@ namespace Bit.App.Pages
|
|||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExtendedObservableCollection<VaultListPageModel.AutofillGrouping> PresentationLoginsGroup { get; private set; }
|
public ExtendedObservableCollection<VaultListPageModel.AutofillGrouping> PresentationCiphersGroup { get; private set; }
|
||||||
= new ExtendedObservableCollection<VaultListPageModel.AutofillGrouping>();
|
= new ExtendedObservableCollection<VaultListPageModel.AutofillGrouping>();
|
||||||
public StackLayout NoDataStackLayout { get; set; }
|
public StackLayout NoDataStackLayout { get; set; }
|
||||||
public ListView ListView { get; set; }
|
public ListView ListView { get; set; }
|
||||||
public ActivityIndicator LoadingIndicator { get; set; }
|
public ActivityIndicator LoadingIndicator { get; set; }
|
||||||
private SearchToolBarItem SearchItem { get; set; }
|
private SearchToolBarItem SearchItem { get; set; }
|
||||||
private AddLoginToolBarItem AddLoginItem { get; set; }
|
private AddCipherToolBarItem AddCipherItem { get; set; }
|
||||||
private IGoogleAnalyticsService GoogleAnalyticsService { get; set; }
|
private IGoogleAnalyticsService GoogleAnalyticsService { get; set; }
|
||||||
private IUserDialogs UserDialogs { get; set; }
|
private IUserDialogs UserDialogs { get; set; }
|
||||||
private string Uri { get; set; }
|
private string Uri { get; set; }
|
||||||
@ -71,34 +71,34 @@ namespace Bit.App.Pages
|
|||||||
Style = (Style)Application.Current.Resources["text-muted"]
|
Style = (Style)Application.Current.Resources["text-muted"]
|
||||||
};
|
};
|
||||||
|
|
||||||
var addLoginButton = new ExtendedButton
|
var addCipherButton = new ExtendedButton
|
||||||
{
|
{
|
||||||
Text = AppResources.AddALogin,
|
Text = AppResources.AddALogin,
|
||||||
Command = new Command(() => AddLoginAsync()),
|
Command = new Command(() => AddCipherAsync()),
|
||||||
Style = (Style)Application.Current.Resources["btn-primaryAccent"]
|
Style = (Style)Application.Current.Resources["btn-primaryAccent"]
|
||||||
};
|
};
|
||||||
|
|
||||||
NoDataStackLayout = new StackLayout
|
NoDataStackLayout = new StackLayout
|
||||||
{
|
{
|
||||||
Children = { noDataLabel, addLoginButton },
|
Children = { noDataLabel, addCipherButton },
|
||||||
VerticalOptions = LayoutOptions.CenterAndExpand,
|
VerticalOptions = LayoutOptions.CenterAndExpand,
|
||||||
Padding = new Thickness(20, 0),
|
Padding = new Thickness(20, 0),
|
||||||
Spacing = 20
|
Spacing = 20
|
||||||
};
|
};
|
||||||
|
|
||||||
AddLoginItem = new AddLoginToolBarItem(this);
|
AddCipherItem = new AddCipherToolBarItem(this);
|
||||||
ToolbarItems.Add(AddLoginItem);
|
ToolbarItems.Add(AddCipherItem);
|
||||||
SearchItem = new SearchToolBarItem(this);
|
SearchItem = new SearchToolBarItem(this);
|
||||||
ToolbarItems.Add(SearchItem);
|
ToolbarItems.Add(SearchItem);
|
||||||
|
|
||||||
ListView = new ListView(ListViewCachingStrategy.RecycleElement)
|
ListView = new ListView(ListViewCachingStrategy.RecycleElement)
|
||||||
{
|
{
|
||||||
IsGroupingEnabled = true,
|
IsGroupingEnabled = true,
|
||||||
ItemsSource = PresentationLoginsGroup,
|
ItemsSource = PresentationCiphersGroup,
|
||||||
HasUnevenRows = true,
|
HasUnevenRows = true,
|
||||||
GroupHeaderTemplate = new DataTemplate(() => new HeaderViewCell()),
|
GroupHeaderTemplate = new DataTemplate(() => new HeaderViewCell()),
|
||||||
ItemTemplate = new DataTemplate(() => new VaultListViewCell(
|
ItemTemplate = new DataTemplate(() => new VaultListViewCell(
|
||||||
(VaultListPageModel.Login l) => MoreClickedAsync(l)))
|
(VaultListPageModel.Cipher l) => MoreClickedAsync(l)))
|
||||||
};
|
};
|
||||||
|
|
||||||
if(Device.RuntimePlatform == Device.iOS)
|
if(Device.RuntimePlatform == Device.iOS)
|
||||||
@ -121,8 +121,8 @@ namespace Bit.App.Pages
|
|||||||
protected override void OnAppearing()
|
protected override void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
ListView.ItemSelected += LoginSelected;
|
ListView.ItemSelected += CipherSelected;
|
||||||
AddLoginItem.InitEvents();
|
AddCipherItem.InitEvents();
|
||||||
SearchItem.InitEvents();
|
SearchItem.InitEvents();
|
||||||
_filterResultsCancellationTokenSource = FetchAndLoadVault();
|
_filterResultsCancellationTokenSource = FetchAndLoadVault();
|
||||||
}
|
}
|
||||||
@ -130,21 +130,21 @@ namespace Bit.App.Pages
|
|||||||
protected override void OnDisappearing()
|
protected override void OnDisappearing()
|
||||||
{
|
{
|
||||||
base.OnDisappearing();
|
base.OnDisappearing();
|
||||||
ListView.ItemSelected -= LoginSelected;
|
ListView.ItemSelected -= CipherSelected;
|
||||||
AddLoginItem.Dispose();
|
AddCipherItem.Dispose();
|
||||||
SearchItem.Dispose();
|
SearchItem.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnBackButtonPressed()
|
protected override bool OnBackButtonPressed()
|
||||||
{
|
{
|
||||||
GoogleAnalyticsService.TrackExtensionEvent("BackClosed", Uri.StartsWith("http") ? "Website" : "App");
|
GoogleAnalyticsService.TrackExtensionEvent("BackClosed", Uri.StartsWith("http") ? "Website" : "App");
|
||||||
MessagingCenter.Send(Application.Current, "Autofill", (VaultListPageModel.Login)null);
|
MessagingCenter.Send(Application.Current, "Autofill", (VaultListPageModel.Cipher)null);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AdjustContent()
|
private void AdjustContent()
|
||||||
{
|
{
|
||||||
if(PresentationLoginsGroup.Count > 0)
|
if(PresentationCiphersGroup.Count > 0)
|
||||||
{
|
{
|
||||||
Content = ListView;
|
Content = ListView;
|
||||||
}
|
}
|
||||||
@ -162,18 +162,18 @@ namespace Bit.App.Pages
|
|||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
var autofillGroupings = new List<VaultListPageModel.AutofillGrouping>();
|
var autofillGroupings = new List<VaultListPageModel.AutofillGrouping>();
|
||||||
var logins = await _cipherService.GetAllAsync(Uri);
|
var ciphers = await _cipherService.GetAllAsync(Uri);
|
||||||
|
|
||||||
var normalLogins = logins?.Item1.Select(l => new VaultListPageModel.AutofillLogin(l, false))
|
var normalLogins = ciphers?.Item1.Select(l => new VaultListPageModel.AutofillCipher(l, false))
|
||||||
.OrderBy(s => s.Name)
|
.OrderBy(s => s.Name)
|
||||||
.ThenBy(s => s.Username)
|
.ThenBy(s => s.Subtitle)
|
||||||
.ToList();
|
.ToList();
|
||||||
if(normalLogins?.Any() ?? false)
|
if(normalLogins?.Any() ?? false)
|
||||||
{
|
{
|
||||||
autofillGroupings.Add(new VaultListPageModel.AutofillGrouping(normalLogins, AppResources.MatchingLogins));
|
autofillGroupings.Add(new VaultListPageModel.AutofillGrouping(normalLogins, AppResources.MatchingLogins));
|
||||||
}
|
}
|
||||||
|
|
||||||
var fuzzyLogins = logins?.Item2.Select(l => new VaultListPageModel.AutofillLogin(l, true))
|
var fuzzyLogins = ciphers?.Item2.Select(l => new VaultListPageModel.AutofillCipher(l, true))
|
||||||
.OrderBy(s => s.Name)
|
.OrderBy(s => s.Name)
|
||||||
.ThenBy(s => s.Username)
|
.ThenBy(s => s.Username)
|
||||||
.ToList();
|
.ToList();
|
||||||
@ -187,7 +187,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
if(autofillGroupings.Any())
|
if(autofillGroupings.Any())
|
||||||
{
|
{
|
||||||
PresentationLoginsGroup.ResetWithRange(autofillGroupings);
|
PresentationCiphersGroup.ResetWithRange(autofillGroupings);
|
||||||
}
|
}
|
||||||
|
|
||||||
AdjustContent();
|
AdjustContent();
|
||||||
@ -197,22 +197,22 @@ namespace Bit.App.Pages
|
|||||||
return cts;
|
return cts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void LoginSelected(object sender, SelectedItemChangedEventArgs e)
|
private async void CipherSelected(object sender, SelectedItemChangedEventArgs e)
|
||||||
{
|
{
|
||||||
var login = e.SelectedItem as VaultListPageModel.AutofillLogin;
|
var cipher = e.SelectedItem as VaultListPageModel.AutofillCipher;
|
||||||
if(login == null)
|
if(cipher == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_deviceInfoService.Version < 21)
|
if(_deviceInfoService.Version < 21)
|
||||||
{
|
{
|
||||||
MoreClickedAsync(login);
|
MoreClickedAsync(cipher);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bool doAutofill = true;
|
bool doAutofill = true;
|
||||||
if(login.Fuzzy)
|
if(cipher.Fuzzy)
|
||||||
{
|
{
|
||||||
doAutofill = await UserDialogs.ConfirmAsync(
|
doAutofill = await UserDialogs.ConfirmAsync(
|
||||||
string.Format(AppResources.BitwardenAutofillServiceMatchConfirm, _name),
|
string.Format(AppResources.BitwardenAutofillServiceMatchConfirm, _name),
|
||||||
@ -222,50 +222,73 @@ namespace Bit.App.Pages
|
|||||||
if(doAutofill)
|
if(doAutofill)
|
||||||
{
|
{
|
||||||
GoogleAnalyticsService.TrackExtensionEvent("AutoFilled", Uri.StartsWith("http") ? "Website" : "App");
|
GoogleAnalyticsService.TrackExtensionEvent("AutoFilled", Uri.StartsWith("http") ? "Website" : "App");
|
||||||
MessagingCenter.Send(Application.Current, "Autofill", login as VaultListPageModel.Login);
|
MessagingCenter.Send(Application.Current, "Autofill", cipher as VaultListPageModel.Cipher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
((ListView)sender).SelectedItem = null;
|
((ListView)sender).SelectedItem = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void AddLoginAsync()
|
private async void AddCipherAsync()
|
||||||
{
|
{
|
||||||
var page = new VaultAddLoginPage(Uri, _name, true);
|
var page = new VaultAddLoginPage(Uri, _name, true);
|
||||||
await Navigation.PushForDeviceAsync(page);
|
await Navigation.PushForDeviceAsync(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void MoreClickedAsync(VaultListPageModel.Login login)
|
private async void MoreClickedAsync(VaultListPageModel.Cipher cipher)
|
||||||
{
|
{
|
||||||
var buttons = new List<string> { AppResources.View, AppResources.Edit };
|
var buttons = new List<string> { AppResources.View, AppResources.Edit };
|
||||||
if(!string.IsNullOrWhiteSpace(login.Password.Value))
|
|
||||||
|
if(cipher.Type == Enums.CipherType.Login)
|
||||||
{
|
{
|
||||||
buttons.Add(AppResources.CopyPassword);
|
if(!string.IsNullOrWhiteSpace(cipher.Password.Value))
|
||||||
|
{
|
||||||
|
buttons.Add(AppResources.CopyPassword);
|
||||||
|
}
|
||||||
|
if(!string.IsNullOrWhiteSpace(cipher.Username))
|
||||||
|
{
|
||||||
|
buttons.Add(AppResources.CopyUsername);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(!string.IsNullOrWhiteSpace(login.Username))
|
else if(cipher.Type == Enums.CipherType.Card)
|
||||||
{
|
{
|
||||||
buttons.Add(AppResources.CopyUsername);
|
if(!string.IsNullOrWhiteSpace(cipher.CardNumber))
|
||||||
|
{
|
||||||
|
buttons.Add(AppResources.CopyNumber);
|
||||||
|
}
|
||||||
|
if(!string.IsNullOrWhiteSpace(cipher.CardCode.Value))
|
||||||
|
{
|
||||||
|
buttons.Add(AppResources.CopySecurityCode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var selection = await DisplayActionSheet(login.Name, AppResources.Cancel, null, buttons.ToArray());
|
var selection = await DisplayActionSheet(cipher.Name, AppResources.Cancel, null, buttons.ToArray());
|
||||||
|
|
||||||
if(selection == AppResources.View)
|
if(selection == AppResources.View)
|
||||||
{
|
{
|
||||||
var page = new VaultViewLoginPage(login.Id);
|
var page = new VaultViewLoginPage(cipher.Id);
|
||||||
await Navigation.PushForDeviceAsync(page);
|
await Navigation.PushForDeviceAsync(page);
|
||||||
}
|
}
|
||||||
else if(selection == AppResources.Edit)
|
else if(selection == AppResources.Edit)
|
||||||
{
|
{
|
||||||
var page = new VaultEditLoginPage(login.Id);
|
var page = new VaultEditLoginPage(cipher.Id);
|
||||||
await Navigation.PushForDeviceAsync(page);
|
await Navigation.PushForDeviceAsync(page);
|
||||||
}
|
}
|
||||||
else if(selection == AppResources.CopyPassword)
|
else if(selection == AppResources.CopyPassword)
|
||||||
{
|
{
|
||||||
Copy(login.Password.Value, AppResources.Password);
|
Copy(cipher.Password.Value, AppResources.Password);
|
||||||
}
|
}
|
||||||
else if(selection == AppResources.CopyUsername)
|
else if(selection == AppResources.CopyUsername)
|
||||||
{
|
{
|
||||||
Copy(login.Username, AppResources.Username);
|
Copy(cipher.Username, AppResources.Username);
|
||||||
|
}
|
||||||
|
else if(selection == AppResources.CopyNumber)
|
||||||
|
{
|
||||||
|
Copy(cipher.CardNumber, AppResources.Number);
|
||||||
|
}
|
||||||
|
else if(selection == AppResources.CopySecurityCode)
|
||||||
|
{
|
||||||
|
Copy(cipher.CardCode.Value, AppResources.SecurityCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,26 +298,26 @@ namespace Bit.App.Pages
|
|||||||
UserDialogs.Toast(string.Format(AppResources.ValueHasBeenCopied, alertLabel));
|
UserDialogs.Toast(string.Format(AppResources.ValueHasBeenCopied, alertLabel));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AddLoginToolBarItem : ExtendedToolbarItem
|
private class AddCipherToolBarItem : ExtendedToolbarItem
|
||||||
{
|
{
|
||||||
public AddLoginToolBarItem(VaultAutofillListLoginsPage page)
|
public AddCipherToolBarItem(VaultAutofillListCiphersPage page)
|
||||||
: base(() => page.AddLoginAsync())
|
: base(() => page.AddCipherAsync())
|
||||||
{
|
{
|
||||||
Text = AppResources.Add;
|
Text = AppResources.Add;
|
||||||
Icon = "plus";
|
Icon = "plus.png";
|
||||||
Priority = 2;
|
Priority = 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SearchToolBarItem : ExtendedToolbarItem
|
private class SearchToolBarItem : ExtendedToolbarItem
|
||||||
{
|
{
|
||||||
private readonly VaultAutofillListLoginsPage _page;
|
private readonly VaultAutofillListCiphersPage _page;
|
||||||
|
|
||||||
public SearchToolBarItem(VaultAutofillListLoginsPage page)
|
public SearchToolBarItem(VaultAutofillListCiphersPage page)
|
||||||
{
|
{
|
||||||
_page = page;
|
_page = page;
|
||||||
Text = AppResources.Search;
|
Text = AppResources.Search;
|
||||||
Icon = "search";
|
Icon = "search.png";
|
||||||
Priority = 1;
|
Priority = 1;
|
||||||
ClickAction = () => DoClick();
|
ClickAction = () => DoClick();
|
||||||
}
|
}
|
@ -17,7 +17,7 @@ using FFImageLoading.Forms;
|
|||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
public class VaultListLoginsPage : ExtendedContentPage
|
public class VaultListCiphersPage : ExtendedContentPage
|
||||||
{
|
{
|
||||||
private readonly IFolderService _folderService;
|
private readonly IFolderService _folderService;
|
||||||
private readonly ICipherService _cipherService;
|
private readonly ICipherService _cipherService;
|
||||||
@ -32,7 +32,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly bool _favorites;
|
private readonly bool _favorites;
|
||||||
private CancellationTokenSource _filterResultsCancellationTokenSource;
|
private CancellationTokenSource _filterResultsCancellationTokenSource;
|
||||||
|
|
||||||
public VaultListLoginsPage(bool favorites, string uri = null)
|
public VaultListCiphersPage(bool favorites, string uri = null)
|
||||||
: base(true)
|
: base(true)
|
||||||
{
|
{
|
||||||
_favorites = favorites;
|
_favorites = favorites;
|
||||||
@ -57,13 +57,13 @@ namespace Bit.App.Pages
|
|||||||
public ExtendedObservableCollection<VaultListPageModel.Folder> PresentationFolders { get; private set; }
|
public ExtendedObservableCollection<VaultListPageModel.Folder> PresentationFolders { get; private set; }
|
||||||
= new ExtendedObservableCollection<VaultListPageModel.Folder>();
|
= new ExtendedObservableCollection<VaultListPageModel.Folder>();
|
||||||
public ListView ListView { get; set; }
|
public ListView ListView { get; set; }
|
||||||
public VaultListPageModel.Login[] Logins { get; set; } = new VaultListPageModel.Login[] { };
|
public VaultListPageModel.Cipher[] Ciphers { get; set; } = new VaultListPageModel.Cipher[] { };
|
||||||
public VaultListPageModel.Folder[] Folders { get; set; } = new VaultListPageModel.Folder[] { };
|
public VaultListPageModel.Folder[] Folders { get; set; } = new VaultListPageModel.Folder[] { };
|
||||||
public SearchBar Search { get; set; }
|
public SearchBar Search { get; set; }
|
||||||
public StackLayout NoDataStackLayout { get; set; }
|
public StackLayout NoDataStackLayout { get; set; }
|
||||||
public StackLayout ResultsStackLayout { get; set; }
|
public StackLayout ResultsStackLayout { get; set; }
|
||||||
public ActivityIndicator LoadingIndicator { get; set; }
|
public ActivityIndicator LoadingIndicator { get; set; }
|
||||||
private AddLoginToolBarItem AddLoginItem { get; set; }
|
private AddCipherToolBarItem AddCipherItem { get; set; }
|
||||||
public string Uri { get; set; }
|
public string Uri { get; set; }
|
||||||
|
|
||||||
private void Init()
|
private void Init()
|
||||||
@ -78,8 +78,8 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
if(!_favorites)
|
if(!_favorites)
|
||||||
{
|
{
|
||||||
AddLoginItem = new AddLoginToolBarItem(this);
|
AddCipherItem = new AddCipherToolBarItem(this);
|
||||||
ToolbarItems.Add(AddLoginItem);
|
ToolbarItems.Add(AddCipherItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
ListView = new ListView(ListViewCachingStrategy.RecycleElement)
|
ListView = new ListView(ListViewCachingStrategy.RecycleElement)
|
||||||
@ -89,7 +89,7 @@ namespace Bit.App.Pages
|
|||||||
HasUnevenRows = true,
|
HasUnevenRows = true,
|
||||||
GroupHeaderTemplate = new DataTemplate(() => new VaultListHeaderViewCell(this)),
|
GroupHeaderTemplate = new DataTemplate(() => new VaultListHeaderViewCell(this)),
|
||||||
ItemTemplate = new DataTemplate(() => new VaultListViewCell(
|
ItemTemplate = new DataTemplate(() => new VaultListViewCell(
|
||||||
(VaultListPageModel.Login l) => MoreClickedAsync(l)))
|
(VaultListPageModel.Cipher c) => MoreClickedAsync(c)))
|
||||||
};
|
};
|
||||||
|
|
||||||
if(Device.RuntimePlatform == Device.iOS)
|
if(Device.RuntimePlatform == Device.iOS)
|
||||||
@ -135,14 +135,14 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
if(!_favorites)
|
if(!_favorites)
|
||||||
{
|
{
|
||||||
var addLoginButton = new ExtendedButton
|
var addCipherButton = new ExtendedButton
|
||||||
{
|
{
|
||||||
Text = AppResources.AddALogin,
|
Text = AppResources.AddALogin,
|
||||||
Command = new Command(() => AddLogin()),
|
Command = new Command(() => AddCipher()),
|
||||||
Style = (Style)Application.Current.Resources["btn-primaryAccent"]
|
Style = (Style)Application.Current.Resources["btn-primaryAccent"]
|
||||||
};
|
};
|
||||||
|
|
||||||
NoDataStackLayout.Children.Add(addLoginButton);
|
NoDataStackLayout.Children.Add(addCipherButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadingIndicator = new ActivityIndicator
|
LoadingIndicator = new ActivityIndicator
|
||||||
@ -208,28 +208,28 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(searchFilter))
|
if(string.IsNullOrWhiteSpace(searchFilter))
|
||||||
{
|
{
|
||||||
LoadFolders(Logins, ct);
|
LoadFolders(Ciphers, ct);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
searchFilter = searchFilter.ToLower();
|
searchFilter = searchFilter.ToLower();
|
||||||
var filteredLogins = Logins
|
var filteredCiphers = Ciphers
|
||||||
.Where(s => s.Name.ToLower().Contains(searchFilter) || s.Username.ToLower().Contains(searchFilter))
|
.Where(s => s.Name.ToLower().Contains(searchFilter) || s.Subtitle.ToLower().Contains(searchFilter))
|
||||||
.TakeWhile(s => !ct.IsCancellationRequested)
|
.TakeWhile(s => !ct.IsCancellationRequested)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
ct.ThrowIfCancellationRequested();
|
ct.ThrowIfCancellationRequested();
|
||||||
LoadFolders(filteredLogins, ct);
|
LoadFolders(filteredCiphers, ct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnAppearing()
|
protected override void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
ListView.ItemSelected += LoginSelected;
|
ListView.ItemSelected += CipherSelected;
|
||||||
Search.TextChanged += SearchBar_TextChanged;
|
Search.TextChanged += SearchBar_TextChanged;
|
||||||
Search.SearchButtonPressed += SearchBar_SearchButtonPressed;
|
Search.SearchButtonPressed += SearchBar_SearchButtonPressed;
|
||||||
AddLoginItem?.InitEvents();
|
AddCipherItem?.InitEvents();
|
||||||
|
|
||||||
_filterResultsCancellationTokenSource = FetchAndLoadVault();
|
_filterResultsCancellationTokenSource = FetchAndLoadVault();
|
||||||
|
|
||||||
@ -267,10 +267,10 @@ namespace Bit.App.Pages
|
|||||||
protected override void OnDisappearing()
|
protected override void OnDisappearing()
|
||||||
{
|
{
|
||||||
base.OnDisappearing();
|
base.OnDisappearing();
|
||||||
ListView.ItemSelected -= LoginSelected;
|
ListView.ItemSelected -= CipherSelected;
|
||||||
Search.TextChanged -= SearchBar_TextChanged;
|
Search.TextChanged -= SearchBar_TextChanged;
|
||||||
Search.SearchButtonPressed -= SearchBar_SearchButtonPressed;
|
Search.SearchButtonPressed -= SearchBar_SearchButtonPressed;
|
||||||
AddLoginItem?.Dispose();
|
AddCipherItem?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnBackButtonPressed()
|
protected override bool OnBackButtonPressed()
|
||||||
@ -281,7 +281,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
|
|
||||||
_googleAnalyticsService.TrackExtensionEvent("BackClosed", Uri.StartsWith("http") ? "Website" : "App");
|
_googleAnalyticsService.TrackExtensionEvent("BackClosed", Uri.StartsWith("http") ? "Website" : "App");
|
||||||
MessagingCenter.Send(Application.Current, "Autofill", (VaultListPageModel.Login)null);
|
MessagingCenter.Send(Application.Current, "Autofill", (VaultListPageModel.Cipher)null);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,21 +310,21 @@ namespace Bit.App.Pages
|
|||||||
Task.Run(async () =>
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
var foldersTask = _folderService.GetAllAsync();
|
var foldersTask = _folderService.GetAllAsync();
|
||||||
var loginsTask = _favorites ? _cipherService.GetAllAsync(true) : _cipherService.GetAllAsync();
|
var ciphersTask = _favorites ? _cipherService.GetAllAsync(true) : _cipherService.GetAllAsync();
|
||||||
await Task.WhenAll(foldersTask, loginsTask);
|
await Task.WhenAll(foldersTask, ciphersTask);
|
||||||
|
|
||||||
var folders = await foldersTask;
|
var folders = await foldersTask;
|
||||||
var logins = await loginsTask;
|
var ciphers = await ciphersTask;
|
||||||
|
|
||||||
Folders = folders
|
Folders = folders
|
||||||
.Select(f => new VaultListPageModel.Folder(f))
|
.Select(f => new VaultListPageModel.Folder(f))
|
||||||
.OrderBy(s => s.Name)
|
.OrderBy(s => s.Name)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
Logins = logins
|
Ciphers = ciphers
|
||||||
.Select(s => new VaultListPageModel.Login(s))
|
.Select(s => new VaultListPageModel.Cipher(s))
|
||||||
.OrderBy(s => s.Name)
|
.OrderBy(s => s.Name)
|
||||||
.ThenBy(s => s.Username)
|
.ThenBy(s => s.Subtitle)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -337,7 +337,7 @@ namespace Bit.App.Pages
|
|||||||
return cts;
|
return cts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadFolders(VaultListPageModel.Login[] logins, CancellationToken ct)
|
private void LoadFolders(VaultListPageModel.Cipher[] ciphers, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var folders = new List<VaultListPageModel.Folder>(Folders);
|
var folders = new List<VaultListPageModel.Folder>(Folders);
|
||||||
|
|
||||||
@ -348,16 +348,16 @@ namespace Bit.App.Pages
|
|||||||
folder.Clear();
|
folder.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
var loginsToAdd = logins
|
var ciphersToAdd = ciphers
|
||||||
.Where(s => s.FolderId == folder.Id)
|
.Where(s => s.FolderId == folder.Id)
|
||||||
.TakeWhile(s => !ct.IsCancellationRequested)
|
.TakeWhile(s => !ct.IsCancellationRequested)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
ct.ThrowIfCancellationRequested();
|
ct.ThrowIfCancellationRequested();
|
||||||
folder.AddRange(loginsToAdd);
|
folder.AddRange(ciphersToAdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
var noneToAdd = logins
|
var noneToAdd = ciphers
|
||||||
.Where(s => s.FolderId == null)
|
.Where(s => s.FolderId == null)
|
||||||
.TakeWhile(s => !ct.IsCancellationRequested)
|
.TakeWhile(s => !ct.IsCancellationRequested)
|
||||||
.ToList();
|
.ToList();
|
||||||
@ -380,10 +380,10 @@ namespace Bit.App.Pages
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void LoginSelected(object sender, SelectedItemChangedEventArgs e)
|
private async void CipherSelected(object sender, SelectedItemChangedEventArgs e)
|
||||||
{
|
{
|
||||||
var login = e.SelectedItem as VaultListPageModel.Login;
|
var cipher = e.SelectedItem as VaultListPageModel.Cipher;
|
||||||
if(login == null)
|
if(cipher == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -397,65 +397,88 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
if(selection == AppResources.View || string.IsNullOrWhiteSpace(Uri))
|
if(selection == AppResources.View || string.IsNullOrWhiteSpace(Uri))
|
||||||
{
|
{
|
||||||
var page = new VaultViewLoginPage(login.Id);
|
var page = new VaultViewLoginPage(cipher.Id);
|
||||||
await Navigation.PushForDeviceAsync(page);
|
await Navigation.PushForDeviceAsync(page);
|
||||||
}
|
}
|
||||||
else if(selection == AppResources.Autofill)
|
else if(selection == AppResources.Autofill)
|
||||||
{
|
{
|
||||||
if(_deviceInfoService.Version < 21)
|
if(_deviceInfoService.Version < 21)
|
||||||
{
|
{
|
||||||
MoreClickedAsync(login);
|
MoreClickedAsync(cipher);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_googleAnalyticsService.TrackExtensionEvent("AutoFilled", Uri.StartsWith("http") ? "Website" : "App");
|
_googleAnalyticsService.TrackExtensionEvent("AutoFilled", Uri.StartsWith("http") ? "Website" : "App");
|
||||||
MessagingCenter.Send(Application.Current, "Autofill", login);
|
MessagingCenter.Send(Application.Current, "Autofill", cipher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
((ListView)sender).SelectedItem = null;
|
((ListView)sender).SelectedItem = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void MoreClickedAsync(VaultListPageModel.Login login)
|
private async void MoreClickedAsync(VaultListPageModel.Cipher cipher)
|
||||||
{
|
{
|
||||||
var buttons = new List<string> { AppResources.View, AppResources.Edit };
|
var buttons = new List<string> { AppResources.View, AppResources.Edit };
|
||||||
if(!string.IsNullOrWhiteSpace(login.Password.Value))
|
|
||||||
|
if(cipher.Type == Enums.CipherType.Login)
|
||||||
{
|
{
|
||||||
buttons.Add(AppResources.CopyPassword);
|
if(!string.IsNullOrWhiteSpace(cipher.Password.Value))
|
||||||
|
{
|
||||||
|
buttons.Add(AppResources.CopyPassword);
|
||||||
|
}
|
||||||
|
if(!string.IsNullOrWhiteSpace(cipher.Username))
|
||||||
|
{
|
||||||
|
buttons.Add(AppResources.CopyUsername);
|
||||||
|
}
|
||||||
|
if(!string.IsNullOrWhiteSpace(cipher.Uri.Value) && (cipher.Uri.Value.StartsWith("http://")
|
||||||
|
|| cipher.Uri.Value.StartsWith("https://")))
|
||||||
|
{
|
||||||
|
buttons.Add(AppResources.GoToWebsite);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(!string.IsNullOrWhiteSpace(login.Username))
|
else if(cipher.Type == Enums.CipherType.Card)
|
||||||
{
|
{
|
||||||
buttons.Add(AppResources.CopyUsername);
|
if(!string.IsNullOrWhiteSpace(cipher.CardNumber))
|
||||||
}
|
{
|
||||||
if(!string.IsNullOrWhiteSpace(login.Uri.Value) && (login.Uri.Value.StartsWith("http://")
|
buttons.Add(AppResources.CopyNumber);
|
||||||
|| login.Uri.Value.StartsWith("https://")))
|
}
|
||||||
{
|
if(!string.IsNullOrWhiteSpace(cipher.CardCode.Value))
|
||||||
buttons.Add(AppResources.GoToWebsite);
|
{
|
||||||
|
buttons.Add(AppResources.CopySecurityCode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var selection = await DisplayActionSheet(login.Name, AppResources.Cancel, null, buttons.ToArray());
|
var selection = await DisplayActionSheet(cipher.Name, AppResources.Cancel, null, buttons.ToArray());
|
||||||
|
|
||||||
if(selection == AppResources.View)
|
if(selection == AppResources.View)
|
||||||
{
|
{
|
||||||
var page = new VaultViewLoginPage(login.Id);
|
var page = new VaultViewLoginPage(cipher.Id);
|
||||||
await Navigation.PushForDeviceAsync(page);
|
await Navigation.PushForDeviceAsync(page);
|
||||||
}
|
}
|
||||||
else if(selection == AppResources.Edit)
|
else if(selection == AppResources.Edit)
|
||||||
{
|
{
|
||||||
var page = new VaultEditLoginPage(login.Id);
|
var page = new VaultEditLoginPage(cipher.Id);
|
||||||
await Navigation.PushForDeviceAsync(page);
|
await Navigation.PushForDeviceAsync(page);
|
||||||
}
|
}
|
||||||
else if(selection == AppResources.CopyPassword)
|
else if(selection == AppResources.CopyPassword)
|
||||||
{
|
{
|
||||||
Copy(login.Password.Value, AppResources.Password);
|
Copy(cipher.Password.Value, AppResources.Password);
|
||||||
}
|
}
|
||||||
else if(selection == AppResources.CopyUsername)
|
else if(selection == AppResources.CopyUsername)
|
||||||
{
|
{
|
||||||
Copy(login.Username, AppResources.Username);
|
Copy(cipher.Username, AppResources.Username);
|
||||||
}
|
}
|
||||||
else if(selection == AppResources.GoToWebsite)
|
else if(selection == AppResources.GoToWebsite)
|
||||||
{
|
{
|
||||||
Device.OpenUri(new Uri(login.Uri.Value));
|
Device.OpenUri(new Uri(cipher.Uri.Value));
|
||||||
|
}
|
||||||
|
else if(selection == AppResources.CopyNumber)
|
||||||
|
{
|
||||||
|
Copy(cipher.CardNumber, AppResources.Number);
|
||||||
|
}
|
||||||
|
else if(selection == AppResources.CopySecurityCode)
|
||||||
|
{
|
||||||
|
Copy(cipher.CardCode.Value, AppResources.SecurityCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,18 +488,18 @@ namespace Bit.App.Pages
|
|||||||
_userDialogs.Toast(string.Format(AppResources.ValueHasBeenCopied, alertLabel));
|
_userDialogs.Toast(string.Format(AppResources.ValueHasBeenCopied, alertLabel));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void AddLogin()
|
private async void AddCipher()
|
||||||
{
|
{
|
||||||
var page = new VaultAddLoginPage(Uri);
|
var page = new VaultAddLoginPage(Uri);
|
||||||
await Navigation.PushForDeviceAsync(page);
|
await Navigation.PushForDeviceAsync(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AddLoginToolBarItem : ExtendedToolbarItem
|
private class AddCipherToolBarItem : ExtendedToolbarItem
|
||||||
{
|
{
|
||||||
private readonly VaultListLoginsPage _page;
|
private readonly VaultListCiphersPage _page;
|
||||||
|
|
||||||
public AddLoginToolBarItem(VaultListLoginsPage page)
|
public AddCipherToolBarItem(VaultListCiphersPage page)
|
||||||
: base(() => page.AddLogin())
|
: base(() => page.AddCipher())
|
||||||
{
|
{
|
||||||
_page = page;
|
_page = page;
|
||||||
Text = AppResources.Add;
|
Text = AppResources.Add;
|
||||||
@ -486,7 +509,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private class VaultListHeaderViewCell : ExtendedViewCell
|
private class VaultListHeaderViewCell : ExtendedViewCell
|
||||||
{
|
{
|
||||||
public VaultListHeaderViewCell(VaultListLoginsPage page)
|
public VaultListHeaderViewCell(VaultListCiphersPage page)
|
||||||
{
|
{
|
||||||
var image = new CachedImage
|
var image = new CachedImage
|
||||||
{
|
{
|
36
src/App/Resources/AppResources.Designer.cs
generated
36
src/App/Resources/AppResources.Designer.cs
generated
@ -646,6 +646,15 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Copy Number.
|
||||||
|
/// </summary>
|
||||||
|
public static string CopyNumber {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("CopyNumber", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Copy Password.
|
/// Looks up a localized string similar to Copy Password.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -655,6 +664,15 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Copy Security Code.
|
||||||
|
/// </summary>
|
||||||
|
public static string CopySecurityCode {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("CopySecurityCode", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Copy TOTP.
|
/// Looks up a localized string similar to Copy TOTP.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -1816,6 +1834,15 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Number.
|
||||||
|
/// </summary>
|
||||||
|
public static string Number {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Number", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Ok.
|
/// Looks up a localized string similar to Ok.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -2077,6 +2104,15 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Security Code.
|
||||||
|
/// </summary>
|
||||||
|
public static string SecurityCode {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("SecurityCode", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to See Development Progress.
|
/// Looks up a localized string similar to See Development Progress.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1039,4 +1039,16 @@
|
|||||||
<data name="NoCustomFields" xml:space="preserve">
|
<data name="NoCustomFields" xml:space="preserve">
|
||||||
<value>No custom fields. You can fully manage custom fields from the web vault or browser extension.</value>
|
<value>No custom fields. You can fully manage custom fields from the web vault or browser extension.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="CopyNumber" xml:space="preserve">
|
||||||
|
<value>Copy Number</value>
|
||||||
|
</data>
|
||||||
|
<data name="CopySecurityCode" xml:space="preserve">
|
||||||
|
<value>Copy Security Code</value>
|
||||||
|
</data>
|
||||||
|
<data name="Number" xml:space="preserve">
|
||||||
|
<value>Number</value>
|
||||||
|
</data>
|
||||||
|
<data name="SecurityCode" xml:space="preserve">
|
||||||
|
<value>Security Code</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
Loading…
Reference in New Issue
Block a user