diff --git a/src/App/App.csproj b/src/App/App.csproj
index 43411eb3f..639e73df4 100644
--- a/src/App/App.csproj
+++ b/src/App/App.csproj
@@ -67,6 +67,7 @@
+
diff --git a/src/App/Controls/AddCipherToolbarItem.cs b/src/App/Controls/AddCipherToolbarItem.cs
new file mode 100644
index 000000000..e70137a30
--- /dev/null
+++ b/src/App/Controls/AddCipherToolbarItem.cs
@@ -0,0 +1,16 @@
+using Bit.App.Resources;
+using Bit.App.Utilities;
+using Xamarin.Forms;
+
+namespace Bit.App.Controls
+{
+ public class AddCipherToolbarItem : ExtendedToolbarItem
+ {
+ public AddCipherToolbarItem(Page page, string folderId)
+ : base(() => Helpers.AddCipher(page, folderId))
+ {
+ Text = AppResources.Add;
+ Icon = "plus.png";
+ }
+ }
+}
diff --git a/src/App/Pages/Vault/VaultAddCipherPage.cs b/src/App/Pages/Vault/VaultAddCipherPage.cs
index 5c0be8e23..d72ceb44f 100644
--- a/src/App/Pages/Vault/VaultAddCipherPage.cs
+++ b/src/App/Pages/Vault/VaultAddCipherPage.cs
@@ -30,6 +30,7 @@ namespace Bit.App.Pages
private readonly IAppInfoService _appInfoService;
private readonly IDeviceInfoService _deviceInfo;
private readonly IDeviceActionService _deviceActionService;
+ private readonly string _defaultFolderId;
private readonly string _defaultUri;
private readonly string _defaultName;
private readonly string _defaultUsername;
@@ -60,9 +61,10 @@ namespace Bit.App.Pages
Init();
}
- public VaultAddCipherPage(CipherType type, string defaultUri = null,
- string defaultName = null, bool fromAutofill = false, bool doInit = true)
+ public VaultAddCipherPage(CipherType type, string defaultUri = null, string defaultName = null,
+ bool fromAutofill = false, bool doInit = true, string defaultFolderId = null)
{
+ _defaultFolderId = defaultFolderId;
_type = type;
_defaultUri = defaultUri;
_defaultName = defaultName;
@@ -146,11 +148,19 @@ namespace Bit.App.Pages
var folderOptions = new List { AppResources.FolderNone };
Folders = _folderService.GetAllAsync().GetAwaiter().GetResult()
.OrderBy(f => f.Name?.Decrypt()).ToList();
+ var selectedIndex = 0;
+ var i = 1;
foreach(var folder in Folders)
{
+ if(folder.Id == _defaultFolderId)
+ {
+ selectedIndex = i;
+ }
folderOptions.Add(folder.Name.Decrypt());
+ i++;
}
FolderCell = new FormPickerCell(AppResources.Folder, folderOptions.ToArray());
+ FolderCell.Picker.SelectedIndex = selectedIndex;
// Favorite
FavoriteCell = new ExtendedSwitchCell { Text = AppResources.Favorite };
diff --git a/src/App/Pages/Vault/VaultListGroupingsPage.cs b/src/App/Pages/Vault/VaultListGroupingsPage.cs
index e6d7837e2..5af457308 100644
--- a/src/App/Pages/Vault/VaultListGroupingsPage.cs
+++ b/src/App/Pages/Vault/VaultListGroupingsPage.cs
@@ -12,7 +12,6 @@ using Plugin.Settings.Abstractions;
using Plugin.Connectivity.Abstractions;
using System.Collections.Generic;
using System.Threading;
-using Bit.App.Enums;
using static Bit.App.Models.Page.VaultListPageModel;
namespace Bit.App.Pages
@@ -57,13 +56,13 @@ namespace Bit.App.Pages
public ListView ListView { get; set; }
public StackLayout NoDataStackLayout { get; set; }
public ActivityIndicator LoadingIndicator { get; set; }
- private AddCipherToolBarItem AddCipherItem { get; set; }
+ private AddCipherToolbarItem AddCipherItem { get; set; }
private SearchToolBarItem SearchItem { get; set; }
private void Init()
{
SearchItem = new SearchToolBarItem(this);
- AddCipherItem = new AddCipherToolBarItem(this);
+ AddCipherItem = new AddCipherToolbarItem(this, null);
ToolbarItems.Add(SearchItem);
ToolbarItems.Add(AddCipherItem);
@@ -102,7 +101,7 @@ namespace Bit.App.Pages
var addCipherButton = new ExtendedButton
{
Text = AppResources.AddAnItem,
- Command = new Command(() => AddCipher()),
+ Command = new Command(() => Helpers.AddCipher(this, null)),
Style = (Style)Application.Current.Resources["btn-primaryAccent"]
};
@@ -135,6 +134,37 @@ namespace Bit.App.Pages
SearchItem?.InitEvents();
_filterResultsCancellationTokenSource = FetchAndLoadVault();
+
+ if(_connectivity.IsConnected && Device.RuntimePlatform == Device.iOS)
+ {
+ var pushPromptShow = _settings.GetValueOrDefault(Constants.PushInitialPromptShown, false);
+ Action registerAction = () =>
+ {
+ var lastPushRegistration =
+ _settings.GetValueOrDefault(Constants.PushLastRegistrationDate, DateTime.MinValue);
+ if(!pushPromptShow || DateTime.UtcNow - lastPushRegistration > TimeSpan.FromDays(1))
+ {
+ _pushNotification.Register();
+ }
+ };
+
+ if(!pushPromptShow)
+ {
+ _settings.AddOrUpdateValue(Constants.PushInitialPromptShown, true);
+ _userDialogs.Alert(new AlertConfig
+ {
+ Message = AppResources.PushNotificationAlert,
+ Title = AppResources.EnableAutomaticSyncing,
+ OnAction = registerAction,
+ OkText = AppResources.OkGotIt
+ });
+ }
+ else
+ {
+ // Check push registration once per day
+ registerAction();
+ }
+ }
}
protected override void OnDisappearing()
@@ -236,49 +266,12 @@ namespace Bit.App.Pages
((ListView)sender).SelectedItem = null;
}
- private async void AddCipher()
- {
- var type = await _userDialogs.ActionSheetAsync(AppResources.SelectTypeAdd, AppResources.Cancel, null, null,
- AppResources.TypeLogin, AppResources.TypeCard, AppResources.TypeIdentity, AppResources.TypeSecureNote);
-
- var selectedType = CipherType.SecureNote;
- if(type == AppResources.Cancel)
- {
- return;
- }
- else if(type == AppResources.TypeLogin)
- {
- selectedType = CipherType.Login;
- }
- else if(type == AppResources.TypeCard)
- {
- selectedType = CipherType.Card;
- }
- else if(type == AppResources.TypeIdentity)
- {
- selectedType = CipherType.Identity;
- }
-
- var page = new VaultAddCipherPage(selectedType);
- await Navigation.PushForDeviceAsync(page);
- }
-
private async void Search()
{
var page = new ExtendedNavigationPage(new VaultSearchCiphersPage());
await Navigation.PushModalAsync(page);
}
- private class AddCipherToolBarItem : ExtendedToolbarItem
- {
- public AddCipherToolBarItem(VaultListGroupingsPage page)
- : base(() => page.AddCipher())
- {
- Text = AppResources.Add;
- Icon = "plus.png";
- }
- }
-
private class SearchToolBarItem : ExtendedToolbarItem
{
public SearchToolBarItem(VaultListGroupingsPage page)
diff --git a/src/App/Pages/Vault/VaultSearchCiphersPage.cs b/src/App/Pages/Vault/VaultSearchCiphersPage.cs
index 457655f6d..83022843a 100644
--- a/src/App/Pages/Vault/VaultSearchCiphersPage.cs
+++ b/src/App/Pages/Vault/VaultSearchCiphersPage.cs
@@ -24,15 +24,17 @@ namespace Bit.App.Pages
private readonly ISettings _settings;
private readonly IAppSettingsService _appSettingsService;
private readonly IGoogleAnalyticsService _googleAnalyticsService;
+ private readonly IDeviceActionService _deviceActionService;
private CancellationTokenSource _filterResultsCancellationTokenSource;
private readonly bool _favorites = false;
private readonly bool _folder = false;
private readonly string _folderId = null;
private readonly string _collectionId = null;
private readonly string _groupingName = null;
+ private readonly string _uri = null;
public VaultSearchCiphersPage(bool folder = false, string folderId = null,
- string collectionId = null, string groupingName = null, bool favorites = false)
+ string collectionId = null, string groupingName = null, bool favorites = false, string uri = null)
: base(true)
{
_folder = folder;
@@ -40,6 +42,7 @@ namespace Bit.App.Pages
_collectionId = collectionId;
_favorites = favorites;
_groupingName = groupingName;
+ _uri = uri;
_cipherService = Resolver.Resolve();
_connectivity = Resolver.Resolve();
@@ -48,6 +51,7 @@ namespace Bit.App.Pages
_settings = Resolver.Resolve();
_appSettingsService = Resolver.Resolve();
_googleAnalyticsService = Resolver.Resolve();
+ _deviceActionService = Resolver.Resolve();
Init();
}
@@ -58,9 +62,16 @@ namespace Bit.App.Pages
public ListView ListView { get; set; }
public SearchBar Search { get; set; }
public StackLayout ResultsStackLayout { get; set; }
+ private AddCipherToolbarItem AddCipherItem { get; set; }
private void Init()
{
+ if(!string.IsNullOrWhiteSpace(_uri) || _folder || !string.IsNullOrWhiteSpace(_folderId))
+ {
+ AddCipherItem = new AddCipherToolbarItem(this, _folderId);
+ ToolbarItems.Add(AddCipherItem);
+ }
+
ListView = new ListView(ListViewCachingStrategy.RecycleElement)
{
IsGroupingEnabled = true,
@@ -70,7 +81,7 @@ namespace Bit.App.Pages
nameof(Section.Count))),
GroupShortNameBinding = new Binding(nameof(Section.Name)),
ItemTemplate = new DataTemplate(() => new VaultListViewCell(
- (Cipher c) => Helpers.CipherMoreClickedAsync(this, c, false)))
+ (Cipher c) => Helpers.CipherMoreClickedAsync(this, c, !string.IsNullOrWhiteSpace(_uri))))
};
if(Device.RuntimePlatform == Device.iOS)
@@ -187,6 +198,18 @@ namespace Bit.App.Pages
}
}
+ protected override bool OnBackButtonPressed()
+ {
+ if(string.IsNullOrWhiteSpace(_uri))
+ {
+ return false;
+ }
+
+ _googleAnalyticsService.TrackExtensionEvent("BackClosed", _uri.StartsWith("http") ? "Website" : "App");
+ _deviceActionService.CloseAutofill();
+ return true;
+ }
+
protected override void OnAppearing()
{
base.OnAppearing();
@@ -198,6 +221,7 @@ namespace Bit.App.Pages
}
});
+ AddCipherItem?.InitEvents();
ListView.ItemSelected += CipherSelected;
Search.TextChanged += SearchBar_TextChanged;
Search.SearchButtonPressed += SearchBar_SearchButtonPressed;
@@ -214,6 +238,7 @@ namespace Bit.App.Pages
base.OnDisappearing();
MessagingCenter.Unsubscribe(_syncService, "SyncCompleted");
+ AddCipherItem?.Dispose();
ListView.ItemSelected -= CipherSelected;
Search.TextChanged -= SearchBar_TextChanged;
Search.SearchButtonPressed -= SearchBar_SearchButtonPressed;
@@ -291,8 +316,32 @@ namespace Bit.App.Pages
return;
}
- var page = new VaultViewCipherPage(cipher.Type, cipher.Id);
- await Navigation.PushForDeviceAsync(page);
+ string selection = null;
+ if(!string.IsNullOrWhiteSpace(_uri))
+ {
+ selection = await DisplayActionSheet(AppResources.AutofillOrView, AppResources.Cancel, null,
+ AppResources.Autofill, AppResources.View);
+ }
+
+ if(selection == AppResources.View || string.IsNullOrWhiteSpace(_uri))
+ {
+ var page = new VaultViewCipherPage(cipher.Type, cipher.Id);
+ await Navigation.PushForDeviceAsync(page);
+ }
+ else if(selection == AppResources.Autofill)
+ {
+ if(_deviceInfoService.Version < 21)
+ {
+ Helpers.CipherMoreClickedAsync(this, cipher, !string.IsNullOrWhiteSpace(_uri));
+ }
+ else
+ {
+ _googleAnalyticsService.TrackExtensionEvent("AutoFilled",
+ _uri.StartsWith("http") ? "Website" : "App");
+ _deviceActionService.Autofill(cipher);
+ }
+ }
+
((ListView)sender).SelectedItem = null;
}
}
diff --git a/src/App/Utilities/Helpers.cs b/src/App/Utilities/Helpers.cs
index 97bbaa916..6a82d520a 100644
--- a/src/App/Utilities/Helpers.cs
+++ b/src/App/Utilities/Helpers.cs
@@ -144,5 +144,33 @@ namespace Bit.App.Utilities
Resolver.Resolve().CopyToClipboard(copyText);
Resolver.Resolve().Toast(string.Format(AppResources.ValueHasBeenCopied, alertLabel));
}
+
+ public static async void AddCipher(Page page, string folderId)
+ {
+ var type = await Resolver.Resolve().ActionSheetAsync(
+ AppResources.SelectTypeAdd, AppResources.Cancel, null, null, AppResources.TypeLogin,
+ AppResources.TypeCard, AppResources.TypeIdentity, AppResources.TypeSecureNote);
+
+ var selectedType = CipherType.SecureNote;
+ if(type == AppResources.Cancel)
+ {
+ return;
+ }
+ else if(type == AppResources.TypeLogin)
+ {
+ selectedType = CipherType.Login;
+ }
+ else if(type == AppResources.TypeCard)
+ {
+ selectedType = CipherType.Card;
+ }
+ else if(type == AppResources.TypeIdentity)
+ {
+ selectedType = CipherType.Identity;
+ }
+
+ var addPage = new VaultAddCipherPage(selectedType, defaultFolderId: folderId);
+ await page.Navigation.PushForDeviceAsync(addPage);
+ }
}
}