1
0
mirror of https://github.com/bitwarden/mobile.git synced 2024-06-29 11:05:52 +02:00

Merge branch 'feature/maui-migration' into feature/maui-migration-passkeys

# Conflicts:
#	src/iOS.Autofill/iOS.Autofill.csproj
This commit is contained in:
Federico Maccaroni 2024-01-12 13:59:59 -03:00
commit 822ad7564e
No known key found for this signature in database
GPG Key ID: 5D233F8F2B034536
141 changed files with 1050 additions and 3221 deletions

5
Directory.Build.props Normal file
View File

@ -0,0 +1,5 @@
<Project>
<PropertyGroup>
<MauiVersion>8.0.4-nightly.*</MauiVersion>
</PropertyGroup>
</Project>

6
nuget.config Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="MAUI Nightly builds" value="https://pkgs.dev.azure.com/xamarin/public/_packaging/maui-nightly/nuget/v3/index.json" />
</packageSources>
</configuration>

View File

@ -156,6 +156,15 @@
<BundleResource Include="Platforms\Android\Resources\drawable-hdpi\logo_white_legacy.png" />
<BundleResource Include="Platforms\Android\Resources\mipmap-xhdpi\ic_launcher.png" />
<BundleResource Include="Platforms\Android\Resources\mipmap-xhdpi\ic_launcher_round.png" />
<BundleResource Include="Platforms\iOS\Resources\logo.png" />
<BundleResource Include="Platforms\iOS\Resources\logo_white%402x.png" />
<BundleResource Include="Platforms\iOS\Resources\more_vert%402x.png" />
<BundleResource Include="Platforms\iOS\Resources\logo_white%403x.png" />
<BundleResource Include="Platforms\iOS\Resources\logo%403x.png" />
<BundleResource Include="Platforms\iOS\Resources\more_vert%403x.png" />
<BundleResource Include="Platforms\iOS\Resources\more_vert.png" />
<BundleResource Include="Platforms\iOS\Resources\logo_white.png" />
<BundleResource Include="Platforms\iOS\Resources\logo%402x.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\iOS.Core\iOS.Core.csproj" Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'" />
@ -236,4 +245,15 @@
<GoogleServicesJson Include="Platforms\Android\google-services.json" />
<GoogleServicesJson Include="Platforms\Android\google-services.json.enc" />
</ItemGroup>
<ItemGroup>
<None Remove="Platforms\iOS\Resources\logo.png" />
<None Remove="Platforms\iOS\Resources\logo_white%402x.png" />
<None Remove="Platforms\iOS\Resources\more_vert%402x.png" />
<None Remove="Platforms\iOS\Resources\logo_white%403x.png" />
<None Remove="Platforms\iOS\Resources\logo%403x.png" />
<None Remove="Platforms\iOS\Resources\more_vert%403x.png" />
<None Remove="Platforms\iOS\Resources\more_vert.png" />
<None Remove="Platforms\iOS\Resources\logo_white.png" />
<None Remove="Platforms\iOS\Resources\logo%402x.png" />
</ItemGroup>
</Project>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2024.1.0" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2024.1.1" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.NFC" />

View File

@ -54,10 +54,6 @@ namespace Bit.iOS
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
//LoadApplication(new App.App(null));
//iOSCoreHelpers.AppearanceAdjustments();
//ZXing.Net.Mobile.Forms.iOS.Platform.Init();
ConnectToWatchIfNeededAsync().FireAndForget();
_broadcasterService.Subscribe(nameof(AppDelegate), async (message) =>
@ -221,7 +217,11 @@ namespace Bit.iOS
public override void DidEnterBackground(UIApplication uiApplication)
{
_stateService?.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
if (_stateService != null && _deviceActionService != null)
{
_stateService.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
}
_messagingService?.Send("slept");
base.DidEnterBackground(uiApplication);
}

View File

@ -11,7 +11,7 @@
<key>CFBundleIdentifier</key>
<string>com.8bit.bitwarden</string>
<key>CFBundleShortVersionString</key>
<string>2024.1.0</string>
<string>2024.1.1</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CFBundleIconName</key>

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

Before

Width:  |  Height:  |  Size: 164 B

After

Width:  |  Height:  |  Size: 164 B

View File

Before

Width:  |  Height:  |  Size: 242 B

After

Width:  |  Height:  |  Size: 242 B

View File

Before

Width:  |  Height:  |  Size: 338 B

After

Width:  |  Height:  |  Size: 338 B

View File

@ -35,6 +35,8 @@ namespace Bit.App
private readonly IAccountsManager _accountsManager;
private readonly IPushNotificationService _pushNotificationService;
private readonly IConfigService _configService;
private readonly ILogger _logger;
private static bool _isResumed;
// these variables are static because the app is launching new activities on notification click, creating new instances of App.
private static bool _pendingCheckPasswordlessLoginRequests;
@ -155,6 +157,7 @@ namespace Bit.App
_accountsManager = ServiceContainer.Resolve<IAccountsManager>("accountsManager");
_pushNotificationService = ServiceContainer.Resolve<IPushNotificationService>();
_configService = ServiceContainer.Resolve<IConfigService>();
_logger = ServiceContainer.Resolve<ILogger>();
_accountsManager.Init(() => Options, this);
@ -169,7 +172,7 @@ namespace Bit.App
var confirmed = true;
var confirmText = string.IsNullOrWhiteSpace(details.ConfirmText) ?
AppResources.Ok : details.ConfirmText;
MainThread.BeginInvokeOnMainThread(async () =>
await MainThread.InvokeOnMainThreadAsync(async () =>
{
if (!string.IsNullOrWhiteSpace(details.CancelText))
{
@ -183,20 +186,16 @@ namespace Bit.App
_messagingService.Send("showDialogResolve", new Tuple<int, bool>(details.DialogId, confirmed));
});
}
#if IOS
else if (message.Command == AppHelpers.RESUMED_MESSAGE_COMMAND)
{
if (DeviceInfo.Platform == DevicePlatform.iOS)
{
ResumedAsync().FireAndForget();
}
ResumedAsync().FireAndForget();
}
else if (message.Command == "slept")
{
if (DeviceInfo.Platform == DevicePlatform.iOS)
{
await SleptAsync();
}
await SleptAsync();
}
#endif
else if (message.Command == "migrated")
{
await Task.Delay(1000);
@ -213,7 +212,7 @@ namespace Bit.App
Options.OtpData = new OtpData((string)message.Data);
}
MainThread.InvokeOnMainThreadAsync(async () =>
await MainThread.InvokeOnMainThreadAsync(async () =>
{
if (MainPage is TabsPage tabsPage)
{
@ -249,7 +248,7 @@ namespace Bit.App
}
else if (message.Command == "convertAccountToKeyConnector")
{
MainThread.BeginInvokeOnMainThread(async () =>
await MainThread.InvokeOnMainThreadAsync(async () =>
{
await MainPage.Navigation.PushModalAsync(
new NavigationPage(new RemoveMasterPasswordPage()));
@ -257,7 +256,7 @@ namespace Bit.App
}
else if (message.Command == Constants.ForceUpdatePassword)
{
MainThread.BeginInvokeOnMainThread(async () =>
await MainThread.InvokeOnMainThreadAsync(async () =>
{
await MainPage.Navigation.PushModalAsync(
new NavigationPage(new UpdateTempPasswordPage()));
@ -373,40 +372,52 @@ namespace Bit.App
protected override async void OnStart()
{
System.Diagnostics.Debug.WriteLine("XF App: OnStart");
_isResumed = true;
await ClearCacheIfNeededAsync();
Prime();
if (string.IsNullOrWhiteSpace(Options.Uri))
try
{
var updated = await AppHelpers.PerformUpdateTasksAsync(_syncService, _deviceActionService,
_stateService);
if (!updated)
System.Diagnostics.Debug.WriteLine("XF App: OnStart");
_isResumed = true;
await ClearCacheIfNeededAsync();
Prime();
if (string.IsNullOrWhiteSpace(Options.Uri))
{
SyncIfNeeded();
var updated = await AppHelpers.PerformUpdateTasksAsync(_syncService, _deviceActionService,
_stateService);
if (!updated)
{
SyncIfNeeded();
}
}
}
if (_pendingCheckPasswordlessLoginRequests)
{
_messagingService.Send(Constants.PasswordlessLoginRequestKey);
}
if (DeviceInfo.Platform == DevicePlatform.Android)
{
if (_pendingCheckPasswordlessLoginRequests)
{
_messagingService.Send(Constants.PasswordlessLoginRequestKey);
}
#if ANDROID
await _vaultTimeoutService.CheckVaultTimeoutAsync();
// Reset delay on every start
_vaultTimeoutService.DelayLockAndLogoutMs = null;
}
#endif
await _configService.GetAsync();
_messagingService.Send("startEventTimer");
await _configService.GetAsync();
_messagingService.Send("startEventTimer");
}
catch (Exception ex)
{
_logger?.Exception(ex);
throw;
}
}
#if ANDROID
protected override async void OnSleep()
#else
protected override void OnSleep()
#endif
{
System.Diagnostics.Debug.WriteLine("XF App: OnSleep");
_isResumed = false;
if (DeviceInfo.Platform == DevicePlatform.Android)
try
{
System.Diagnostics.Debug.WriteLine("XF App: OnSleep");
_isResumed = false;
#if ANDROID
var isLocked = await _vaultTimeoutService.IsLockedAsync();
if (!isLocked)
{
@ -417,20 +428,34 @@ namespace Bit.App
ClearAutofillUri();
}
await SleptAsync();
#endif
}
catch (Exception ex)
{
_logger?.Exception(ex);
throw;
}
}
protected override void OnResume()
{
System.Diagnostics.Debug.WriteLine("XF App: OnResume");
_isResumed = true;
if (_pendingCheckPasswordlessLoginRequests)
{
_messagingService.Send(Constants.PasswordlessLoginRequestKey);
}
if (DeviceInfo.Platform == DevicePlatform.Android)
try
{
System.Diagnostics.Debug.WriteLine("XF App: OnResume");
_isResumed = true;
if (_pendingCheckPasswordlessLoginRequests)
{
_messagingService.Send(Constants.PasswordlessLoginRequestKey);
}
#if ANDROID
ResumedAsync().FireAndForget();
#endif
}
catch (Exception ex)
{
_logger?.Exception(ex);
throw;
}
}
@ -517,14 +542,22 @@ namespace Bit.App
{
MainThread.BeginInvokeOnMainThread(() =>
{
Options.Uri = null;
if (isLocked)
try
{
App.MainPage = new NavigationPage(new LockPage());
Options.Uri = null;
if (isLocked)
{
App.MainPage = new NavigationPage(new LockPage());
}
else
{
App.MainPage = new TabsPage();
}
}
else
catch (Exception ex)
{
App.MainPage = new TabsPage();
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
});
});
@ -549,7 +582,7 @@ namespace Bit.App
ThemeManager.SetTheme(Resources);
RequestedThemeChanged += (s, a) =>
{
UpdateThemeAsync();
UpdateThemeAsync().FireAndForget();
};
_isResumed = true;
#if IOS
@ -568,11 +601,18 @@ namespace Bit.App
}
Task.Run(async () =>
{
var lastSync = await _syncService.GetLastSyncAsync();
if (lastSync == null || ((DateTime.UtcNow - lastSync) > TimeSpan.FromMinutes(30)))
try
{
await Task.Delay(1000);
await _syncService.FullSyncAsync(false);
var lastSync = await _syncService.GetLastSyncAsync();
if (lastSync == null || ((DateTime.UtcNow - lastSync) > TimeSpan.FromMinutes(30)))
{
await Task.Delay(1000);
await _syncService.FullSyncAsync(false);
}
}
catch (Exception ex)
{
_logger.Exception(ex);
}
});
}

View File

@ -165,7 +165,7 @@ namespace Bit.App.Pages
await MainThread.InvokeOnMainThreadAsync(async () =>
{
var result = await Page.DisplayActionSheet(AppResources.LoggingInOn, AppResources.Cancel, null, options);
var result = await _deviceActionService.Value.DisplayActionSheetAsync(AppResources.LoggingInOn, AppResources.Cancel, null, options);
if (result is null || result == AppResources.Cancel)
{

View File

@ -4,6 +4,7 @@ using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Bit.Core.Services;
namespace Bit.App.Pages
{
@ -26,16 +27,24 @@ namespace Bit.App.Pages
_vm = BindingContext as LockPageViewModel;
_vm.CheckPendingAuthRequests = checkPendingAuthRequests;
_vm.Page = this;
_vm.UnlockedAction = () => MainThread.BeginInvokeOnMainThread(async () => await UnlockedAsync());
_vm.UnlockedAction = () => MainThread.BeginInvokeOnMainThread(async () =>
{
try
{
await UnlockedAsync();
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
});
if (DeviceInfo.Platform == DevicePlatform.iOS)
{
ToolbarItems.Add(_moreItem);
}
else
{
ToolbarItems.Add(_logOut);
}
#if IOS
ToolbarItems.Add(_moreItem);
#else
ToolbarItems.Add(_logOut);
#endif
}
public Entry SecretEntry
@ -65,52 +74,60 @@ namespace Bit.App.Pages
protected override async void OnAppearing()
{
base.OnAppearing();
_broadcasterService.Subscribe(nameof(LockPage), message =>
try
{
if (message.Command == Constants.ClearSensitiveFields)
base.OnAppearing();
_broadcasterService.Subscribe(nameof(LockPage), message =>
{
MainThread.BeginInvokeOnMainThread(_vm.ResetPinPasswordFields);
}
});
if (_appeared)
{
return;
}
_appeared = true;
_mainContent.Content = _mainLayout;
//Workaround: This delay allows the Avatar to correctly load on iOS. The cause of this issue is also likely connected with the race conditions issue when using loading modals in iOS
await Task.Delay(50);
_accountAvatar?.OnAppearing();
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
await _vm.InitAsync();
_vm.FocusSecretEntry += PerformFocusSecretEntry;
if (!_vm.BiometricEnabled)
{
RequestFocus(SecretEntry);
}
else
{
if (!_vm.HasMasterPassword && !_vm.PinEnabled)
{
_passwordGrid.IsVisible = false;
_unlockButton.IsVisible = false;
}
if (_autoPromptBiometric)
{
var tasks = Task.Run(async () =>
if (message.Command == Constants.ClearSensitiveFields)
{
await Task.Delay(500);
MainThread.BeginInvokeOnMainThread(async () => await _vm.PromptBiometricAsync());
});
MainThread.BeginInvokeOnMainThread(_vm.ResetPinPasswordFields);
}
});
if (_appeared)
{
return;
}
_appeared = true;
_mainContent.Content = _mainLayout;
//Workaround: This delay allows the Avatar to correctly load on iOS. The cause of this issue is also likely connected with the race conditions issue when using loading modals in iOS
await Task.Delay(50);
_accountAvatar?.OnAppearing();
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
await _vm.InitAsync();
_vm.FocusSecretEntry += PerformFocusSecretEntry;
if (!_vm.BiometricEnabled)
{
RequestFocus(SecretEntry);
}
else
{
if (!_vm.HasMasterPassword && !_vm.PinEnabled)
{
_passwordGrid.IsVisible = false;
_unlockButton.IsVisible = false;
}
if (_autoPromptBiometric)
{
var tasks = Task.Run(async () =>
{
await Task.Delay(500);
await MainThread.InvokeOnMainThreadAsync(async () => await _vm.PromptBiometricAsync());
});
}
}
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
}
@ -167,27 +184,44 @@ namespace Bit.App.Pages
private async void Biometric_Clicked(object sender, EventArgs e)
{
if (DoOnce())
try
{
await _vm.PromptBiometricAsync();
if (DoOnce())
{
await _vm.PromptBiometricAsync();
}
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
}
private async void More_Clicked(object sender, System.EventArgs e)
{
await _accountListOverlay.HideAsync();
if (!DoOnce())
try
{
return;
await _accountListOverlay.HideAsync();
if (!DoOnce())
{
return;
}
var selection = await DisplayActionSheet(AppResources.Options,
AppResources.Cancel, null, AppResources.LogOut);
if (selection == AppResources.LogOut)
{
await _vm.LogOutAsync();
}
}
var selection = await DisplayActionSheet(AppResources.Options,
AppResources.Cancel, null, AppResources.LogOut);
if (selection == AppResources.LogOut)
catch (Exception ex)
{
await _vm.LogOutAsync();
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
}

View File

@ -14,6 +14,7 @@ namespace Bit.App.Pages
{
private readonly LazyResolve<IStateService> _stateService = new LazyResolve<IStateService>();
private readonly LazyResolve<IDeviceActionService> _deviceActionService = new LazyResolve<IDeviceActionService>();
private readonly LazyResolve<ILogger> _logger = new LazyResolve<ILogger>();
protected int ShowModalAnimationDelay = 400;
protected int ShowPageAnimationDelay = 100;
@ -48,22 +49,38 @@ namespace Bit.App.Pages
protected override async void OnNavigatedTo(NavigatedToEventArgs args)
{
base.OnNavigatedTo(args);
if (IsThemeDirty)
try
{
UpdateOnThemeChanged();
base.OnNavigatedTo(args);
if (IsThemeDirty)
{
try
{
await UpdateOnThemeChanged();
}
catch (Exception ex)
{
Core.Services.LoggerHelper.LogEvenIfCantBeResolved(ex);
// Don't rethrow on theme changed so the user can still continue on the app.
}
}
await SaveActivityAsync();
if (ShouldCheckToPreventOnNavigatedToCalledTwice && _hasInitedOnNavigatedTo)
{
return;
}
_hasInitedOnNavigatedTo = true;
await InitOnNavigatedToAsync();
}
await SaveActivityAsync();
if (ShouldCheckToPreventOnNavigatedToCalledTwice && _hasInitedOnNavigatedTo)
catch (Exception ex)
{
return;
Core.Services.LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
_hasInitedOnNavigatedTo = true;
await InitOnNavigatedToAsync();
}
protected virtual Task InitOnNavigatedToAsync() => Task.CompletedTask;
@ -116,29 +133,37 @@ namespace Bit.App.Pages
{
async Task DoWorkAsync()
{
await workFunction.Invoke();
if (sourceView != null)
try
{
if (targetView != null)
await workFunction.Invoke();
if (sourceView != null)
{
targetView.Content = sourceView;
}
else
{
Content = sourceView;
if (targetView != null)
{
targetView.Content = sourceView;
}
else
{
Content = sourceView;
}
}
}
catch (Exception ex)
{
_logger.Value.Exception(ex);
throw;
}
}
if (DeviceInfo.Platform == DevicePlatform.iOS)
{
await DoWorkAsync();
return;
}
#if IOS
await DoWorkAsync();
#else
await Task.Run(async () =>
{
await Task.Delay(fromModal ? ShowModalAnimationDelay : ShowPageAnimationDelay);
MainThread.BeginInvokeOnMainThread(async () => await DoWorkAsync());
});
#endif
}
protected void RequestFocus(InputView input)

View File

@ -42,7 +42,7 @@ namespace Bit.App.Pages
public async Task InitAsync()
{
var history = await _passwordGenerationService.GetHistoryAsync();
Device.BeginInvokeOnMainThread(() =>
MainThread.BeginInvokeOnMainThread(() =>
{
History.ResetWithRange(history ?? new List<GeneratedPasswordHistory>());
ShowNoData = History.Count == 0;
@ -66,7 +66,7 @@ namespace Bit.App.Pages
{
try
{
await Device.InvokeOnMainThreadAsync(() => History.ResetWithRange(new List<GeneratedPasswordHistory>()));
await MainThread.InvokeOnMainThreadAsync(() => History.ResetWithRange(new List<GeneratedPasswordHistory>()));
await InitAsync();
}

View File

@ -15,6 +15,7 @@ namespace Bit.App.Pages
public partial class GeneratorPage : BaseContentPage, IThemeDirtablePage
{
private readonly IBroadcasterService _broadcasterService;
private readonly ILogger _logger;
private GeneratorPageViewModel _vm;
private readonly bool _fromTabPage;
@ -26,6 +27,8 @@ namespace Bit.App.Pages
_tabsPage = tabsPage;
InitializeComponent();
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>();
_logger = ServiceContainer.Resolve<ILogger>();
_vm = BindingContext as GeneratorPageViewModel;
_vm.Page = this;
_fromTabPage = fromTabPage;
@ -35,26 +38,21 @@ namespace Bit.App.Pages
_vm.EmailWebsite = emailWebsite;
_vm.EditMode = editMode;
_vm.IosExtension = appOptions?.IosExtension ?? false;
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
var isIos = Device.RuntimePlatform == Device.iOS;
if (selectAction != null)
{
if (isIos)
{
ToolbarItems.Add(_closeItem);
}
#if IOS
ToolbarItems.Add(_closeItem);
#endif
ToolbarItems.Add(_selectItem);
}
else
{
if (isIos)
{
ToolbarItems.Add(_moreItem);
}
else
{
ToolbarItems.Add(_historyItem);
}
#if IOS
ToolbarItems.Add(_moreItem);
#else
ToolbarItems.Add(_historyItem);
#endif
}
_typePicker.On<Microsoft.Maui.Controls.PlatformConfiguration.iOS>().SetUpdateMode(UpdateMode.WhenFinished);
_passwordTypePicker.On<Microsoft.Maui.Controls.PlatformConfiguration.iOS>().SetUpdateMode(UpdateMode.WhenFinished);
@ -71,22 +69,30 @@ namespace Bit.App.Pages
protected async override void OnAppearing()
{
base.OnAppearing();
lblPassword.IsVisible = true;
if (!_fromTabPage)
try
{
await InitAsync();
}
base.OnAppearing();
_broadcasterService.Subscribe(nameof(GeneratorPage), (message) =>
{
if (message.Command is ThemeManager.UPDATED_THEME_MESSAGE_KEY)
lblPassword.IsVisible = true;
if (!_fromTabPage)
{
Device.BeginInvokeOnMainThread(() => _vm.RedrawPassword());
await InitAsync();
}
});
_broadcasterService.Subscribe(nameof(GeneratorPage), (message) =>
{
if (message.Command is ThemeManager.UPDATED_THEME_MESSAGE_KEY)
{
MainThread.BeginInvokeOnMainThread(() => _vm.RedrawPassword());
}
});
}
catch (Exception ex)
{
_logger.Exception(ex);
throw;
}
}
protected override void OnDisappearing()
@ -100,27 +106,35 @@ namespace Bit.App.Pages
protected override bool OnBackButtonPressed()
{
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.Android && _tabsPage != null)
#if ANDROID
if (_tabsPage != null)
{
_tabsPage.ResetToVaultPage();
return true;
}
#endif
return base.OnBackButtonPressed();
}
private async void More_Clicked(object sender, EventArgs e)
{
if (!DoOnce())
try
{
return;
if (!DoOnce())
{
return;
}
var selection = await DisplayActionSheet(AppResources.Options, AppResources.Cancel,
null, AppResources.PasswordHistory);
if (selection == AppResources.PasswordHistory)
{
var page = new GeneratorHistoryPage();
await Navigation.PushModalAsync(new Microsoft.Maui.Controls.NavigationPage(page));
}
}
var selection = await DisplayActionSheet(AppResources.Options, AppResources.Cancel,
null, AppResources.PasswordHistory);
if (selection == AppResources.PasswordHistory)
catch (Exception ex)
{
var page = new GeneratorHistoryPage();
await Navigation.PushModalAsync(new Microsoft.Maui.Controls.NavigationPage(page));
_logger.Exception(ex);
}
}
@ -144,7 +158,7 @@ namespace Bit.App.Pages
{
await base.UpdateOnThemeChanged();
await Device.InvokeOnMainThreadAsync(() =>
await MainThread.InvokeOnMainThreadAsync(() =>
{
if (_vm != null)
{

View File

@ -18,6 +18,7 @@ namespace Bit.App.Pages
private readonly ISyncService _syncService;
private readonly IVaultTimeoutService _vaultTimeoutService;
private readonly ISendService _sendService;
private readonly ILogger _logger;
private readonly SendGroupingsPageViewModel _vm;
private readonly string _pageName;
@ -33,6 +34,8 @@ namespace Bit.App.Pages
_syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
_sendService = ServiceContainer.Resolve<ISendService>("sendService");
_logger = ServiceContainer.Resolve<ILogger>();
_vm = BindingContext as SendGroupingsPageViewModel;
_vm.Page = this;
_vm.MainPage = mainPage;
@ -43,85 +46,89 @@ namespace Bit.App.Pages
_vm.PageTitle = pageTitle;
}
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.iOS)
#if IOS
_absLayout.Children.Remove(_fab);
if (type == null)
{
_absLayout.Children.Remove(_fab);
if (type == null)
{
ToolbarItems.Add(_aboutIconItem);
}
ToolbarItems.Add(_addItem);
}
else
{
ToolbarItems.Add(_syncItem);
ToolbarItems.Add(_lockItem);
ToolbarItems.Add(_aboutTextItem);
ToolbarItems.Add(_aboutIconItem);
}
ToolbarItems.Add(_addItem);
#else
ToolbarItems.Add(_syncItem);
ToolbarItems.Add(_lockItem);
ToolbarItems.Add(_aboutTextItem);
#endif
}
protected override async void OnAppearing()
{
base.OnAppearing();
if (_syncService.SyncInProgress)
try
{
IsBusy = true;
}
_broadcasterService.Subscribe(_pageName, async (message) =>
{
try
base.OnAppearing();
if (_syncService.SyncInProgress)
{
if (message.Command == "syncStarted")
{
Device.BeginInvokeOnMainThread(() => IsBusy = true);
}
else if (message.Command == "syncCompleted" || message.Command == "sendUpdated")
{
await Task.Delay(500);
Device.BeginInvokeOnMainThread(() =>
{
IsBusy = false;
if (_vm.LoadedOnce)
{
var task = _vm.LoadAsync();
}
});
}
IsBusy = true;
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
}
});
await LoadOnAppearedAsync(_mainLayout, false, async () =>
{
if (!_syncService.SyncInProgress || (await _sendService.GetAllAsync()).Any())
_broadcasterService.Subscribe(_pageName, async (message) =>
{
try
{
await _vm.LoadAsync();
if (message.Command == "syncStarted")
{
MainThread.BeginInvokeOnMainThread(() => IsBusy = true);
}
else if (message.Command == "syncCompleted" || message.Command == "sendUpdated")
{
await Task.Delay(500);
await MainThread.InvokeOnMainThreadAsync(() =>
{
IsBusy = false;
if (_vm.LoadedOnce)
{
var task = _vm.LoadAsync();
}
});
}
}
catch (Exception e) when (e.Message.Contains("No key."))
catch (Exception ex)
{
await Task.Delay(1000);
await _vm.LoadAsync();
LoggerHelper.LogEvenIfCantBeResolved(ex);
}
}
else
{
await Task.Delay(5000);
if (!_vm.Loaded)
{
await _vm.LoadAsync();
}
}
});
AdjustToolbar();
await CheckAddRequest();
}, _mainContent);
await LoadOnAppearedAsync(_mainLayout, false, async () =>
{
if (!_syncService.SyncInProgress || (await _sendService.GetAllAsync()).Any())
{
try
{
await _vm.LoadAsync();
}
catch (Exception e) when (e.Message.Contains("No key."))
{
await Task.Delay(1000);
await _vm.LoadAsync();
}
}
else
{
await Task.Delay(5000);
if (!_vm.Loaded)
{
await _vm.LoadAsync();
}
}
AdjustToolbar();
await CheckAddRequest();
}, _mainContent);
}
catch (Exception ex)
{
_logger.Exception(ex);
throw;
}
}
protected override void OnDisappearing()

View File

@ -1,22 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.Core.Resources.Localization;
using Bit.App.Abstractions;
using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.View;
using Bit.Core.Resources.Localization;
using Bit.Core.Utilities;
using DeviceType = Bit.Core.Enums.DeviceType;
using Microsoft.Maui.Networking;
using Microsoft.Maui.Devices;
using Microsoft.Maui.Controls;
using Microsoft.Maui;
namespace Bit.App.Pages
{
public class SendGroupingsPageViewModel : BaseViewModel
@ -117,28 +106,38 @@ namespace Bit.App.Pages
{
return;
}
var authed = await _stateService.IsAuthenticatedAsync();
if (!authed)
try
{
return;
var authed = await _stateService.IsAuthenticatedAsync();
if (!authed)
{
return;
}
if (await _vaultTimeoutService.IsLockedAsync())
{
return;
}
if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing)
{
SyncRefreshing = true;
await _syncService.FullSyncAsync(false);
return;
}
_doingLoad = true;
LoadedOnce = true;
ShowNoData = false;
Loading = true;
ShowList = false;
SendEnabled = !await AppHelpers.IsSendDisabledByPolicyAsync();
}
if (await _vaultTimeoutService.IsLockedAsync())
catch (Exception ex)
{
return;
}
if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing)
{
SyncRefreshing = true;
await _syncService.FullSyncAsync(false);
return;
_logger.Value.Exception(ex);
throw;
}
_doingLoad = true;
LoadedOnce = true;
ShowNoData = false;
Loading = true;
ShowList = false;
SendEnabled = !await AppHelpers.IsSendDisabledByPolicyAsync();
var groupedSends = new List<SendGroupingsPageListGroup>();
var page = Page as SendGroupingsPage;
@ -146,8 +145,11 @@ namespace Bit.App.Pages
{
await LoadDataAsync();
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
var uppercaseGroupNames = Device.RuntimePlatform == Device.iOS;
#if IOS
var uppercaseGroupNames = true;
#else
var uppercaseGroupNames = false;
#endif
if (MainPage)
{
groupedSends.Add(new SendGroupingsPageListGroup(
@ -208,6 +210,11 @@ namespace Bit.App.Pages
}
}
}
catch (Exception ex)
{
_logger.Value.Exception(ex);
throw;
}
finally
{
_doingLoad = false;
@ -315,14 +322,22 @@ namespace Bit.App.Pages
private async void SendOptionsAsync(SendView send)
{
if ((Page as BaseContentPage).DoOnce())
try
{
var selection = await AppHelpers.SendListOptions(Page, send);
if (selection == AppResources.RemovePassword || selection == AppResources.Delete)
if ((Page as BaseContentPage).DoOnce())
{
await LoadAsync();
var selection = await AppHelpers.SendListOptions(Page, send);
if (selection == AppResources.RemovePassword || selection == AppResources.Delete)
{
await LoadAsync();
}
}
}
catch (Exception ex)
{
_logger.Value.Exception(ex);
throw;
}
}
}
}

View File

@ -29,7 +29,7 @@ namespace Bit.App.Pages
{
if (message.Command == "selectSaveFileResult")
{
Device.BeginInvokeOnMainThread(() =>
MainThread.BeginInvokeOnMainThread(() =>
{
var data = message.Data as Tuple<string, string>;
if (data == null)

View File

@ -49,12 +49,10 @@ namespace Bit.App.Pages
private void UpdatePlaceholder()
{
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.Android)
{
MainThread.BeginInvokeOnMainThread(() =>
_emptyPlaceholder.Source = ImageSource.FromFile(ThemeManager.UsingLightTheme ? "empty_login_requests" : "empty_login_requests_dark"));
}
#if ANDROID
MainThread.BeginInvokeOnMainThread(() =>
_emptyPlaceholder.Source = ImageSource.FromFile(ThemeManager.UsingLightTheme ? "empty_login_requests" : "empty_login_requests_dark"));
#endif
}
}
}

View File

@ -10,6 +10,7 @@ using Bit.Core.Abstractions;
using Bit.Core.Models.Data;
using Bit.Core.Models.Domain;
using Bit.Core.Utilities;
using Bit.Core.Services;
namespace Bit.App.Pages
{
@ -87,21 +88,29 @@ namespace Bit.App.Pages
protected override async void OnAppearing()
{
base.OnAppearing();
_broadcasterService.Subscribe(nameof(TabsPage), async (message) =>
try
{
if (message.Command == "syncCompleted")
base.OnAppearing();
_broadcasterService.Subscribe(nameof(TabsPage), async (message) =>
{
MainThread.BeginInvokeOnMainThread(async () => await UpdateVaultButtonTitleAsync());
if (message.Command == "syncCompleted")
{
MainThread.BeginInvokeOnMainThread(async () => await UpdateVaultButtonTitleAsync());
}
});
await UpdateVaultButtonTitleAsync();
if (await _keyConnectorService.UserNeedsMigrationAsync())
{
_messagingService.Send("convertAccountToKeyConnector");
}
});
await UpdateVaultButtonTitleAsync();
if (await _keyConnectorService.UserNeedsMigrationAsync())
{
_messagingService.Send("convertAccountToKeyConnector");
}
await ForcePasswordResetIfNeededAsync();
await ForcePasswordResetIfNeededAsync();
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
}
private async Task ForcePasswordResetIfNeededAsync()
@ -174,23 +183,31 @@ namespace Bit.App.Pages
protected override async void OnCurrentPageChanged()
{
if (CurrentPage is NavigationPage navPage)
try
{
if (_groupingsPage?.RootPage is GroupingsPage groupingsPage)
if (CurrentPage is NavigationPage navPage)
{
await groupingsPage.HideAccountSwitchingOverlayAsync();
}
if (_groupingsPage?.RootPage is GroupingsPage groupingsPage)
{
await groupingsPage.HideAccountSwitchingOverlayAsync();
}
_messagingService.Send(ThemeManager.UPDATED_THEME_MESSAGE_KEY);
if (navPage.RootPage is GroupingsPage)
{
// Load something?
}
else if (navPage.RootPage is GeneratorPage genPage)
{
await genPage.InitAsync();
_messagingService.Send(ThemeManager.UPDATED_THEME_MESSAGE_KEY);
if (navPage.RootPage is GroupingsPage)
{
// Load something?
}
else if (navPage.RootPage is GeneratorPage genPage)
{
await genPage.InitAsync();
}
}
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
}
public void OnPageReselected()

View File

@ -33,7 +33,7 @@ namespace Bit.App.Pages
{
if (message.Command == "selectFileResult")
{
Device.BeginInvokeOnMainThread(() =>
MainThread.BeginInvokeOnMainThread(() =>
{
var data = message.Data as Tuple<byte[], string>;
_vm.FileData = data.Item1;

View File

@ -7,6 +7,7 @@ using Bit.Core.Enums;
using Bit.Core.Utilities;
using Microsoft.Maui.Controls.PlatformConfiguration;
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
using Bit.Core.Services;
namespace Bit.App.Pages
{
@ -263,8 +264,16 @@ namespace Bit.App.Pages
{
MainThread.BeginInvokeOnMainThread(async () =>
{
await Navigation.PopModalAsync();
await _vm.UpdateTotpKeyAsync(key);
try
{
await Navigation.PopModalAsync();
await _vm.UpdateTotpKeyAsync(key);
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
});
});

View File

@ -63,12 +63,12 @@ namespace Bit.App.Pages
{
if (message.Command == "syncStarted")
{
Device.BeginInvokeOnMainThread(() => IsBusy = true);
MainThread.BeginInvokeOnMainThread(() => IsBusy = true);
}
else if (message.Command == "syncCompleted")
{
await Task.Delay(500);
Device.BeginInvokeOnMainThread(() =>
MainThread.BeginInvokeOnMainThread(() =>
{
IsBusy = false;
if (message.Data is Dictionary<string, object> data && data.ContainsKey("successfully"))
@ -83,7 +83,7 @@ namespace Bit.App.Pages
}
else if (message.Command == "selectSaveFileResult")
{
Device.BeginInvokeOnMainThread(() =>
MainThread.BeginInvokeOnMainThread(() =>
{
var data = message.Data as Tuple<string, string>;
if (data == null)

View File

@ -109,52 +109,59 @@ namespace Bit.App.Pages
var cts = new CancellationTokenSource();
Task.Run(async () =>
{
List<CipherView> ciphers = null;
var searchable = !string.IsNullOrWhiteSpace(searchText) && searchText.Length > 1;
var shouldShowAllWhenEmpty = ShowAllIfSearchTextEmpty && string.IsNullOrEmpty(searchText);
if (searchable || shouldShowAllWhenEmpty)
try
{
if (timeout != null)
List<CipherView> ciphers = null;
var searchable = !string.IsNullOrWhiteSpace(searchText) && searchText.Length > 1;
var shouldShowAllWhenEmpty = ShowAllIfSearchTextEmpty && string.IsNullOrEmpty(searchText);
if (searchable || shouldShowAllWhenEmpty)
{
await Task.Delay(timeout.Value);
}
if (searchText != (Page as CiphersPage).SearchBar.Text
&&
!shouldShowAllWhenEmpty)
{
return;
}
if (timeout != null)
{
await Task.Delay(timeout.Value);
}
if (searchText != (Page as CiphersPage).SearchBar.Text
&&
!shouldShowAllWhenEmpty)
{
return;
}
previousCts?.Cancel();
try
{
var vaultFilteredCiphers = await GetAllCiphersAsync();
if (!shouldShowAllWhenEmpty)
previousCts?.Cancel();
try
{
ciphers = await _searchService.SearchCiphersAsync(searchText,
Filter ?? (c => c.IsDeleted == Deleted), vaultFilteredCiphers, cts.Token);
var vaultFilteredCiphers = await GetAllCiphersAsync();
if (!shouldShowAllWhenEmpty)
{
ciphers = await _searchService.SearchCiphersAsync(searchText,
Filter ?? (c => c.IsDeleted == Deleted), vaultFilteredCiphers, cts.Token);
}
else
{
ciphers = vaultFilteredCiphers;
}
cts.Token.ThrowIfCancellationRequested();
}
else
catch (OperationCanceledException)
{
ciphers = vaultFilteredCiphers;
return;
}
cts.Token.ThrowIfCancellationRequested();
}
catch (OperationCanceledException)
if (ciphers == null)
{
return;
ciphers = new List<CipherView>();
}
MainThread.BeginInvokeOnMainThread(() =>
{
Ciphers.ResetWithRange(ciphers.Select(c => new CipherItemViewModel(c, _websiteIconsEnabled)).ToList());
ShowNoData = !shouldShowAllWhenEmpty && searchable && Ciphers.Count == 0;
ShowList = (searchable || shouldShowAllWhenEmpty) && !ShowNoData;
});
}
if (ciphers == null)
catch (Exception ex)
{
ciphers = new List<CipherView>();
_logger.Exception(ex);
}
MainThread.BeginInvokeOnMainThread(() =>
{
Ciphers.ResetWithRange(ciphers.Select(c => new CipherItemViewModel(c, _websiteIconsEnabled)).ToList());
ShowNoData = !shouldShowAllWhenEmpty && searchable && Ciphers.Count == 0;
ShowList = (searchable || shouldShowAllWhenEmpty) && !ShowNoData;
});
}, cts.Token);
_searchCancellationTokenSource = cts;
}

View File

@ -1,9 +1,9 @@
using Bit.App.Abstractions;
using Bit.App.Controls;
using Bit.Core.Resources.Localization;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Resources.Localization;
using Bit.Core.Services;
using Bit.Core.Utilities;
@ -19,6 +19,7 @@ namespace Bit.App.Pages
private readonly ICipherService _cipherService;
private readonly IDeviceActionService _deviceActionService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly ILogger _logger;
private readonly GroupingsPageViewModel _vm;
private readonly string _pageName;
@ -39,6 +40,8 @@ namespace Bit.App.Pages
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_logger = ServiceContainer.Resolve<ILogger>();
_vm = BindingContext as GroupingsPageViewModel;
_vm.Page = this;
_vm.MainPage = mainPage;
@ -57,17 +60,14 @@ namespace Bit.App.Pages
_vm.VaultFilterDescription = vaultFilterSelection;
}
if (DeviceInfo.Platform == DevicePlatform.iOS)
{
_absLayout.Children.Remove(_fab);
ToolbarItems.Add(_addItem);
}
else
{
ToolbarItems.Add(_syncItem);
ToolbarItems.Add(_lockItem);
ToolbarItems.Add(_exitItem);
}
#if IOS
_absLayout.Children.Remove(_fab);
ToolbarItems.Add(_addItem);
#else
ToolbarItems.Add(_syncItem);
ToolbarItems.Add(_lockItem);
ToolbarItems.Add(_exitItem);
#endif
if (deleted || showTotp)
{
_absLayout.Children.Remove(_fab);
@ -81,107 +81,115 @@ namespace Bit.App.Pages
protected override async void OnAppearing()
{
base.OnAppearing();
if (_syncService.SyncInProgress)
try
{
IsBusy = true;
}
_accountAvatar?.OnAppearing();
if (_vm.MainPage)
{
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
}
_broadcasterService.Subscribe(_pageName, async (message) =>
{
try
base.OnAppearing();
if (_syncService.SyncInProgress)
{
if (message.Command == "syncStarted")
IsBusy = true;
}
_accountAvatar?.OnAppearing();
if (_vm.MainPage)
{
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
}
_broadcasterService.Subscribe(_pageName, async (message) =>
{
try
{
MainThread.BeginInvokeOnMainThread(() => IsBusy = true);
}
else if (message.Command == "syncCompleted")
{
await Task.Delay(500);
if (_vm.MainPage)
if (message.Command == "syncStarted")
{
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
MainThread.BeginInvokeOnMainThread(() => IsBusy = true);
}
MainThread.BeginInvokeOnMainThread(() =>
else if (message.Command == "syncCompleted")
{
IsBusy = false;
if (_vm.LoadedOnce)
await Task.Delay(500);
if (_vm.MainPage)
{
var task = _vm.LoadAsync();
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
}
});
MainThread.BeginInvokeOnMainThread(() =>
{
IsBusy = false;
if (_vm.LoadedOnce)
{
var task = _vm.LoadAsync();
}
});
}
}
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
}
});
await LoadOnAppearedAsync(_mainLayout, false, async () =>
{
if (_previousPage == null)
{
if (!_syncService.SyncInProgress || (await _cipherService.GetAllAsync()).Any())
catch (Exception ex)
{
try
{
await _vm.LoadAsync();
}
catch (Exception e) when (e.Message.Contains("No key."))
{
await Task.Delay(1000);
await _vm.LoadAsync();
}
LoggerHelper.LogEvenIfCantBeResolved(ex);
}
else
});
await LoadOnAppearedAsync(_mainLayout, false, async () =>
{
if (_previousPage == null)
{
await Task.Delay(5000);
if (!_vm.Loaded)
if (!_syncService.SyncInProgress || (await _cipherService.GetAllAsync()).Any())
{
await _vm.LoadAsync();
try
{
await _vm.LoadAsync();
}
catch (Exception e) when (e.Message.Contains("No key."))
{
await Task.Delay(1000);
await _vm.LoadAsync();
}
}
else
{
await Task.Delay(5000);
if (!_vm.Loaded)
{
await _vm.LoadAsync();
}
}
}
}
await ShowPreviousPageAsync();
AdjustToolbar();
}, _mainContent);
await ShowPreviousPageAsync();
AdjustToolbar();
}, _mainContent);
if (!_vm.MainPage)
{
return;
}
// Push registration
var lastPushRegistration = await _stateService.GetPushLastRegistrationDateAsync();
lastPushRegistration = lastPushRegistration.GetValueOrDefault(DateTime.MinValue);
if (DeviceInfo.Platform == DevicePlatform.iOS)
{
var pushPromptShow = await _stateService.GetPushInitialPromptShownAsync();
if (!pushPromptShow.GetValueOrDefault(false))
if (!_vm.MainPage)
{
await _stateService.SetPushInitialPromptShownAsync(true);
await DisplayAlert(AppResources.EnableAutomaticSyncing, AppResources.PushNotificationAlert,
AppResources.OkGotIt);
return;
}
if (!pushPromptShow.GetValueOrDefault(false) ||
DateTime.UtcNow - lastPushRegistration > TimeSpan.FromDays(1))
// Push registration
var lastPushRegistration = await _stateService.GetPushLastRegistrationDateAsync();
lastPushRegistration = lastPushRegistration.GetValueOrDefault(DateTime.MinValue);
if (DeviceInfo.Platform == DevicePlatform.iOS)
{
await _pushNotificationService.RegisterAsync();
var pushPromptShow = await _stateService.GetPushInitialPromptShownAsync();
if (!pushPromptShow.GetValueOrDefault(false))
{
await _stateService.SetPushInitialPromptShownAsync(true);
await DisplayAlert(AppResources.EnableAutomaticSyncing, AppResources.PushNotificationAlert,
AppResources.OkGotIt);
}
if (!pushPromptShow.GetValueOrDefault(false) ||
DateTime.UtcNow - lastPushRegistration > TimeSpan.FromDays(1))
{
await _pushNotificationService.RegisterAsync();
}
}
else if (DeviceInfo.Platform == DevicePlatform.Android)
{
if (DateTime.UtcNow - lastPushRegistration > TimeSpan.FromDays(1))
{
await _pushNotificationService.RegisterAsync();
}
}
}
else if (DeviceInfo.Platform == DevicePlatform.Android)
catch (Exception ex)
{
if (DateTime.UtcNow - lastPushRegistration > TimeSpan.FromDays(1))
{
await _pushNotificationService.RegisterAsync();
}
_logger.Exception(ex);
throw;
}
}
@ -195,14 +203,22 @@ namespace Bit.App.Pages
return false;
}
protected override async void OnDisappearing()
protected override void OnDisappearing()
{
base.OnDisappearing();
IsBusy = false;
_vm.StopCiphersTotpTick().FireAndForget();
_broadcasterService.Unsubscribe(_pageName);
_vm.DisableRefreshing();
_accountAvatar?.OnDisappearing();
try
{
base.OnDisappearing();
IsBusy = false;
_vm.StopCiphersTotpTick().FireAndForget();
_broadcasterService.Unsubscribe(_pageName);
_vm.DisableRefreshing();
_accountAvatar?.OnDisappearing();
}
catch (Exception ex)
{
_logger.Exception(ex);
throw;
}
}
private async void RowSelected(object sender, SelectionChangedEventArgs e)
@ -264,45 +280,80 @@ namespace Bit.App.Pages
private async void Search_Clicked(object sender, EventArgs e)
{
await _accountListOverlay.HideAsync();
if (DoOnce())
try
{
var page = new CiphersPage(_vm.Filter, _vm.MainPage ? null : _vm.PageTitle, deleted: _vm.Deleted);
await Navigation.PushModalAsync(new NavigationPage(page));
await _accountListOverlay.HideAsync();
if (DoOnce())
{
var page = new CiphersPage(_vm.Filter, _vm.MainPage ? null : _vm.PageTitle, deleted: _vm.Deleted);
await Navigation.PushModalAsync(new NavigationPage(page));
}
}
catch (Exception ex)
{
_logger.Exception(ex);
}
}
private async void Sync_Clicked(object sender, EventArgs e)
{
await _accountListOverlay.HideAsync();
await _vm.SyncAsync();
try
{
await _accountListOverlay.HideAsync();
await _vm.SyncAsync();
}
catch (Exception ex)
{
_logger.Exception(ex);
}
}
private async void Lock_Clicked(object sender, EventArgs e)
{
await _accountListOverlay.HideAsync();
await _vaultTimeoutService.LockAsync(true, true);
try
{
await _accountListOverlay.HideAsync();
await _vaultTimeoutService.LockAsync(true, true);
}
catch (Exception ex)
{
_logger.Exception(ex);
}
}
private async void Exit_Clicked(object sender, EventArgs e)
{
await _accountListOverlay.HideAsync();
await _vm.ExitAsync();
try
{
await _accountListOverlay.HideAsync();
await _vm.ExitAsync();
}
catch (Exception ex)
{
_logger.Exception(ex);
}
}
private async void AddButton_Clicked(object sender, EventArgs e)
{
var skipAction = _accountListOverlay.IsVisible && DeviceInfo.Platform == DevicePlatform.Android;
await _accountListOverlay.HideAsync();
if (skipAction)
try
{
// Account list in the process of closing via tapping on invisible FAB, skip this attempt
return;
var skipAction = _accountListOverlay.IsVisible && DeviceInfo.Platform == DevicePlatform.Android;
await _accountListOverlay.HideAsync();
if (skipAction)
{
// Account list in the process of closing via tapping on invisible FAB, skip this attempt
return;
}
if (!_vm.Deleted && DoOnce())
{
var page = new CipherAddEditPage(null, _vm.Type, _vm.FolderId, _vm.CollectionId, _vm.GetVaultFilterOrgId());
await Navigation.PushModalAsync(new NavigationPage(page));
}
}
if (!_vm.Deleted && DoOnce())
catch (Exception ex)
{
var page = new CipherAddEditPage(null, _vm.Type, _vm.FolderId, _vm.CollectionId, _vm.GetVaultFilterOrgId());
await Navigation.PushModalAsync(new NavigationPage(page));
_logger.Exception(ex);
}
}

View File

@ -166,41 +166,52 @@ namespace Bit.App.Pages
{
return;
}
var authed = await _stateService.IsAuthenticatedAsync();
if (!authed)
{
return;
}
if (await _vaultTimeoutService.IsLockedAsync())
{
return;
}
if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing)
{
SyncRefreshing = true;
await _syncService.SyncPasswordlessLoginRequestsAsync();
await _syncService.FullSyncAsync(false);
return;
}
_deviceActionService.SetScreenCaptureAllowedAsync().FireAndForget();
await InitVaultFilterAsync(MainPage);
if (MainPage)
try
{
PageTitle = ShowVaultFilter ? AppResources.Vaults : AppResources.MyVault;
var authed = await _stateService.IsAuthenticatedAsync();
if (!authed)
{
return;
}
if (await _vaultTimeoutService.IsLockedAsync())
{
return;
}
if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing)
{
SyncRefreshing = true;
await _syncService.SyncPasswordlessLoginRequestsAsync();
await _syncService.FullSyncAsync(false);
return;
}
_deviceActionService.SetScreenCaptureAllowedAsync().FireAndForget();
await InitVaultFilterAsync(MainPage);
if (MainPage)
{
PageTitle = ShowVaultFilter ? AppResources.Vaults : AppResources.MyVault;
}
_doingLoad = true;
LoadedOnce = true;
ShowNoData = false;
Loading = true;
ShowList = false;
ShowAddCipherButton = !Deleted;
_websiteIconsEnabled = await _stateService.GetDisableFaviconAsync() != true;
}
catch (Exception ex)
{
_logger.Exception(ex);
throw;
}
_doingLoad = true;
LoadedOnce = true;
ShowNoData = false;
Loading = true;
ShowList = false;
ShowAddCipherButton = !Deleted;
var groupedItems = new List<GroupingsPageListGroup>();
var page = Page as GroupingsPage;
_websiteIconsEnabled = await _stateService.GetDisableFaviconAsync() != true;
try
{
await LoadDataAsync();
@ -307,11 +318,6 @@ namespace Bit.App.Pages
await MainThread.InvokeOnMainThreadAsync(() =>
{
#if IOS
// HACK: [PS-536] Fix to avoid blank list after back navigation on unlocking with previous page info
// because of update to XF v5.0.0.2401
GroupedItems.Clear();
#endif
GroupedItems.ReplaceRange(items);
});
}
@ -335,23 +341,22 @@ namespace Bit.App.Pages
await MainThread.InvokeOnMainThreadAsync(() =>
{
if (groupedItems.Any())
{
#if IOS
// HACK: [PS-536] Fix to avoid blank list after back navigation on unlocking with previous page info
// because of update to XF v5.0.0.2401
GroupedItems.Clear();
#endif
GroupedItems.ReplaceRange(new List<IGroupingsPageListItem> { new GroupingsPageHeaderListItem(groupedItems[0].Name, groupedItems[0].ItemCount) });
GroupedItems.AddRange(items);
}
else
if (!groupedItems.Any())
{
GroupedItems.Clear();
return;
}
GroupedItems.ReplaceRange(new List<IGroupingsPageListItem> { new GroupingsPageHeaderListItem(groupedItems[0].Name, groupedItems[0].ItemCount) });
GroupedItems.AddRange(items);
});
}
}
catch (Exception ex)
{
_logger.Exception(ex);
throw;
}
finally
{
_doingLoad = false;

View File

@ -42,7 +42,7 @@ namespace Bit.App.Pages
{
var cipher = await _cipherService.GetAsync(CipherId);
var decCipher = await cipher.DecryptAsync();
Device.BeginInvokeOnMainThread(() =>
MainThread.BeginInvokeOnMainThread(() =>
{
History.ResetWithRange(decCipher.PasswordHistory ?? new List<PasswordHistoryView>());
ShowNoData = History.Count == 0;

View File

@ -784,13 +784,13 @@
<value>Avto-doldurmaq və ya bu elementə baxmaq istəyirsiniz?</value>
</data>
<data name="BitwardenAutofillServiceMatchConfirm" xml:space="preserve">
<value>Bu elementi avto-doldurmaq istədiyinizə əminsiniz? "{0}" ilə tam uyğun gəlmir.</value>
<value>Bu elementi avto-doldurmaq istədiyinizə əminsiniz? "{0}" ilə tam uyuşmur.</value>
</data>
<data name="MatchingItems" xml:space="preserve">
<value>Uyğun gələnlər</value>
<value>Uyuşan elementlər</value>
</data>
<data name="PossibleMatchingItems" xml:space="preserve">
<value>Uyğun gələ bilənlər</value>
<value>Uyuşması mümkün olan elementlər</value>
</data>
<data name="Search" xml:space="preserve">
<value>Axtar</value>
@ -1246,10 +1246,10 @@ Skan prosesi avtomatik baş tutacaq.</value>
<value>Başlayır</value>
</data>
<data name="URIMatchDetection" xml:space="preserve">
<value>URI uyğunluq aşkarlaması</value>
<value>URI uyuşma aşkarlaması</value>
</data>
<data name="MatchDetection" xml:space="preserve">
<value>Uyğunluq aşkarlaması</value>
<value>Uyuşmanı aşkarlama</value>
<comment>URI match detection for auto-fill.</comment>
</data>
<data name="YesAndSave" xml:space="preserve">
@ -1530,11 +1530,11 @@ Skan prosesi avtomatik baş tutacaq.</value>
<comment>Clipboard is the operating system thing where you copy/paste data to on your device.</comment>
</data>
<data name="DefaultUriMatchDetection" xml:space="preserve">
<value>İlkin URI uyğunluq aşkarlaması</value>
<value>İlkin URI uyuşma aşkarlaması</value>
<comment>Default URI match detection for auto-fill.</comment>
</data>
<data name="DefaultUriMatchDetectionDescription" xml:space="preserve">
<value>Avto-doldurma kimi əməliyyatları icra edərkən giriş etmə prosesi üçün URI uyğunluq aşkarlamasının ilkin yolunu seçin.</value>
<value>Avto-doldurma kimi əməliyyatları icra edərkən giriş etmə prosesi üçün URI uyuşma aşkarlamasının idarə edliəcəyi ilkin yolu seçin.</value>
</data>
<data name="Theme" xml:space="preserve">
<value>Tema</value>
@ -2464,7 +2464,7 @@ Skan prosesi avtomatik baş tutacaq.</value>
<value>Əlçatımlılıq Xidməti açıqlaması</value>
</data>
<data name="AccessibilityDisclosureText" xml:space="preserve">
<value>Bitwarden, tətbiqlərdə və veb saytlarda giriş sahələrini axtarmaq üçün Əlçatımlılıq Xidmətini istifadə edir, daha sonra tətbiq və ya sayt üçün uyğunluq aşkar etdikdə istifadəçi adı və parolun daxil edilməsi üçün müvafiq sahə kimliklərini yaradır. Xidmət tərəfindən bizə təqdim edilən məlumatların heç birini saxlamırıq, kimlik məlumatlarının daxil edilməsindən kənar ekrandakı hər hansısa elementə nəzarət etməyə cəhd etmirik.</value>
<value>Bitwarden, tətbiqlərdə və veb saytlarda giriş sahələrini axtarmaq üçün Əlçatımlılıq Xidmətini istifadə edir, daha sonra tətbiq və ya sayt üçün uyuşma tapdıqda istifadəçi adı və parolun daxil edilməsi üçün müvafiq sahə kimliklərini yaradır. Xidmət tərəfindən bizə təqdim edilən məlumatların heç birini saxlamırıq, kimlik məlumatlarının daxil edilməsindən kənar ekrandakı hər hansısa elementə nəzarət etməyə cəhd etmirik.</value>
</data>
<data name="Accept" xml:space="preserve">
<value>Qəbul et</value>
@ -2505,7 +2505,7 @@ Bu hesaba keçmək istəyirsiniz?</value>
<value>Cihazınıza bir bildiriş göndərildi.</value>
</data>
<data name="PleaseMakeSureYourVaultIsUnlockedAndTheFingerprintPhraseMatchesOnTheOtherDevice" xml:space="preserve">
<value>Lütfən anbarınızın kilidinin açıq olduğuna və Barmaq izi ifadəsinin digər cihazda uyğun gəldiyinə əmin olun.</value>
<value>Lütfən anbarınızın kilidinin açıq olduğuna və Barmaq izi ifadəsinin digər cihazla uyuşduğuna əmin olun.</value>
</data>
<data name="ResendNotification" xml:space="preserve">
<value>Bildirişi təkrar göndər</value>
@ -2592,13 +2592,13 @@ Bu hesaba keçmək istəyirsiniz?</value>
<value>Açarı, mövcud və ya yeni bir elementə əlavə edin</value>
</data>
<data name="ThereAreNoItemsInYourVaultThatMatchX" xml:space="preserve">
<value>Anbarınızda {0} ilə uyğunlaşan heç bir element yoxdur</value>
<value>Anbarınızda {0} ilə uyuşan heç bir element yoxdur</value>
</data>
<data name="SearchForAnItemOrAddANewItem" xml:space="preserve">
<value>Bir element axtarın və ya yenisini əlavə edin</value>
</data>
<data name="ThereAreNoItemsThatMatchTheSearch" xml:space="preserve">
<value>Axtarışa uyğun gələn heç bir element yoxdur</value>
<value>Axtarışla uyuşan heç bir element yoxdur</value>
</data>
<data name="US" xml:space="preserve">
<value>ABŞ</value>

View File

@ -878,17 +878,17 @@
<comment>The placeholder will show the file size of the attachment. Ex "25 MB"</comment>
</data>
<data name="AuthenticatorKey" xml:space="preserve">
<value>Todennusmenetelmän avain (TOTP)</value>
<value>Todennusavain (TOTP)</value>
</data>
<data name="VerificationCodeTotp" xml:space="preserve">
<value>Todennuskoodi (TOTP)</value>
<comment>Totp code label</comment>
</data>
<data name="AuthenticatorKeyAdded" xml:space="preserve">
<value>Todennusmenetelmän avain lisätty.</value>
<value>Todennusavain on lisätty.</value>
</data>
<data name="AuthenticatorKeyReadError" xml:space="preserve">
<value>Todennusavaimen luku epäonnistui.</value>
<value>Todennusavaimen lukeminen ei onnistu.</value>
</data>
<data name="PointYourCameraAtTheQRCode" xml:space="preserve">
<value>Kohdista kamera QR-koodiin.
@ -907,7 +907,7 @@ Koodi skannataan automaattisesti.</value>
<value>Kopioi TOTP-todennuskoodi</value>
</data>
<data name="CopyTotpAutomaticallyDescription" xml:space="preserve">
<value>Jos kirjautumistieto sisältää kaksivaiheisen TOTP-todennusavaimen, kopioidaan todennuskoodi automaattisesti leikepöydälle automaattisen täytön yhteydessä.</value>
<value>Jos kirjautumistieto sisältää kaksivaiheisen todennuksen avaimen, kopioidaan TOTP-todennuskoodi leikepöydälle kohteen automaattisen täytön yhteydessä.</value>
</data>
<data name="CopyTotpAutomatically" xml:space="preserve">
<value>TOTP-koodin kopiointi</value>
@ -1853,7 +1853,7 @@ Koodi skannataan automaattisesti.</value>
<value>Jos käytössä, esteettömyyspalvelu näyttää pikavalintapalkin laajentaakseen automaattisen täytön palvelun toiminnan kattamaan myös vanhemmat sovellukset, jotka eivät tue Android Autofill Framework -rajapintaa.</value>
</data>
<data name="PersonalOwnershipSubmitError" xml:space="preserve">
<value>Yrityskäytännön johdosta kohteiden tallennus henkilökohtaiseen holviin ei ole mahdollista. Muuta omistusasetus organisaatiolle ja valitse käytettävissä olevista kokoelmista.</value>
<value>Yrityskäytännön vuoksi kohteiden tallennus henkilökohtaiseen holviin ei ole mahdollista. Muuta omistusasetus organisaatiolle ja valitse käytettävissä olevista kokoelmista.</value>
</data>
<data name="PersonalOwnershipPolicyInEffect" xml:space="preserve">
<value>Organisaatiokäytäntö vaikuttaa omistajuusvalintoihisi.</value>
@ -2126,7 +2126,7 @@ Koodi skannataan automaattisesti.</value>
<value>Varmista, että oletusselaimesi sisältää WebAuthn-tuen ja yritä uudelleen.</value>
</data>
<data name="ResetPasswordAutoEnrollInviteWarning" xml:space="preserve">
<value>Organisaatiolla on käytäntö, joka liittää tilisi automaattisesti salasanan palautusapuun. Liitos sallii organisaation ylläpitäjien vaihtaa pääsalasanasi.</value>
<value>Organisaatiolla on yrityskäytäntö, joka liittää tilisi automaattisesti salasanan palautusapuun. Liitos sallii organisaation ylläpitäjien vaihtaa pääsalasanasi.</value>
</data>
<data name="VaultTimeoutPolicyInEffect" xml:space="preserve">
<value>Organisaatiokäytännöt ovat määrittäneet holvisi aikakatkaisun enimmäisajaksi {0} tunti(a) {1} minuutti(a).</value>
@ -2273,7 +2273,7 @@ Koodi skannataan automaattisesti.</value>
<value>Premium-tilaus vaaditaan</value>
</data>
<data name="CannotAddAuthenticatorKey" xml:space="preserve">
<value>Etkö voi lisätä todennusavainta? </value>
<value>Eikä todennusavaimen lisääminen onnistu?</value>
</data>
<data name="ScanQRCode" xml:space="preserve">
<value>Skannaa QR-koodi</value>
@ -2282,7 +2282,7 @@ Koodi skannataan automaattisesti.</value>
<value>Eikö QR-koodin skannaus onnistu? </value>
</data>
<data name="AuthenticatorKeyScanner" xml:space="preserve">
<value>Todentajan avain</value>
<value>Todennusavain</value>
</data>
<data name="EnterKeyManually" xml:space="preserve">
<value>Syötä se manuaalisesti</value>

File diff suppressed because it is too large Load Diff

View File

@ -885,7 +885,7 @@
<comment>Totp code label</comment>
</data>
<data name="AuthenticatorKeyAdded" xml:space="preserve">
<value>Chave de autenticador adicionada.</value>
<value>Chave de autenticação adicionada.</value>
</data>
<data name="AuthenticatorKeyReadError" xml:space="preserve">
<value>Não é possível ler a chave de autenticação.</value>

View File

@ -885,10 +885,10 @@
<comment>Totp code label</comment>
</data>
<data name="AuthenticatorKeyAdded" xml:space="preserve">
<value>Ключ проверки подлинности добавлен.</value>
<value>Ключ аутентификатора добавлен.</value>
</data>
<data name="AuthenticatorKeyReadError" xml:space="preserve">
<value>Не удается прочитать ключ проверки подлинности.</value>
<value>Не удается прочитать ключ аутентификатора.</value>
</data>
<data name="PointYourCameraAtTheQRCode" xml:space="preserve">
<value>Наведите камеру на QR-код.
@ -2272,7 +2272,7 @@
<value>Требуется подписка Премиум</value>
</data>
<data name="CannotAddAuthenticatorKey" xml:space="preserve">
<value>Не удается добавить ключ проверки подлинности? </value>
<value>Не удается добавить ключ аутентификатора? </value>
</data>
<data name="ScanQRCode" xml:space="preserve">
<value>Сканировать QR-код</value>

View File

@ -858,7 +858,7 @@
<comment>"YubiKey" is the product name and should not be translated.</comment>
</data>
<data name="AddNewAttachment" xml:space="preserve">
<value>புதிய இணைப்பை சேர்</value>
<value>புதிய இணைப்பைச் சேர்</value>
</data>
<data name="Attachments" xml:space="preserve">
<value>இணைப்புகள்</value>
@ -2331,7 +2331,7 @@
<value>நேரம்</value>
</data>
<data name="Near" xml:space="preserve">
<value>Near</value>
<value>இதற்கருகில்</value>
</data>
<data name="ConfirmLogIn" xml:space="preserve">
<value>உள்நுழைவை உறுதிபடுத்து</value>
@ -2772,7 +2772,7 @@
<value>இப்போது ஒத்திசை</value>
</data>
<data name="UnlockOptions" xml:space="preserve">
<value>Unlock options</value>
<value>பூட்டவிழ்ப்பு விருப்பங்கள்</value>
</data>
<data name="SessionTimeout" xml:space="preserve">
<value>அமர்வு நேரமுடிவு</value>
@ -2785,16 +2785,16 @@
<comment>A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing.</comment>
</data>
<data name="OneHourAndOneMinute" xml:space="preserve">
<value>One hour and one minute</value>
<value>ஒரு மணிநேரம் மற்றும் ஒரு நிமிடம்</value>
</data>
<data name="OneHourAndXMinute" xml:space="preserve">
<value>One hour and {0} minutes</value>
<value>ஒரு மணிநேரம் மற்றும் {0} நிமிடங்கள்</value>
</data>
<data name="XHoursAndOneMinute" xml:space="preserve">
<value>{0} hours and one minute</value>
<value>{0} மணிநேரம் மற்றும் ஒரு நிமிடம்</value>
</data>
<data name="XHoursAndYMinutes" xml:space="preserve">
<value>{0} hours and {1} minutes</value>
<value>{0} மணிநேரம் மற்றும் {1} நிமிடங்கள்</value>
</data>
<data name="XHours" xml:space="preserve">
<value>{0} மணிநேரம்</value>
@ -2809,29 +2809,29 @@
<value>கூடுதல் தேர்வுகள்</value>
</data>
<data name="ContinueToWebApp" xml:space="preserve">
<value>Continue to web app?</value>
<value>வலைச்செயலிக்குத் தொடரவா?</value>
</data>
<data name="ContinueToX" xml:space="preserve">
<value>Continue to {0}?</value>
<value>{0}-க்குத் தொடரவா?</value>
<comment>The parameter is an URL, like bitwarden.com.</comment>
</data>
<data name="ContinueToHelpCenter" xml:space="preserve">
<value>Continue to Help center?</value>
<value>உதவி மையத்திற்குத் தொடரவா?</value>
</data>
<data name="ContinueToContactSupport" xml:space="preserve">
<value>Continue to contact support?</value>
<value>ஆதரவைத் தொடர்புகொள்ளத் தொடரவா?</value>
</data>
<data name="ContinueToAppStore" xml:space="preserve">
<value>Continue to app store?</value>
</data>
<data name="TwoStepLoginDescriptionLong" xml:space="preserve">
<value>Make your account more secure by setting up two-step login in the Bitwarden web app.</value>
<value>Bitwarden வலைச்செயலியில் இரு-படி உள்நுழைவை அமைத்து உமது கணக்கின் பாதுகாப்பை அதிகரி.</value>
</data>
<data name="ChangeMasterPasswordDescriptionLong" xml:space="preserve">
<value>You can change your master password on the Bitwarden web app.</value>
<value>Bitwarden வலைச்செயலியில் உமது பிரதான கடவுச்சொல்லை மாற்றலாம்.</value>
</data>
<data name="YouCanImportDataToYourVaultOnX" xml:space="preserve">
<value>You can import data to your vault on {0}.</value>
<value>{0}-இல் உமது பெட்டகத்திற்குத் தரவை இறக்குமதி செய்யலாம்.</value>
<comment>The parameter is an URL, like vault.bitwarden.com.</comment>
</data>
<data name="LearnMoreAboutHowToUseBitwardenOnTheHelpCenter" xml:space="preserve">

View File

@ -553,7 +553,7 @@
<value>Дія після часу очікування сховища</value>
</data>
<data name="VaultTimeoutLogOutConfirmation" xml:space="preserve">
<value>Вихід скасує всі права доступу до вашого сховища і вимагатиме авторизації після завершення часу очікування. Ви дійсно хочете використати цей параметр?</value>
<value>Вихід скасує всі права доступу до вашого сховища і вимагатиме автентифікацію після завершення часу очікування. Ви дійсно хочете використати цей параметр?</value>
</data>
<data name="LoggingIn" xml:space="preserve">
<value>Вхід...</value>
@ -721,7 +721,7 @@
<value>Веб сховище Bitwarden</value>
</data>
<data name="Lost2FAApp" xml:space="preserve">
<value>Втратили доступ до програми перевірки?</value>
<value>Втратили доступ до програми автентифікації?</value>
</data>
<data name="Items" xml:space="preserve">
<value>Записи</value>
@ -806,11 +806,11 @@
<comment>Message shown when trying to launch an app that does not exist on the user's device.</comment>
</data>
<data name="AuthenticatorAppTitle" xml:space="preserve">
<value>Програма авторизації</value>
<value>Програма автентифікації</value>
<comment>For 2FA</comment>
</data>
<data name="EnterVerificationCodeApp" xml:space="preserve">
<value>Введіть 6-значний код підтвердження з програми авторизації.</value>
<value>Введіть 6-значний код підтвердження з програми автентифікації.</value>
<comment>For 2FA</comment>
</data>
<data name="EnterVerificationCodeEmail" xml:space="preserve">
@ -822,7 +822,7 @@
<comment>For 2FA whenever there are no available providers on this device.</comment>
</data>
<data name="NoTwoStepAvailable" xml:space="preserve">
<value>Цей обліковий запис має увімкнену двоетапну перевірку, однак, жоден із налаштованих провайдерів не підтримується на цьому пристрої. Будь ласка, скористайтеся підтримуваним пристроєм або додайте інших провайдерів, які мають кращу підтримку різних пристроїв (наприклад, програму авторизації).</value>
<value>Цей обліковий запис має увімкнену двоетапну перевірку, однак жоден із налаштованих провайдерів не підтримується на цьому пристрої. Будь ласка, скористайтеся підтримуваним пристроєм або додайте інших провайдерів, які мають кращу підтримку різних пристроїв (наприклад, програму автентифікації).</value>
</data>
<data name="RecoveryCodeTitle" xml:space="preserve">
<value>Код відновлення</value>
@ -878,17 +878,17 @@
<comment>The placeholder will show the file size of the attachment. Ex "25 MB"</comment>
</data>
<data name="AuthenticatorKey" xml:space="preserve">
<value>Ключ авторизації (TOTP)</value>
<value>Ключ автентифікації (TOTP)</value>
</data>
<data name="VerificationCodeTotp" xml:space="preserve">
<value>Код підтвердження (TOTP)</value>
<comment>Totp code label</comment>
</data>
<data name="AuthenticatorKeyAdded" xml:space="preserve">
<value>Ключ авторизації додано.</value>
<value>Ключ автентифікації додано.</value>
</data>
<data name="AuthenticatorKeyReadError" xml:space="preserve">
<value>Не вдається прочитати ключ авторизації.</value>
<value>Не вдається прочитати ключ автентифікації.</value>
</data>
<data name="PointYourCameraAtTheQRCode" xml:space="preserve">
<value>Наведіть свою камеру на QR-код.
@ -907,7 +907,7 @@
<value>Копіювати TOTP</value>
</data>
<data name="CopyTotpAutomaticallyDescription" xml:space="preserve">
<value>Якщо запис має ключ авторизації, копіювати код підтвердження TOTP до буфера обміну під час автозаповнення.</value>
<value>Якщо запис має ключ автентифікації, копіювати код підтвердження TOTP до буфера обміну під час автозаповнення.</value>
</data>
<data name="CopyTotpAutomatically" xml:space="preserve">
<value>Автоматично копіювати коди TOTP</value>
@ -2110,13 +2110,13 @@
<value>FIDO2 WebAuthn</value>
</data>
<data name="Fido2Instruction" xml:space="preserve">
<value>Щоб продовжити, приготуйте свій увімкнений ключ безпеки FIDO2 WebAuthn, потім дотримуйтесь настанов після натискання "Авторизація WebAuthn" на наступному екрані.</value>
<value>Щоб продовжити, приготуйте свій ключ безпеки FIDO2 WebAuthn, потім дотримуйтесь настанов після натискання "Автентифікація WebAuthn" на наступному екрані.</value>
</data>
<data name="Fido2Desc" xml:space="preserve">
<value>Авторизація з використанням FIDO2 WebAuthn. Ви можете авторизуватися за допомогою зовнішнього ключа безпеки.</value>
<value>Автентифікація з використанням FIDO2 WebAuthn. Ви можете пройти автентифікацію за допомогою зовнішнього ключа безпеки.</value>
</data>
<data name="Fido2AuthenticateWebAuthn" xml:space="preserve">
<value>Авторизація WebAuthn</value>
<value>Автентифікація WebAuthn</value>
</data>
<data name="Fido2ReturnToApp" xml:space="preserve">
<value>Повернутися до програми</value>
@ -2272,7 +2272,7 @@
<value>Необхідна передплата преміум</value>
</data>
<data name="CannotAddAuthenticatorKey" xml:space="preserve">
<value>Не вдається додати ключ авторизації? </value>
<value>Не вдається додати ключ автентифікації? </value>
</data>
<data name="ScanQRCode" xml:space="preserve">
<value>Сканувати QR-код</value>
@ -2281,7 +2281,7 @@
<value>Не вдається сканувати QR-код? </value>
</data>
<data name="AuthenticatorKeyScanner" xml:space="preserve">
<value>Ключ авторизації</value>
<value>Ключ автентифікації</value>
</data>
<data name="EnterKeyManually" xml:space="preserve">
<value>Введіть ключ вручну</value>

View File

@ -885,17 +885,17 @@
<comment>Totp code label</comment>
</data>
<data name="AuthenticatorKeyAdded" xml:space="preserve">
<value>验证器密钥已添加。</value>
<value>已添加验证器密钥。</value>
</data>
<data name="AuthenticatorKeyReadError" xml:space="preserve">
<value>无法读取验证器密钥。</value>
</data>
<data name="PointYourCameraAtTheQRCode" xml:space="preserve">
<value>将您的摄像头对准 QR 码。
<value>将您的摄像头对准二维码。
扫描将自动完成。</value>
</data>
<data name="ScanQrTitle" xml:space="preserve">
<value>扫描 QR 码</value>
<value>扫描二维码</value>
</data>
<data name="Camera" xml:space="preserve">
<value>相机</value>
@ -2275,10 +2275,10 @@
<value>无法添加验证器密钥?</value>
</data>
<data name="ScanQRCode" xml:space="preserve">
<value>扫描 QR 码</value>
<value>扫描二维码</value>
</data>
<data name="CannotScanQRCode" xml:space="preserve">
<value>无法扫描 QR 码吗? </value>
<value>无法扫描二维码吗? </value>
</data>
<data name="AuthenticatorKeyScanner" xml:space="preserve">
<value>验证器密钥</value>

View File

@ -631,7 +631,7 @@ namespace Bit.Core.Services
public async Task<SsoPrevalidateResponse> PreValidateSsoAsync(string identifier)
{
var path = "/account/prevalidate?domainHint=" + WebUtility.UrlEncode(identifier);
var path = "/sso/prevalidate?domainHint=" + WebUtility.UrlEncode(identifier);
using (var requestMessage = new HttpRequestMessage())
{
requestMessage.Version = new Version(1, 0);

View File

@ -1,51 +0,0 @@
#if ENABLED_TAP_GESTURE_RECOGNIZER_MAUI_EMBEDDED_WORKAROUND
using System;
using AuthenticationServices;
using Bit.App.Abstractions;
using Bit.Core.Utilities;
using Bit.iOS.Autofill.Models;
using Bit.iOS.Core.Utilities;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform;
using UIKit;
namespace Bit.iOS.Autofill
{
public partial class CredentialProviderViewController : ASCredentialProviderViewController, IAccountsManagerHost
{
const string STORYBOARD_NAME = "MainInterface";
Lazy<UIStoryboard> _storyboard = new Lazy<UIStoryboard>(() => UIStoryboard.FromName(STORYBOARD_NAME, null));
public void InitWithContext(Context context)
{
_context = context;
}
public void DismissLockAndContinue()
{
if (UIApplication.SharedApplication.KeyWindow is null)
{
return;
}
UIApplication.SharedApplication.KeyWindow.RootViewController = _storyboard.Value.InstantiateInitialViewController();
if (UIApplication.SharedApplication.KeyWindow?.RootViewController is CredentialProviderViewController cpvc)
{
cpvc.InitWithContext(_context);
cpvc.OnLockDismissedAsync().FireAndForget();
}
}
private void NavigateToPage(ContentPage page)
{
var navigationPage = new NavigationPage(page);
var window = new Window(navigationPage);
window.ToHandler(MauiContextSingleton.Instance.MauiContext);
}
}
}
#endif

View File

@ -17,6 +17,8 @@ using CoreFoundation;
using CoreNFC;
using Foundation;
using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform;
using UIKit;
namespace Bit.iOS.Autofill
@ -313,7 +315,6 @@ namespace Bit.iOS.Autofill
}
}
#if !ENABLED_TAP_GESTURE_RECOGNIZER_MAUI_EMBEDDED_WORKAROUND
public async void DismissLockAndContinue()
{
DismissViewController(false, async () => await OnLockDismissedAsync());
@ -327,7 +328,6 @@ namespace Bit.iOS.Autofill
PresentViewController(uiController, true, null);
}
#endif
public async Task OnLockDismissedAsync()
{

View File

@ -11,7 +11,7 @@
<key>CFBundleIdentifier</key>
<string>com.8bit.bitwarden.autofill</string>
<key>CFBundleShortVersionString</key>
<string>2024.1.0</string>
<string>2024.1.1</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CFBundleLocalizations</key>

View File

@ -10,7 +10,7 @@
<EnableDefaultCompileItems>False</EnableDefaultCompileItems>
<DefineConstants>$(DefineConstants);ENABLED_TAP_GESTURE_RECOGNIZER_MAUI_EMBEDDED_WORKAROUND</DefineConstants>
<DefineConstants>$(DefineConstants)</DefineConstants>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">12.0</SupportedOSPlatformVersion>
</PropertyGroup>
@ -83,7 +83,6 @@
<Compile Include="Models\Context.cs" />
<BundleResource Include="Resources\MaterialIcons_Regular.ttf" />
<BundleResource Include="Resources\bwi-font.ttf" />
<Compile Include="CredentialProviderViewController.TapGestureHack.cs" />
<Compile Include="CredentialProviderViewController.Passkeys.cs" />
</ItemGroup>
<ItemGroup>
@ -123,13 +122,13 @@
<BundleResource Include="Resources\logo_white%403x.png" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="..\iOS\Resources\more_vert.png">
<BundleResource Include="..\App\Platforms\iOS\Resources\more_vert.png">
<Link>Resources\more_vert.png</Link>
</BundleResource>
<BundleResource Include="..\iOS\Resources\more_vert%402x.png">
<BundleResource Include="..\App\Platforms\iOS\Resources\more_vert%402x.png">
<Link>Resources\more_vert%402x.png</Link>
</BundleResource>
<BundleResource Include="..\iOS\Resources\more_vert%403x.png">
<BundleResource Include="..\App\Platforms\iOS\Resources\more_vert%403x.png">
<Link>Resources\more_vert%403x.png</Link>
</BundleResource>
</ItemGroup>

View File

@ -204,7 +204,18 @@ namespace Bit.iOS.Core.Controllers
var tasks = Task.Run(async () =>
{
await Task.Delay(500);
NSRunLoop.Main.BeginInvokeOnMainThread(async () => await PromptBiometricAsync());
NSRunLoop.Main.BeginInvokeOnMainThread(async () =>
{
try
{
await PromptBiometricAsync();
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
throw;
}
});
});
}
}

View File

@ -31,7 +31,7 @@ namespace Bit.iOS.Core.Services
var dictArr = new NSDictionary<NSString, NSObject>[1];
dictArr[0] = new NSDictionary<NSString, NSObject>(new NSString(UTType.UTF8PlainText), new NSString(text));
Device.BeginInvokeOnMainThread(() => UIPasteboard.General.SetItems(dictArr, new UIPasteboardOptions
MainThread.BeginInvokeOnMainThread(() => UIPasteboard.General.SetItems(dictArr, new UIPasteboardOptions
{
LocalOnly = true,
ExpirationDate = clearSeconds > 0 ? NSDate.FromTimeIntervalSinceNow(clearSeconds) : null

View File

@ -11,7 +11,7 @@
<key>CFBundleIdentifier</key>
<string>com.8bit.bitwarden.find-login-action-extension</string>
<key>CFBundleShortVersionString</key>
<string>2024.1.0</string>
<string>2024.1.1</string>
<key>CFBundleLocalizations</key>
<array>
<string>en</string>

View File

@ -100,13 +100,13 @@
<BundleResource Include="Resources\yubikey.png" />
<BundleResource Include="Resources\yubikey%403x.png" />
<BundleResource Include="Resources\yubikey%402x.png" />
<BundleResource Include="..\iOS\Resources\more_vert.png">
<BundleResource Include="..\App\Platforms\iOS\Resources\more_vert.png">
<Link>Resources\more_vert.png</Link>
</BundleResource>
<BundleResource Include="..\iOS\Resources\more_vert%402x.png">
<BundleResource Include="..\App\Platforms\iOS\Resources\more_vert%402x.png">
<Link>Resources\more_vert%402x.png</Link>
</BundleResource>
<BundleResource Include="..\iOS\Resources\more_vert%403x.png">
<BundleResource Include="..\App\Platforms\iOS\Resources\more_vert%403x.png">
<Link>Resources\more_vert%403x.png</Link>
</BundleResource>
</ItemGroup>

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>2024.1.0</string>
<string>2024.1.1</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>MinimumOSVersion</key>

View File

@ -70,37 +70,37 @@
<Compile Include="LoadingViewController.TapGestureHack.cs" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="..\iOS\Resources\logo_white%403x.png">
<BundleResource Include="..\App\Platforms\iOS\Resources\logo_white%403x.png">
<Link>Resources\logo_white%403x.png</Link>
</BundleResource>
<BundleResource Include="..\iOS\Resources\logo_white%402x.png">
<BundleResource Include="..\App\Platforms\iOS\Resources\logo_white%402x.png">
<Link>Resources\logo_white%402x.png</Link>
</BundleResource>
<BundleResource Include="..\iOS\Resources\logo_white.png">
<BundleResource Include="..\App\Platforms\iOS\Resources\logo_white.png">
<Link>Resources\logo_white.png</Link>
</BundleResource>
<BundleResource Include="..\iOS\Resources\logo%403x.png">
<BundleResource Include="..\App\Platforms\iOS\Resources\logo%403x.png">
<Link>Resources\logo%403x.png</Link>
</BundleResource>
<BundleResource Include="..\iOS\Resources\logo%402x.png">
<BundleResource Include="..\App\Platforms\iOS\Resources\logo%402x.png">
<Link>Resources\logo%402x.png</Link>
</BundleResource>
<BundleResource Include="..\iOS\Resources\logo.png">
<BundleResource Include="..\App\Platforms\iOS\Resources\logo.png">
<Link>Resources\logo.png</Link>
</BundleResource>
<BundleResource Include="..\iOS\Resources\more_vert.png">
<BundleResource Include="..\App\Platforms\iOS\Resources\more_vert.png">
<Link>Resources\more_vert.png</Link>
</BundleResource>
<BundleResource Include="..\iOS\Resources\more_vert%402x.png">
<BundleResource Include="..\App\Platforms\iOS\Resources\more_vert%402x.png">
<Link>Resources\more_vert%402x.png</Link>
</BundleResource>
<BundleResource Include="..\iOS\Resources\more_vert%403x.png">
<BundleResource Include="..\App\Platforms\iOS\Resources\more_vert%403x.png">
<Link>Resources\more_vert%403x.png</Link>
</BundleResource>
<BundleResource Include="..\iOS\Resources\bwi-font.ttf">
<BundleResource Include="..\Core\Resources\Fonts\bwi-font.ttf">
<Link>Resources\bwi-font.ttf</Link>
</BundleResource>
<BundleResource Include="..\iOS\Resources\MaterialIcons_Regular.ttf">
<BundleResource Include="..\Core\Resources\Fonts\MaterialIcons_Regular.ttf">
<Link>Resources\MaterialIcons_Regular.ttf</Link>
</BundleResource>
</ItemGroup>

View File

@ -1,413 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AuthenticationServices;
using Bit.App.Abstractions;
using Bit.App.Pages;
using Bit.App.Services;
using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Bit.iOS.Core.Services;
using Bit.iOS.Core.Utilities;
using Bit.iOS.Services;
using CoreNFC;
using Foundation;
using UIKit;
using WatchConnectivity;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
namespace Bit.iOS
{
[Register("AppDelegate")]
public partial class AppDelegate : FormsApplicationDelegate
{
const int SPLASH_VIEW_TAG = 4321;
private NFCNdefReaderSession _nfcSession = null;
private iOSPushNotificationHandler _pushHandler = null;
private Core.NFCReaderDelegate _nfcDelegate = null;
private NSTimer _clipboardTimer = null;
private nint _clipboardBackgroundTaskId;
private NSTimer _eventTimer = null;
private nint _eventBackgroundTaskId;
private IDeviceActionService _deviceActionService;
private IMessagingService _messagingService;
private IBroadcasterService _broadcasterService;
private IStorageService _storageService;
private IStateService _stateService;
private IEventService _eventService;
private LazyResolve<IDeepLinkContext> _deepLinkContext = new LazyResolve<IDeepLinkContext>();
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
Forms.Init();
InitApp();
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_eventService = ServiceContainer.Resolve<IEventService>("eventService");
LoadApplication(new App.App(null));
iOSCoreHelpers.AppearanceAdjustments();
ZXing.Net.Mobile.Forms.iOS.Platform.Init();
ConnectToWatchIfNeededAsync().FireAndForget();
_broadcasterService.Subscribe(nameof(AppDelegate), async (message) =>
{
try
{
if (message.Command == "startEventTimer")
{
StartEventTimer();
}
else if (message.Command == "stopEventTimer")
{
var task = StopEventTimerAsync();
}
else if (message.Command is ThemeManager.UPDATED_THEME_MESSAGE_KEY)
{
Device.BeginInvokeOnMainThread(() =>
{
iOSCoreHelpers.AppearanceAdjustments();
});
}
else if (message.Command == "listenYubiKeyOTP")
{
iOSCoreHelpers.ListenYubiKey((bool)message.Data, _deviceActionService, _nfcSession, _nfcDelegate);
}
else if (message.Command == "unlocked")
{
var needsAutofillReplacement = await _storageService.GetAsync<bool?>(
Core.Constants.AutofillNeedsIdentityReplacementKey);
if (needsAutofillReplacement.GetValueOrDefault())
{
await ASHelpers.ReplaceAllIdentities();
}
}
else if (message.Command == "showAppExtension")
{
Device.BeginInvokeOnMainThread(() => ShowAppExtension((ExtensionPageViewModel)message.Data));
}
else if (message.Command == "syncCompleted")
{
if (message.Data is Dictionary<string, object> data && data.ContainsKey("successfully"))
{
var success = data["successfully"] as bool?;
if (success.GetValueOrDefault() && _deviceActionService.SystemMajorVersion() >= 12)
{
await ASHelpers.ReplaceAllIdentities();
}
}
}
else if (message.Command == "addedCipher" || message.Command == "editedCipher" ||
message.Command == "restoredCipher")
{
if (_deviceActionService.SystemMajorVersion() >= 12)
{
if (await ASHelpers.IdentitiesCanIncremental())
{
var cipherId = message.Data as string;
if (message.Command == "addedCipher" && !string.IsNullOrWhiteSpace(cipherId))
{
var identity = await ASHelpers.GetCipherIdentityAsync(cipherId);
if (identity == null)
{
return;
}
await ASCredentialIdentityStore.SharedStore?.SaveCredentialIdentitiesAsync(
new ASPasswordCredentialIdentity[] { identity });
return;
}
}
await ASHelpers.ReplaceAllIdentities();
}
}
else if (message.Command == "deletedCipher" || message.Command == "softDeletedCipher")
{
if (_deviceActionService.SystemMajorVersion() >= 12)
{
if (await ASHelpers.IdentitiesCanIncremental())
{
var identity = ASHelpers.ToCredentialIdentity(
message.Data as Bit.Core.Models.View.CipherView);
if (identity == null)
{
return;
}
await ASCredentialIdentityStore.SharedStore?.RemoveCredentialIdentitiesAsync(
new ASPasswordCredentialIdentity[] { identity });
return;
}
await ASHelpers.ReplaceAllIdentities();
}
}
else if (message.Command == "logout")
{
if (_deviceActionService.SystemMajorVersion() >= 12)
{
await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync();
}
}
else if ((message.Command == "softDeletedCipher" || message.Command == "restoredCipher")
&& _deviceActionService.SystemMajorVersion() >= 12)
{
await ASHelpers.ReplaceAllIdentities();
}
else if (message.Command == AppHelpers.VAULT_TIMEOUT_ACTION_CHANGED_MESSAGE_COMMAND)
{
var timeoutAction = await _stateService.GetVaultTimeoutActionAsync();
if (timeoutAction == VaultTimeoutAction.Logout)
{
await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync();
}
else
{
await ASHelpers.ReplaceAllIdentities();
}
}
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
}
});
return base.FinishedLaunching(app, options);
}
public override void OnResignActivation(UIApplication uiApplication)
{
var view = new UIView(UIApplication.SharedApplication.KeyWindow.Frame)
{
Tag = SPLASH_VIEW_TAG
};
var backgroundView = new UIView(UIApplication.SharedApplication.KeyWindow.Frame)
{
BackgroundColor = ThemeManager.GetResourceColor("SplashBackgroundColor").ToUIColor()
};
var logo = new UIImage(!ThemeManager.UsingLightTheme ? "logo_white.png" : "logo.png");
var imageView = new UIImageView(logo)
{
Center = new CoreGraphics.CGPoint(view.Center.X, view.Center.Y - 30)
};
view.AddSubview(backgroundView);
view.AddSubview(imageView);
UIApplication.SharedApplication.KeyWindow.AddSubview(view);
UIApplication.SharedApplication.KeyWindow.BringSubviewToFront(view);
UIApplication.SharedApplication.KeyWindow.EndEditing(true);
base.OnResignActivation(uiApplication);
}
public override void DidEnterBackground(UIApplication uiApplication)
{
_stateService?.SetLastActiveTimeAsync(_deviceActionService.GetActiveTime());
_messagingService?.Send("slept");
base.DidEnterBackground(uiApplication);
}
public override void OnActivated(UIApplication uiApplication)
{
base.OnActivated(uiApplication);
UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0;
UIApplication.SharedApplication.KeyWindow?
.ViewWithTag(SPLASH_VIEW_TAG)?
.RemoveFromSuperview();
ThemeManager.UpdateThemeOnPagesAsync();
}
public override void WillEnterForeground(UIApplication uiApplication)
{
_messagingService?.Send(AppHelpers.RESUMED_MESSAGE_COMMAND);
base.WillEnterForeground(uiApplication);
}
public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication,
NSObject annotation)
{
return true;
}
public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
{
return _deepLinkContext.Value.OnNewUri(url) || Xamarin.Essentials.Platform.OpenUrl(app, url, options);
}
public override bool ContinueUserActivity(UIApplication application, NSUserActivity userActivity,
UIApplicationRestorationHandler completionHandler)
{
if (Xamarin.Essentials.Platform.ContinueUserActivity(application, userActivity, completionHandler))
{
return true;
}
return base.ContinueUserActivity(application, userActivity, completionHandler);
}
public override void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
{
_pushHandler?.OnErrorReceived(error);
}
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
_pushHandler?.OnRegisteredSuccess(deviceToken);
}
public override void DidRegisterUserNotificationSettings(UIApplication application,
UIUserNotificationSettings notificationSettings)
{
application.RegisterForRemoteNotifications();
}
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo,
Action<UIBackgroundFetchResult> completionHandler)
{
_pushHandler?.OnMessageReceived(userInfo);
}
public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
{
_pushHandler?.OnMessageReceived(userInfo);
}
public void InitApp()
{
if (ServiceContainer.RegisteredServices.Count > 0)
{
return;
}
// Migration services
ServiceContainer.Register<INativeLogService>("nativeLogService", new ConsoleLogService());
// Note: This might cause a race condition. Investigate more.
Task.Run(() =>
{
FFImageLoading.Forms.Platform.CachedImageRenderer.Init();
FFImageLoading.ImageService.Instance.Initialize(new FFImageLoading.Config.Configuration
{
FadeAnimationEnabled = false,
FadeAnimationForCachedImages = false
});
});
iOSCoreHelpers.RegisterLocalServices();
RegisterPush();
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
ServiceContainer.Init(deviceActionService.DeviceUserAgent, Constants.ClearCiphersCacheKey,
Constants.iOSAllClearCipherCacheKeys);
iOSCoreHelpers.InitLogger();
iOSCoreHelpers.RegisterFinallyBeforeBootstrap();
_pushHandler = new iOSPushNotificationHandler(
ServiceContainer.Resolve<IPushNotificationListenerService>("pushNotificationListenerService"));
_nfcDelegate = new Core.NFCReaderDelegate((success, message) =>
_messagingService.Send("gotYubiKeyOTP", message));
iOSCoreHelpers.Bootstrap(async () => await ApplyManagedSettingsAsync());
}
private void RegisterPush()
{
var notificationListenerService = new PushNotificationListenerService();
ServiceContainer.Register<IPushNotificationListenerService>(
"pushNotificationListenerService", notificationListenerService);
var iosPushNotificationService = new iOSPushNotificationService();
ServiceContainer.Register<IPushNotificationService>(
"pushNotificationService", iosPushNotificationService);
}
private void ShowAppExtension(ExtensionPageViewModel extensionPageViewModel)
{
var itemProvider = new NSItemProvider(new NSDictionary(), Core.Constants.UTTypeAppExtensionSetup);
var extensionItem = new NSExtensionItem
{
Attachments = new NSItemProvider[] { itemProvider }
};
var activityViewController = new UIActivityViewController(new NSExtensionItem[] { extensionItem }, null)
{
CompletionHandler = (activityType, completed) =>
{
extensionPageViewModel.EnabledExtension(completed && activityType == iOSCoreHelpers.AppExtensionId);
}
};
var modal = UIApplication.SharedApplication.KeyWindow.RootViewController.ModalViewController;
if (activityViewController.PopoverPresentationController != null)
{
activityViewController.PopoverPresentationController.SourceView = modal.View;
var frame = UIScreen.MainScreen.Bounds;
frame.Height /= 2;
activityViewController.PopoverPresentationController.SourceRect = frame;
}
modal.PresentViewController(activityViewController, true, null);
}
private void StartEventTimer()
{
_eventTimer?.Invalidate();
_eventTimer?.Dispose();
_eventTimer = null;
Device.BeginInvokeOnMainThread(() =>
{
_eventTimer = NSTimer.CreateScheduledTimer(60, true, timer =>
{
var task = Task.Run(() => _eventService.UploadEventsAsync());
});
});
}
private async Task StopEventTimerAsync()
{
_eventTimer?.Invalidate();
_eventTimer?.Dispose();
_eventTimer = null;
if (_eventBackgroundTaskId > 0)
{
UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId);
_eventBackgroundTaskId = 0;
}
_eventBackgroundTaskId = UIApplication.SharedApplication.BeginBackgroundTask(() =>
{
UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId);
_eventBackgroundTaskId = 0;
});
await _eventService.UploadEventsAsync();
UIApplication.SharedApplication.EndBackgroundTask(_eventBackgroundTaskId);
_eventBackgroundTaskId = 0;
}
private async Task ApplyManagedSettingsAsync()
{
var userDefaults = NSUserDefaults.StandardUserDefaults;
var managedSettings = userDefaults.DictionaryForKey("com.apple.configuration.managed");
if (managedSettings != null && managedSettings.Count > 0)
{
var dict = new Dictionary<string, string>();
foreach (var setting in managedSettings)
{
dict.Add(setting.Key.ToString(), setting.Value?.ToString());
}
await AppHelpers.SetPreconfiguredSettingsAsync(dict);
}
}
private async Task ConnectToWatchIfNeededAsync()
{
if (_stateService != null && await _stateService.GetShouldConnectToWatchAsync())
{
WCSessionManager.SharedManager.StartSession();
}
}
}
}

View File

@ -1,31 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.authentication-services.autofill-credential-provider</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.8bit.bitwarden</string>
</array>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)com.8bit.bitwarden</string>
</array>
<key>com.apple.developer.ubiquity-container-identifiers</key>
<array>
<string>iCloud.$(CFBundleIdentifier)</string>
</array>
<key>com.apple.developer.nfc.readersession.formats</key>
<array>
<string>NDEF</string>
<string>TAG</string>
</array>
<key>com.apple.developer.associated-domains</key>
<array>
<string>webcredentials:bitwarden.com</string>
</array>
<key>aps-environment</key>
<string>development</string>
</dict>
</plist>

View File

@ -1,141 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>MinimumOSVersion</key>
<string>11.0</string>
<key>CFBundleDisplayName</key>
<string>Bitwarden</string>
<key>CFBundleName</key>
<string>Bitwarden</string>
<key>CFBundleIdentifier</key>
<string>com.8bit.bitwarden</string>
<key>CFBundleShortVersionString</key>
<string>2024.1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CFBundleIconName</key>
<string>AppIcon</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>bitwarden</string>
<string>org-appextension-feature-password-management</string>
</array>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.8bit.bitwarden.url</string>
</dict>
<dict>
<key>CFBundleURLName</key>
<string>com.8bit.bitwarden</string>
<key>CFBundleURLSchemes</key>
<array>
<string>otpauth</string>
</array>
</dict>
</array>
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>es</string>
<string>zh-Hans</string>
<string>zh-Hant</string>
<string>pt-PT</string>
<string>pt-BR</string>
<string>sv</string>
<string>sk</string>
<string>it</string>
<string>fi</string>
<string>fr</string>
<string>ro</string>
<string>id</string>
<string>hr</string>
<string>hu</string>
<string>nl</string>
<string>tr</string>
<string>uk</string>
<string>de</string>
<string>dk</string>
<string>cz</string>
<string>nb</string>
<string>ja</string>
<string>et</string>
<string>vi</string>
<string>pl</string>
<string>ko</string>
<string>fa</string>
<string>ru</string>
<string>be</string>
<string>bg</string>
<string>ca</string>
<string>cs</string>
<string>el</string>
<string>th</string>
</array>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile~ipad</key>
<string>LaunchScreen</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
<key>UIStatusBarHidden</key>
<true/>
<key>UIAppFonts</key>
<array>
<string>bwi-font.ttf</string>
<string>MaterialIcons_Regular.ttf</string>
</array>
<key>UIRequiredDeviceCapabilities</key>
<dict>
<key>arm64</key>
<true/>
</dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>XSAppIconAssets</key>
<string>Resources/Assets.xcassets/AppIcons.appiconset</string>
<key>ITSAppUsesNonExemptEncryption</key>
<true/>
<key>ITSEncryptionExportComplianceCode</key>
<string>ecf076d3-4824-4d7b-b716-2a9a47d7d296</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app does not require access to the photo library.</string>
<key>NSCameraUsageDescription</key>
<string>Scan QR codes</string>
<key>NSFaceIDUsageDescription</key>
<string>Use Face ID to unlock your vault.</string>
<key>NFCReaderUsageDescription</key>
<string>Use Yubikeys for two-facor authentication.</string>
</dict>
</plist>

View File

@ -1,91 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="5">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/>
</dependencies>
<scenes>
<scene sceneID="4">
<objects>
<viewController id="5" sceneMemberID="viewController" customClass="LaunchScreenViewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="2"/>
<viewControllerLayoutGuide type="bottom" id="3"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="6">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" id="16" translatesAutoresizingMaskIntoConstraints="NO" image="LaunchScreen">
<rect key="frame" x="159" y="248" width="282" height="44"/>
</imageView>
</subviews>
<constraints>
<constraint id="19" firstItem="16" firstAttribute="centerY" secondItem="6" secondAttribute="centerY" constant="-30"/>
<constraint id="20" firstItem="6" firstAttribute="centerX" secondItem="16" secondAttribute="centerX"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="7" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-54" y="0.0"/>
</scene>
</scenes>
<resources>
<image name="Default.png" width="320" height="480"/>
<image name="fa-cogs.png" width="22" height="22"/>
<image name="fa-lock.png" width="22" height="22"/>
<image name="fa-refresh.png" width="22" height="22"/>
<image name="fa_folder_open.png" width="14" height="14"/>
<image name="Icon-120.png" width="120" height="120"/>
<image name="Icon-152.png" width="152" height="152"/>
<image name="Icon-16.png" width="16" height="16"/>
<image name="Icon-24.png" width="24" height="24"/>
<image name="Icon-32.png" width="32" height="32"/>
<image name="Icon-40.png" width="40" height="40"/>
<image name="Icon-60.png" width="60" height="60"/>
<image name="Icon-64.png" width="64" height="64"/>
<image name="Icon-72.png" width="72" height="72"/>
<image name="Icon-76.png" width="76" height="76"/>
<image name="Icon-Small-40.png" width="40" height="40"/>
<image name="Icon-Small-50.png" width="50" height="50"/>
<image name="Icon-Small.png" width="29" height="29"/>
<image name="Icon.png" width="57" height="57"/>
<image name="ion_chevron_right.png" width="14" height="14"/>
<image name="ion_plus.png" width="22" height="22"/>
<image name="LaunchScreen" width="282" height="44"/>
<image name="cogs.png" width="29" height="29"/>
<image name="eye.png" width="22" height="22"/>
<image name="eye_slash.png" width="22" height="22"/>
<image name="more.png" width="28" height="28"/>
<image name="more_selected.png" width="28" height="28"/>
<image name="plus.png" width="18" height="18"/>
<image name="star.png" width="25" height="25"/>
<image name="cloudup.png" width="44" height="44"/>
<image name="envelope.png" width="18" height="18"/>
<image name="globe.png" width="44" height="44"/>
<image name="lightbulb-o.png" width="18" height="18"/>
<image name="lock.png" width="18" height="18"/>
<image name="refresh.png" width="44" height="44"/>
<image name="upload.png" width="44" height="44"/>
<image name="user.png" width="18" height="18"/>
<image name="wrench.png" width="22" height="22"/>
<image name="camera.png" width="22" height="22"/>
<image name="download.png" width="18" height="18"/>
<image name="ext-act.png" width="290" height="252"/>
<image name="ext-more.png" width="290" height="252"/>
<image name="ext-use.png" width="290" height="252"/>
<image name="fa_lock.png" width="25" height="25"/>
<image name="fingerprint.png" width="91" height="92"/>
<image name="folder.png" width="18" height="18"/>
<image name="Icon-83.5.png" width="83.5" height="83.5"/>
<image name="lightbulb.png" width="18" height="18"/>
<image name="paperclip.png" width="14" height="14"/>
<image name="photo.png" width="22" height="22"/>
<image name="share.png" width="14" height="14"/>
<image name="share_tools.png" width="44" height="44"/>
<image name="tools.png" width="26" height="26"/>
<image name="trash.png" width="18" height="18"/>
<image name="yubikey.png" width="266" height="160"/>
</resources>
</document>

View File

@ -1,13 +0,0 @@
using UIKit;
namespace Bit.iOS
{
public class Application
{
static void Main(string[] args)
{
ObjCRuntime.Dlfcn.dlopen(ObjCRuntime.Constants.libSystemLibrary, 0);
UIApplication.Main(args, null, "AppDelegate");
}
}
}

View File

@ -1,35 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("BitwardeniOS")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Bitwarden Inc.")]
[assembly: AssemblyProduct("Bitwarden")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("72bdc44f-c588-44f3-b6df-9aace7daafdd")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -1,117 +0,0 @@
{
"images": [
{
"scale": "2x",
"size": "20x20",
"idiom": "iphone",
"filename": "Icon-40.png"
},
{
"scale": "3x",
"size": "20x20",
"idiom": "iphone",
"filename": "Icon-60.png"
},
{
"scale": "2x",
"size": "29x29",
"idiom": "iphone",
"filename": "Icon-58.png"
},
{
"scale": "3x",
"size": "29x29",
"idiom": "iphone",
"filename": "Icon-87.png"
},
{
"scale": "2x",
"size": "40x40",
"idiom": "iphone",
"filename": "Icon-80.png"
},
{
"scale": "3x",
"size": "40x40",
"idiom": "iphone",
"filename": "Icon-120.png"
},
{
"scale": "2x",
"size": "60x60",
"idiom": "iphone",
"filename": "Icon-120.png"
},
{
"scale": "3x",
"size": "60x60",
"idiom": "iphone",
"filename": "Icon-180.png"
},
{
"scale": "1x",
"size": "20x20",
"idiom": "ipad",
"filename": "Icon-20.png"
},
{
"scale": "2x",
"size": "20x20",
"idiom": "ipad",
"filename": "Icon-40.png"
},
{
"scale": "1x",
"size": "29x29",
"idiom": "ipad",
"filename": "Icon-29.png"
},
{
"scale": "2x",
"size": "29x29",
"idiom": "ipad",
"filename": "Icon-58.png"
},
{
"scale": "1x",
"size": "40x40",
"idiom": "ipad",
"filename": "Icon-40.png"
},
{
"scale": "2x",
"size": "40x40",
"idiom": "ipad",
"filename": "Icon-80.png"
},
{
"scale": "1x",
"size": "76x76",
"idiom": "ipad",
"filename": "Icon-76.png"
},
{
"scale": "2x",
"size": "76x76",
"idiom": "ipad",
"filename": "Icon-152.png"
},
{
"scale": "2x",
"size": "83.5x83.5",
"idiom": "ipad",
"filename": "Icon-167.png"
},
{
"scale": "1x",
"size": "1024x1024",
"idiom": "ios-marketing",
"filename": "Icon-1024.png"
}
],
"properties": {},
"info": {
"version": 1,
"author": "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 738 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 899 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,6 +0,0 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -1,68 +0,0 @@
{
"images" : [
{
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal"
},
{
"filename" : "logo.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "logo_white.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "logo@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "logo_white@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "logo@3x.png",
"idiom" : "universal",
"scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "logo_white@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -1,25 +0,0 @@
{
"images" : [
{
"filename" : "Empty-items-state.pdf",
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "Empty-items-state-dark.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

View File

@ -1,25 +0,0 @@
{
"images" : [
{
"filename" : "empty_login_requests.pdf",
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "empty_login_requests_dark.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

View File

@ -1,608 +0,0 @@
{
"images" : [
{
"filename" : "ic_warning-1.pdf",
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal"
},
{
"idiom" : "universal",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "universal",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "universal",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "universal",
"scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "universal",
"scale" : "3x"
},
{
"idiom" : "iphone"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "iphone"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "iphone"
},
{
"idiom" : "iphone",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "iphone",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "iphone",
"scale" : "1x"
},
{
"idiom" : "iphone",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "iphone",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "iphone",
"scale" : "2x"
},
{
"idiom" : "iphone",
"scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "iphone",
"scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "iphone",
"scale" : "3x"
},
{
"idiom" : "iphone",
"scale" : "1x",
"subtype" : "retina4"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "iphone",
"scale" : "1x",
"subtype" : "retina4"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "iphone",
"scale" : "1x",
"subtype" : "retina4"
},
{
"idiom" : "iphone",
"scale" : "2x",
"subtype" : "retina4"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "iphone",
"scale" : "2x",
"subtype" : "retina4"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "iphone",
"scale" : "2x",
"subtype" : "retina4"
},
{
"idiom" : "iphone",
"scale" : "3x",
"subtype" : "retina4"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "iphone",
"scale" : "3x",
"subtype" : "retina4"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "iphone",
"scale" : "3x",
"subtype" : "retina4"
},
{
"idiom" : "ipad"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "ipad"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "ipad"
},
{
"idiom" : "ipad",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "ipad",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "ipad",
"scale" : "1x"
},
{
"idiom" : "ipad",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "ipad",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "ipad",
"scale" : "2x"
},
{
"idiom" : "car"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "car"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "car"
},
{
"idiom" : "car",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "car",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "car",
"scale" : "2x"
},
{
"idiom" : "car",
"scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "car",
"scale" : "3x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "car",
"scale" : "3x"
},
{
"idiom" : "mac"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "mac"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "mac"
},
{
"idiom" : "mac",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "mac",
"scale" : "1x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "mac",
"scale" : "1x"
},
{
"idiom" : "mac",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "mac",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "mac",
"scale" : "2x"
},
{
"idiom" : "watch"
},
{
"idiom" : "watch",
"screen-width" : "<=145"
},
{
"idiom" : "watch",
"screen-width" : ">161"
},
{
"idiom" : "watch",
"screen-width" : ">145"
},
{
"idiom" : "watch",
"screen-width" : ">183"
},
{
"idiom" : "watch",
"scale" : "2x"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : "<=145"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">161"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">145"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">183"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "watch"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "watch"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "watch",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "watch",
"scale" : "2x"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "watch",
"scale" : "2x",
"screen-width" : "<=145"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "watch",
"scale" : "2x",
"screen-width" : "<=145"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "light"
}
],
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">145"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">145"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -1,119 +0,0 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 0.000000 -0.004333 cm
0.866667 0.294118 0.223529 scn
24.979254 0.004448 m
2.740886 0.004448 l
2.256652 0.000004 1.780215 0.127985 1.360950 0.376835 c
0.941685 0.625685 0.595185 0.983850 0.356966 1.415783 c
0.120480 1.834383 -0.003394 2.309866 0.000071 2.793346 c
0.003536 3.276825 0.132607 3.750528 0.374291 4.165575 c
11.493476 23.511885 l
11.737758 23.932262 12.084259 24.279764 12.499193 24.521503 c
12.914126 24.763245 13.382767 24.890335 13.860070 24.890335 c
14.337375 24.890335 14.806015 24.763245 15.220948 24.521503 c
15.635882 24.279764 15.982384 23.932262 16.226665 23.511885 c
27.345852 4.165575 l
27.587534 3.750528 27.717470 3.276825 27.720068 2.793346 c
27.722668 2.309866 27.599663 1.834383 27.363176 1.415783 c
27.124958 0.983850 26.778458 0.624796 26.359192 0.376835 c
25.939928 0.128874 25.463488 0.000004 24.979254 0.004448 c
24.979254 0.004448 l
h
13.860070 23.111946 m
13.684222 23.114613 13.510106 23.069286 13.356780 22.981300 c
13.203454 22.893314 13.075248 22.764446 12.985158 22.608913 c
1.867706 3.262606 l
1.782813 3.116850 1.737768 2.949764 1.737768 2.780014 c
1.737768 2.610262 1.782813 2.443178 1.867706 2.297422 c
1.956063 2.138336 2.084268 2.006802 2.239327 1.916149 c
2.394386 1.825497 2.570235 1.779282 2.747816 1.781948 c
24.979254 1.781948 l
25.157701 1.779282 25.333551 1.825497 25.487745 1.916149 c
25.641937 2.006802 25.771009 2.138336 25.859367 2.297422 c
25.944260 2.443178 25.989302 2.610262 25.989302 2.780014 c
25.989302 2.949764 25.944260 3.116850 25.859367 3.262606 c
14.734983 22.608913 l
14.644894 22.764446 14.516687 22.892426 14.363361 22.981300 c
14.210035 23.070175 14.035919 23.114613 13.860070 23.111946 c
13.860070 23.111946 l
h
13.860070 7.887661 m
13.630514 7.887661 13.409620 7.980980 13.247631 8.148064 c
13.085643 8.315149 12.993821 8.540892 12.993821 8.776411 c
12.993821 16.904917 l
12.993821 17.140434 13.084776 17.367065 13.247631 17.533262 c
13.410486 17.699459 13.630514 17.793667 13.860070 17.793667 c
14.089627 17.793667 14.310521 17.700348 14.472510 17.533262 c
14.634499 17.366177 14.726320 17.140434 14.726320 16.904917 c
14.726320 8.776411 l
14.726320 8.540892 14.635365 8.314260 14.472510 8.148064 c
14.309655 7.981867 14.089627 7.887661 13.860070 7.887661 c
h
13.860070 3.493681 m
14.427464 3.493681 14.887444 3.965607 14.887444 4.547737 c
14.887444 5.129869 14.427464 5.601795 13.860070 5.601795 c
13.292677 5.601795 12.832698 5.129869 12.832698 4.547737 c
12.832698 3.965607 13.292677 3.493681 13.860070 3.493681 c
h
f
n
Q
endstream
endobj
3 0 obj
2568
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 27.720123 24.886002 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000002658 00000 n
0000002681 00000 n
0000002854 00000 n
0000002928 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
2987
%%EOF

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 931 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 931 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Some files were not shown because too many files have changed in this diff Show More