mirror of
https://github.com/bitwarden/mobile.git
synced 2024-12-24 16:38:22 +01:00
view/add/edit login uris
This commit is contained in:
parent
83fd19784a
commit
4c8204f29a
@ -3,7 +3,7 @@ using System.ComponentModel;
|
||||
using Xamarin.Forms;
|
||||
using System.Collections.Generic;
|
||||
using Bit.App.Enums;
|
||||
using System.Linq;
|
||||
using Bit.App.Resources;
|
||||
|
||||
namespace Bit.App.Models.Page
|
||||
{
|
||||
@ -12,6 +12,7 @@ namespace Bit.App.Models.Page
|
||||
private string _name, _notes;
|
||||
private List<Attachment> _attachments;
|
||||
private List<Field> _fields;
|
||||
private List<LoginUri> _loginUris;
|
||||
|
||||
// Login
|
||||
private string _loginUsername, _loginPassword, _loginUri, _loginTotpCode;
|
||||
@ -117,69 +118,17 @@ namespace Bit.App.Models.Page
|
||||
public ImageSource LoginShowHideImage => RevealLoginPassword ?
|
||||
ImageSource.FromFile("eye_slash.png") : ImageSource.FromFile("eye.png");
|
||||
|
||||
public string LoginUri
|
||||
public List<LoginUri> LoginUris
|
||||
{
|
||||
get => _loginUri;
|
||||
get => _loginUris;
|
||||
set
|
||||
{
|
||||
_loginUri = value;
|
||||
PropertyChanged(this, new PropertyChangedEventArgs(nameof(LoginUri)));
|
||||
PropertyChanged(this, new PropertyChangedEventArgs(nameof(LoginUriHost)));
|
||||
PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowLoginUri)));
|
||||
PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowLoginLaunch)));
|
||||
}
|
||||
}
|
||||
public bool ShowLoginUri => !string.IsNullOrWhiteSpace(LoginUri);
|
||||
public bool ShowLoginLaunch
|
||||
{
|
||||
get
|
||||
{
|
||||
if(!ShowLoginUri)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(Device.RuntimePlatform == Device.Android && !LoginUri.StartsWith("http") &&
|
||||
!LoginUri.StartsWith("androidapp://"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(Device.RuntimePlatform != Device.Android && !LoginUri.StartsWith("http"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!Uri.TryCreate(LoginUri, UriKind.Absolute, out Uri uri))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public string LoginUriHost
|
||||
{
|
||||
get
|
||||
{
|
||||
if(!ShowLoginUri)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if(!Uri.TryCreate(LoginUri, UriKind.Absolute, out Uri uri))
|
||||
{
|
||||
return LoginUri;
|
||||
}
|
||||
|
||||
if(DomainName.TryParseBaseDomain(uri.Host, out string domain))
|
||||
{
|
||||
return domain;
|
||||
}
|
||||
|
||||
return uri.Host;
|
||||
_loginUris = value;
|
||||
PropertyChanged(this, new PropertyChangedEventArgs(nameof(LoginUris)));
|
||||
PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowLoginUris)));
|
||||
}
|
||||
}
|
||||
public bool ShowLoginUris => (LoginUris?.Count ?? 0) > 0;
|
||||
|
||||
public string LoginTotpCode
|
||||
{
|
||||
@ -601,7 +550,23 @@ namespace Bit.App.Models.Page
|
||||
case CipherType.Login:
|
||||
LoginUsername = cipher.Login.Username?.Decrypt(cipher.OrganizationId);
|
||||
LoginPassword = cipher.Login.Password?.Decrypt(cipher.OrganizationId);
|
||||
LoginUri = cipher.Login.Uris?.FirstOrDefault()?.Uri?.Decrypt(cipher.OrganizationId);
|
||||
|
||||
if(cipher.Login.Uris != null)
|
||||
{
|
||||
var uris = new List<LoginUri>();
|
||||
foreach(var uri in cipher.Login.Uris)
|
||||
{
|
||||
uris.Add(new LoginUri
|
||||
{
|
||||
Value = uri.Uri?.Decrypt(cipher.OrganizationId)
|
||||
});
|
||||
}
|
||||
LoginUris = uris;
|
||||
}
|
||||
else
|
||||
{
|
||||
LoginUris = null;
|
||||
}
|
||||
break;
|
||||
case CipherType.Card:
|
||||
CardName = cipher.Card.CardholderName?.Decrypt(cipher.OrganizationId);
|
||||
@ -666,5 +631,62 @@ namespace Bit.App.Models.Page
|
||||
public FieldType Type { get; set; }
|
||||
public bool Revealed { get; set; }
|
||||
}
|
||||
|
||||
public class LoginUri
|
||||
{
|
||||
public string Value { get; set; }
|
||||
public bool ShowLaunch
|
||||
{
|
||||
get
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(Value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(Device.RuntimePlatform == Device.Android && !IsWebsite && !IsApp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(Device.RuntimePlatform != Device.Android && !IsWebsite)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!Uri.TryCreate(Value, UriKind.Absolute, out Uri uri))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public string Host
|
||||
{
|
||||
get
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(Value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if(!Uri.TryCreate(Value, UriKind.Absolute, out Uri uri))
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
if(DomainName.TryParseBaseDomain(uri.Host, out string domain))
|
||||
{
|
||||
return domain;
|
||||
}
|
||||
|
||||
return uri.Host;
|
||||
}
|
||||
}
|
||||
public string Label => IsWebsite ? AppResources.Website : AppResources.URI;
|
||||
public bool IsWebsite => Value.StartsWith("http://") || Value.StartsWith("https://");
|
||||
public bool IsApp => Value.StartsWith(Constants.AndroidAppProtocol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,6 +85,7 @@ namespace Bit.App.Pages
|
||||
public List<Folder> Folders { get; set; }
|
||||
public TableRoot TableRoot { get; set; }
|
||||
public TableSection TopSection { get; set; }
|
||||
public TableSection UrisSection { get; set; }
|
||||
public TableSection MiddleSection { get; set; }
|
||||
public TableSection FieldsSection { get; set; }
|
||||
public ExtendedTableView Table { get; set; }
|
||||
@ -94,11 +95,11 @@ namespace Bit.App.Pages
|
||||
public FormPickerCell FolderCell { get; private set; }
|
||||
public ExtendedSwitchCell FavoriteCell { get; set; }
|
||||
public ExtendedTextCell AddFieldCell { get; private set; }
|
||||
public ExtendedTextCell AddUriCell { get; private set; }
|
||||
|
||||
// Login
|
||||
public FormEntryCell LoginPasswordCell { get; private set; }
|
||||
public FormEntryCell LoginUsernameCell { get; private set; }
|
||||
public FormEntryCell LoginUriCell { get; private set; }
|
||||
public FormEntryCell LoginTotpCell { get; private set; }
|
||||
public ExtendedTextCell LoginGenerateCell { get; private set; }
|
||||
|
||||
@ -190,13 +191,16 @@ namespace Bit.App.Pages
|
||||
{
|
||||
AddFieldCell.Tapped += AddFieldCell_Tapped;
|
||||
}
|
||||
if(AddUriCell != null)
|
||||
{
|
||||
AddUriCell.Tapped += AddUriCell_Tapped;
|
||||
}
|
||||
|
||||
switch(_type)
|
||||
{
|
||||
case CipherType.Login:
|
||||
LoginPasswordCell.InitEvents();
|
||||
LoginUsernameCell.InitEvents();
|
||||
LoginUriCell.InitEvents();
|
||||
LoginTotpCell.InitEvents();
|
||||
LoginPasswordCell.Button.Clicked += PasswordButton_Clicked;
|
||||
LoginGenerateCell.Tapped += GenerateCell_Tapped;
|
||||
@ -237,6 +241,9 @@ namespace Bit.App.Pages
|
||||
break;
|
||||
}
|
||||
|
||||
Helpers.InitSectionEvents(FieldsSection);
|
||||
Helpers.InitSectionEvents(UrisSection);
|
||||
|
||||
if(_type == CipherType.Login && !_fromAutofill && !_settings.GetValueOrDefault(AddedLoginAlertKey, false))
|
||||
{
|
||||
_settings.AddOrUpdateValue(AddedLoginAlertKey, true);
|
||||
@ -267,6 +274,10 @@ namespace Bit.App.Pages
|
||||
{
|
||||
AddFieldCell.Tapped -= AddFieldCell_Tapped;
|
||||
}
|
||||
if(AddUriCell != null)
|
||||
{
|
||||
AddUriCell.Tapped -= AddUriCell_Tapped;
|
||||
}
|
||||
|
||||
switch(_type)
|
||||
{
|
||||
@ -274,7 +285,6 @@ namespace Bit.App.Pages
|
||||
LoginTotpCell.Dispose();
|
||||
LoginPasswordCell.Dispose();
|
||||
LoginUsernameCell.Dispose();
|
||||
LoginUriCell.Dispose();
|
||||
LoginPasswordCell.Button.Clicked -= PasswordButton_Clicked;
|
||||
LoginGenerateCell.Tapped -= GenerateCell_Tapped;
|
||||
if(LoginTotpCell?.Button != null)
|
||||
@ -314,16 +324,8 @@ namespace Bit.App.Pages
|
||||
break;
|
||||
}
|
||||
|
||||
if(FieldsSection != null && FieldsSection.Count > 0)
|
||||
{
|
||||
foreach(var cell in FieldsSection)
|
||||
{
|
||||
if(cell is FormEntryCell entrycell)
|
||||
{
|
||||
entrycell.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
Helpers.DisposeSectionEvents(FieldsSection);
|
||||
Helpers.DisposeSectionEvents(UrisSection);
|
||||
}
|
||||
|
||||
protected override bool OnBackButtonPressed()
|
||||
@ -398,8 +400,7 @@ namespace Bit.App.Pages
|
||||
|
||||
if(_type == CipherType.Login)
|
||||
{
|
||||
LoginTotpCell = new FormEntryCell(AppResources.AuthenticatorKey, nextElement: NotesCell.Editor,
|
||||
useButton: _deviceInfo.HasCamera);
|
||||
LoginTotpCell = new FormEntryCell(AppResources.AuthenticatorKey, useButton: _deviceInfo.HasCamera);
|
||||
if(_deviceInfo.HasCamera)
|
||||
{
|
||||
LoginTotpCell.Button.Image = "camera.png";
|
||||
@ -435,20 +436,23 @@ namespace Bit.App.Pages
|
||||
LoginUsernameCell.Entry.Text = _defaultUsername;
|
||||
}
|
||||
|
||||
LoginUriCell = new FormEntryCell(AppResources.URI, Keyboard.Url, nextElement: LoginUsernameCell.Entry);
|
||||
if(!string.IsNullOrWhiteSpace(_defaultUri))
|
||||
{
|
||||
LoginUriCell.Entry.Text = _defaultUri;
|
||||
}
|
||||
|
||||
NameCell.NextElement = LoginUriCell.Entry;
|
||||
NameCell.NextElement = LoginUsernameCell.Entry;
|
||||
|
||||
// Build sections
|
||||
TopSection.Add(LoginUriCell);
|
||||
TopSection.Add(LoginUsernameCell);
|
||||
TopSection.Add(LoginPasswordCell);
|
||||
TopSection.Add(LoginGenerateCell);
|
||||
MiddleSection.Insert(0, LoginTotpCell);
|
||||
TopSection.Add(LoginTotpCell);
|
||||
|
||||
// Uris
|
||||
UrisSection = new TableSection(Helpers.GetEmptyTableSectionTitle());
|
||||
AddUriCell = new ExtendedTextCell
|
||||
{
|
||||
Text = AppResources.NewUri,
|
||||
TextColor = Colors.Primary
|
||||
};
|
||||
UrisSection.Add(AddUriCell);
|
||||
UrisSection.Insert(0, Helpers.MakeUriCell(string.Empty, UrisSection));
|
||||
}
|
||||
else if(_type == CipherType.Card)
|
||||
{
|
||||
@ -583,6 +587,11 @@ namespace Bit.App.Pages
|
||||
FieldsSection
|
||||
};
|
||||
|
||||
if(UrisSection != null)
|
||||
{
|
||||
TableRoot.Insert(1, UrisSection);
|
||||
}
|
||||
|
||||
Table = new ExtendedTableView
|
||||
{
|
||||
Intent = TableIntent.Settings,
|
||||
@ -638,8 +647,6 @@ namespace Bit.App.Pages
|
||||
case CipherType.Login:
|
||||
cipher.Login = new Login
|
||||
{
|
||||
Uri = string.IsNullOrWhiteSpace(LoginUriCell.Entry.Text) ? null :
|
||||
LoginUriCell.Entry.Text.Encrypt(),
|
||||
Username = string.IsNullOrWhiteSpace(LoginUsernameCell.Entry.Text) ? null :
|
||||
LoginUsernameCell.Entry.Text.Encrypt(),
|
||||
Password = string.IsNullOrWhiteSpace(LoginPasswordCell.Entry.Text) ? null :
|
||||
@ -647,6 +654,8 @@ namespace Bit.App.Pages
|
||||
Totp = string.IsNullOrWhiteSpace(LoginTotpCell.Entry.Text) ? null :
|
||||
LoginTotpCell.Entry.Text.Encrypt(),
|
||||
};
|
||||
|
||||
Helpers.ProcessUrisSectionForSave(UrisSection, cipher);
|
||||
break;
|
||||
case CipherType.SecureNote:
|
||||
cipher.SecureNote = new SecureNote
|
||||
@ -821,5 +830,15 @@ namespace Bit.App.Pages
|
||||
{
|
||||
await Helpers.AddField(this, FieldsSection);
|
||||
}
|
||||
|
||||
private void AddUriCell_Tapped(object sender, EventArgs e)
|
||||
{
|
||||
var cell = Helpers.MakeUriCell(string.Empty, UrisSection);
|
||||
if(cell != null)
|
||||
{
|
||||
UrisSection.Insert(UrisSection.Count - 1, cell);
|
||||
cell.InitEvents();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ namespace Bit.App.Pages
|
||||
public List<Folder> Folders { get; set; }
|
||||
public TableRoot TableRoot { get; set; }
|
||||
public TableSection TopSection { get; set; }
|
||||
public TableSection UrisSection { get; set; }
|
||||
public TableSection MiddleSection { get; set; }
|
||||
public TableSection FieldsSection { get; set; }
|
||||
public ExtendedTableView Table { get; set; }
|
||||
@ -52,11 +53,11 @@ namespace Bit.App.Pages
|
||||
public ExtendedTextCell AttachmentsCell { get; private set; }
|
||||
public ExtendedTextCell DeleteCell { get; private set; }
|
||||
public ExtendedTextCell AddFieldCell { get; private set; }
|
||||
public ExtendedTextCell AddUriCell { get; private set; }
|
||||
|
||||
// Login
|
||||
public FormEntryCell LoginPasswordCell { get; private set; }
|
||||
public FormEntryCell LoginUsernameCell { get; private set; }
|
||||
public FormEntryCell LoginUriCell { get; private set; }
|
||||
public FormEntryCell LoginTotpCell { get; private set; }
|
||||
public ExtendedTextCell LoginGenerateCell { get; private set; }
|
||||
|
||||
@ -169,8 +170,7 @@ namespace Bit.App.Pages
|
||||
// Types
|
||||
if(Cipher.Type == CipherType.Login)
|
||||
{
|
||||
LoginTotpCell = new FormEntryCell(AppResources.AuthenticatorKey, nextElement: NotesCell.Editor,
|
||||
useButton: _deviceInfo.HasCamera);
|
||||
LoginTotpCell = new FormEntryCell(AppResources.AuthenticatorKey, useButton: _deviceInfo.HasCamera);
|
||||
if(_deviceInfo.HasCamera)
|
||||
{
|
||||
LoginTotpCell.Button.Image = "camera.png";
|
||||
@ -201,18 +201,31 @@ namespace Bit.App.Pages
|
||||
LoginUsernameCell.Entry.DisableAutocapitalize = true;
|
||||
LoginUsernameCell.Entry.Autocorrect = false;
|
||||
|
||||
LoginUriCell = new FormEntryCell(AppResources.URI, Keyboard.Url, nextElement: LoginUsernameCell.Entry);
|
||||
LoginUriCell.Entry.Text = Cipher.Login?.Uri?.Decrypt(Cipher.OrganizationId);
|
||||
|
||||
// Name
|
||||
NameCell.NextElement = LoginUriCell.Entry;
|
||||
NameCell.NextElement = LoginUsernameCell.Entry;
|
||||
|
||||
// Build sections
|
||||
TopSection.Add(LoginUriCell);
|
||||
TopSection.Add(LoginUsernameCell);
|
||||
TopSection.Add(LoginPasswordCell);
|
||||
TopSection.Add(LoginGenerateCell);
|
||||
MiddleSection.Insert(0, LoginTotpCell);
|
||||
TopSection.Add(LoginTotpCell);
|
||||
|
||||
// Uris
|
||||
UrisSection = new TableSection(Helpers.GetEmptyTableSectionTitle());
|
||||
AddUriCell = new ExtendedTextCell
|
||||
{
|
||||
Text = AppResources.NewUri,
|
||||
TextColor = Colors.Primary
|
||||
};
|
||||
UrisSection.Add(AddUriCell);
|
||||
if(Cipher.Login?.Uris != null)
|
||||
{
|
||||
foreach(var uri in Cipher.Login.Uris)
|
||||
{
|
||||
var value = uri.Uri?.Decrypt(Cipher.OrganizationId);
|
||||
UrisSection.Insert(UrisSection.Count - 1, Helpers.MakeUriCell(value, UrisSection));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(Cipher.Type == CipherType.Card)
|
||||
{
|
||||
@ -436,6 +449,11 @@ namespace Bit.App.Pages
|
||||
}
|
||||
};
|
||||
|
||||
if(UrisSection != null)
|
||||
{
|
||||
TableRoot.Insert(1, UrisSection);
|
||||
}
|
||||
|
||||
Table = new ExtendedTableView
|
||||
{
|
||||
Intent = TableIntent.Settings,
|
||||
@ -488,8 +506,6 @@ namespace Bit.App.Pages
|
||||
case CipherType.Login:
|
||||
Cipher.Login = new Login
|
||||
{
|
||||
Uri = string.IsNullOrWhiteSpace(LoginUriCell.Entry.Text) ? null :
|
||||
LoginUriCell.Entry.Text.Encrypt(Cipher.OrganizationId),
|
||||
Username = string.IsNullOrWhiteSpace(LoginUsernameCell.Entry.Text) ? null :
|
||||
LoginUsernameCell.Entry.Text.Encrypt(Cipher.OrganizationId),
|
||||
Password = string.IsNullOrWhiteSpace(LoginPasswordCell.Entry.Text) ? null :
|
||||
@ -497,6 +513,8 @@ namespace Bit.App.Pages
|
||||
Totp = string.IsNullOrWhiteSpace(LoginTotpCell.Entry.Text) ? null :
|
||||
LoginTotpCell.Entry.Text.Encrypt(Cipher.OrganizationId),
|
||||
};
|
||||
|
||||
Helpers.ProcessUrisSectionForSave(UrisSection, Cipher);
|
||||
break;
|
||||
case CipherType.SecureNote:
|
||||
Cipher.SecureNote = new SecureNote
|
||||
@ -679,13 +697,16 @@ namespace Bit.App.Pages
|
||||
{
|
||||
AddFieldCell.Tapped += AddFieldCell_Tapped;
|
||||
}
|
||||
if(AddUriCell != null)
|
||||
{
|
||||
AddUriCell.Tapped += AddUriCell_Tapped;
|
||||
}
|
||||
|
||||
switch(Cipher.Type)
|
||||
{
|
||||
case CipherType.Login:
|
||||
LoginPasswordCell?.InitEvents();
|
||||
LoginUsernameCell?.InitEvents();
|
||||
LoginUriCell?.InitEvents();
|
||||
LoginTotpCell?.InitEvents();
|
||||
if(LoginPasswordCell?.Button != null)
|
||||
{
|
||||
@ -732,16 +753,8 @@ namespace Bit.App.Pages
|
||||
break;
|
||||
}
|
||||
|
||||
if(FieldsSection != null && FieldsSection.Count > 0)
|
||||
{
|
||||
foreach(var cell in FieldsSection)
|
||||
{
|
||||
if(cell is FormEntryCell entrycell)
|
||||
{
|
||||
entrycell.InitEvents();
|
||||
}
|
||||
}
|
||||
}
|
||||
Helpers.InitSectionEvents(FieldsSection);
|
||||
Helpers.InitSectionEvents(UrisSection);
|
||||
}
|
||||
|
||||
protected override void OnDisappearing()
|
||||
@ -764,6 +777,10 @@ namespace Bit.App.Pages
|
||||
{
|
||||
AddFieldCell.Tapped -= AddFieldCell_Tapped;
|
||||
}
|
||||
if(AddUriCell != null)
|
||||
{
|
||||
AddUriCell.Tapped -= AddUriCell_Tapped;
|
||||
}
|
||||
|
||||
switch(Cipher.Type)
|
||||
{
|
||||
@ -771,7 +788,6 @@ namespace Bit.App.Pages
|
||||
LoginTotpCell?.Dispose();
|
||||
LoginPasswordCell?.Dispose();
|
||||
LoginUsernameCell?.Dispose();
|
||||
LoginUriCell?.Dispose();
|
||||
if(LoginPasswordCell?.Button != null)
|
||||
{
|
||||
LoginPasswordCell.Button.Clicked -= PasswordButton_Clicked;
|
||||
@ -817,16 +833,8 @@ namespace Bit.App.Pages
|
||||
break;
|
||||
}
|
||||
|
||||
if(FieldsSection != null && FieldsSection.Count > 0)
|
||||
{
|
||||
foreach(var cell in FieldsSection)
|
||||
{
|
||||
if(cell is FormEntryCell entrycell)
|
||||
{
|
||||
entrycell.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
Helpers.DisposeSectionEvents(FieldsSection);
|
||||
Helpers.DisposeSectionEvents(UrisSection);
|
||||
}
|
||||
|
||||
private void PasswordButton_Clicked(object sender, EventArgs e)
|
||||
@ -920,6 +928,16 @@ namespace Bit.App.Pages
|
||||
await Helpers.AddField(this, FieldsSection);
|
||||
}
|
||||
|
||||
private void AddUriCell_Tapped(object sender, EventArgs e)
|
||||
{
|
||||
var cell = Helpers.MakeUriCell(string.Empty, UrisSection);
|
||||
if(cell != null)
|
||||
{
|
||||
UrisSection.Insert(UrisSection.Count - 1, cell);
|
||||
cell.InitEvents();
|
||||
}
|
||||
}
|
||||
|
||||
private void AlertNoConnection()
|
||||
{
|
||||
DisplayAlert(AppResources.InternetConnectionRequiredTitle, AppResources.InternetConnectionRequiredMessage,
|
||||
|
@ -39,6 +39,7 @@ namespace Bit.App.Pages
|
||||
private VaultViewCipherPageModel Model { get; set; } = new VaultViewCipherPageModel();
|
||||
private ExtendedTableView Table { get; set; }
|
||||
private TableSection ItemInformationSection { get; set; }
|
||||
public TableSection UrisSection { get; set; }
|
||||
private TableSection NotesSection { get; set; }
|
||||
private TableSection AttachmentsSection { get; set; }
|
||||
private TableSection FieldsSection { get; set; }
|
||||
@ -50,7 +51,6 @@ namespace Bit.App.Pages
|
||||
// Login
|
||||
public LabeledValueCell LoginUsernameCell { get; set; }
|
||||
public LabeledValueCell LoginPasswordCell { get; set; }
|
||||
public LabeledValueCell LoginUriCell { get; set; }
|
||||
public LabeledValueCell LoginTotpCodeCell { get; set; }
|
||||
|
||||
// Card
|
||||
@ -141,22 +141,6 @@ namespace Bit.App.Pages
|
||||
Helpers.OnPlatform(iOS: "Menlo-Regular", Android: "monospace", Windows: "Courier");
|
||||
LoginPasswordCell.Value.LineBreakMode = LineBreakMode.WordWrap;
|
||||
|
||||
// URI
|
||||
LoginUriCell = new LabeledValueCell(AppResources.Website, button1Image: "launch.png");
|
||||
LoginUriCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.LoginUriHost));
|
||||
LoginUriCell.Button1.SetBinding(IsVisibleProperty, nameof(VaultViewCipherPageModel.ShowLoginLaunch));
|
||||
LoginUriCell.Button1.Command = new Command(async () =>
|
||||
{
|
||||
if(Device.RuntimePlatform == Device.Android && Model.LoginUri.StartsWith("androidapp://"))
|
||||
{
|
||||
await _deviceActionService.LaunchAppAsync(Model.LoginUri, this);
|
||||
}
|
||||
else if(Model.LoginUri.StartsWith("http://") || Model.LoginUri.StartsWith("https://"))
|
||||
{
|
||||
Device.OpenUri(new Uri(Model.LoginUri));
|
||||
}
|
||||
});
|
||||
|
||||
// Totp
|
||||
LoginTotpCodeCell = new LabeledValueCell(
|
||||
AppResources.VerificationCodeTotp, button1Image: "clipboard.png", subText: "--");
|
||||
@ -299,6 +283,22 @@ namespace Bit.App.Pages
|
||||
|
||||
private void BuildTable(Cipher cipher)
|
||||
{
|
||||
// URIs
|
||||
if(UrisSection != null && Table.Root.Contains(UrisSection))
|
||||
{
|
||||
Table.Root.Remove(UrisSection);
|
||||
}
|
||||
if(Model.ShowLoginUris)
|
||||
{
|
||||
UrisSection = new TableSection(Helpers.GetEmptyTableSectionTitle());
|
||||
foreach(var uri in Model.LoginUris)
|
||||
{
|
||||
UrisSection.Add(new UriViewCell(this, uri));
|
||||
}
|
||||
Table.Root.Add(UrisSection);
|
||||
}
|
||||
|
||||
// Notes
|
||||
if(Table.Root.Contains(NotesSection))
|
||||
{
|
||||
Table.Root.Remove(NotesSection);
|
||||
@ -365,7 +365,6 @@ namespace Bit.App.Pages
|
||||
switch(cipher.Type)
|
||||
{
|
||||
case CipherType.Login:
|
||||
AddSectionCell(LoginUriCell, Model.ShowLoginUri);
|
||||
AddSectionCell(LoginUsernameCell, Model.ShowLoginUsername);
|
||||
AddSectionCell(LoginPasswordCell, Model.ShowLoginPassword);
|
||||
|
||||
@ -575,8 +574,7 @@ namespace Bit.App.Pages
|
||||
public FieldViewCell(VaultViewCipherPage page, VaultViewCipherPageModel.Field field, bool? a, bool? b)
|
||||
: base(field.Name, field.MaskedValue, string.Empty, "clipboard.png")
|
||||
{
|
||||
Value.FontFamily = Helpers.OnPlatform(iOS: "Menlo-Regular",
|
||||
Android: "monospace", Windows: "Courier");
|
||||
Value.FontFamily = Helpers.OnPlatform(iOS: "Menlo-Regular", Android: "monospace", Windows: "Courier");
|
||||
if(Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
Button1.Margin = new Thickness(10, 0);
|
||||
@ -610,5 +608,29 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class UriViewCell : LabeledValueCell
|
||||
{
|
||||
public UriViewCell(VaultViewCipherPage page, VaultViewCipherPageModel.LoginUri uri)
|
||||
: base(uri.Label, uri.Host, uri.ShowLaunch ? "launch.png" : null, "clipboard.png")
|
||||
{
|
||||
Value.LineBreakMode = LineBreakMode.TailTruncation;
|
||||
if(Button1 != null)
|
||||
{
|
||||
Button1.Command = new Command(async () =>
|
||||
{
|
||||
if(Device.RuntimePlatform == Device.Android && uri.IsApp)
|
||||
{
|
||||
await page._deviceActionService.LaunchAppAsync(uri.Value, page);
|
||||
}
|
||||
else if(uri.IsWebsite)
|
||||
{
|
||||
Device.OpenUri(new Uri(uri.Value));
|
||||
}
|
||||
});
|
||||
}
|
||||
Button2.Command = new Command(() => page.Copy(uri.Value, AppResources.URI));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
18
src/App/Resources/AppResources.Designer.cs
generated
18
src/App/Resources/AppResources.Designer.cs
generated
@ -2130,6 +2130,15 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to New URI.
|
||||
/// </summary>
|
||||
public static string NewUri {
|
||||
get {
|
||||
return ResourceManager.GetString("NewUri", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to No.
|
||||
/// </summary>
|
||||
@ -2940,6 +2949,15 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to URI {0}.
|
||||
/// </summary>
|
||||
public static string URIPosition {
|
||||
get {
|
||||
return ResourceManager.GetString("URIPosition", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Use another two-step login method.
|
||||
/// </summary>
|
||||
|
@ -1249,4 +1249,11 @@
|
||||
<data name="Remove" xml:space="preserve">
|
||||
<value>Remove</value>
|
||||
</data>
|
||||
<data name="NewUri" xml:space="preserve">
|
||||
<value>New URI</value>
|
||||
</data>
|
||||
<data name="URIPosition" xml:space="preserve">
|
||||
<value>URI {0}</value>
|
||||
<comment>Label for a uri/url with position. i.e. URI 1, URI 2, etc</comment>
|
||||
</data>
|
||||
</root>
|
@ -373,5 +373,99 @@ namespace Bit.App.Utilities
|
||||
cipher.Fields = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static FormEntryCell MakeUriCell(string value, TableSection urisSection)
|
||||
{
|
||||
var label = string.Format(AppResources.URIPosition, urisSection.Count);
|
||||
var cell = new FormEntryCell(label, entryKeyboard: Keyboard.Url);
|
||||
cell.Entry.Text = value;
|
||||
cell.Entry.DisableAutocapitalize = true;
|
||||
cell.Entry.Autocorrect = false;
|
||||
|
||||
var deleteAction = new MenuItem { Text = AppResources.Remove, IsDestructive = true };
|
||||
deleteAction.Clicked += (sender, e) =>
|
||||
{
|
||||
if(urisSection.Contains(cell))
|
||||
{
|
||||
urisSection.Remove(cell);
|
||||
if(cell is FormEntryCell feCell)
|
||||
{
|
||||
feCell.Dispose();
|
||||
}
|
||||
cell = null;
|
||||
|
||||
for(int i = 0; i < urisSection.Count; i++)
|
||||
{
|
||||
if(urisSection[i] is FormEntryCell uriCell)
|
||||
{
|
||||
uriCell.Label.Text = string.Format(AppResources.URIPosition, i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var optionsAction = new MenuItem { Text = AppResources.Options };
|
||||
optionsAction.Clicked += async (sender, e) =>
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
cell.ContextActions.Add(optionsAction);
|
||||
cell.ContextActions.Add(deleteAction);
|
||||
return cell;
|
||||
}
|
||||
|
||||
public static void ProcessUrisSectionForSave(TableSection urisSection, Cipher cipher)
|
||||
{
|
||||
if(urisSection != null && urisSection.Count > 0)
|
||||
{
|
||||
var uris = new List<LoginUri>();
|
||||
foreach(var cell in urisSection)
|
||||
{
|
||||
if(cell is FormEntryCell entryCell && !string.IsNullOrWhiteSpace(entryCell.Entry.Text))
|
||||
{
|
||||
uris.Add(new LoginUri
|
||||
{
|
||||
Uri = entryCell.Entry.Text.Encrypt(cipher.OrganizationId),
|
||||
Match = null
|
||||
});
|
||||
}
|
||||
}
|
||||
cipher.Login.Uris = uris;
|
||||
}
|
||||
|
||||
if(!cipher.Login.Uris?.Any() ?? true)
|
||||
{
|
||||
cipher.Login.Uris = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void InitSectionEvents(TableSection section)
|
||||
{
|
||||
if(section != null && section.Count > 0)
|
||||
{
|
||||
foreach(var cell in section)
|
||||
{
|
||||
if(cell is FormEntryCell entrycell)
|
||||
{
|
||||
entrycell.InitEvents();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void DisposeSectionEvents(TableSection section)
|
||||
{
|
||||
if(section != null && section.Count > 0)
|
||||
{
|
||||
foreach(var cell in section)
|
||||
{
|
||||
if(cell is FormEntryCell entrycell)
|
||||
{
|
||||
entrycell.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user