PM-3349 PM-3350 MAUI Migration Initial
424
src/App/Platforms/iOS/AppDelegate.cs
Normal file
@@ -0,0 +1,424 @@
|
||||
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 Microsoft.Maui.Controls.Compatibility.Platform.iOS;
|
||||
using UIKit;
|
||||
using WatchConnectivity;
|
||||
|
||||
namespace Bit.iOS
|
||||
{
|
||||
[Register("AppDelegate")]
|
||||
public partial class AppDelegate : MauiUIApplicationDelegate
|
||||
{
|
||||
protected override MauiApp CreateMauiApp() => App.MauiProgram.CreateMauiApp();
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
var finishedLaunching = base.FinishedLaunching(app, options);
|
||||
|
||||
ThemeManager.SetTheme(Microsoft.Maui.Controls.Application.Current.Resources);
|
||||
iOSCoreHelpers.AppearanceAdjustments();
|
||||
|
||||
return finishedLaunching;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
[Export("application:openURL:sourceApplication:annotation:")]
|
||||
public 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) || base.OpenUrl(app, url, options);
|
||||
}
|
||||
|
||||
// TODO: [MAUI-Migration] Check if this works given that the baes will call the invoke the service lifecycle.
|
||||
/* 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);
|
||||
} */
|
||||
|
||||
[Export("application:didFailToRegisterForRemoteNotificationsWithError:")]
|
||||
public void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
|
||||
{
|
||||
_pushHandler?.OnErrorReceived(error);
|
||||
}
|
||||
|
||||
[Export("application:didRegisterForRemoteNotificationsWithDeviceToken:")]
|
||||
public void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
|
||||
{
|
||||
_pushHandler?.OnRegisteredSuccess(deviceToken);
|
||||
}
|
||||
|
||||
[Export("application:didRegisterUserNotificationSettings:")]
|
||||
public void DidRegisterUserNotificationSettings(UIApplication application,
|
||||
UIUserNotificationSettings notificationSettings)
|
||||
{
|
||||
application.RegisterForRemoteNotifications();
|
||||
}
|
||||
|
||||
[Export("application:didReceiveRemoteNotification:fetchCompletionHandler:")]
|
||||
public void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo,
|
||||
Action<UIBackgroundFetchResult> completionHandler)
|
||||
{
|
||||
_pushHandler?.OnMessageReceived(userInfo);
|
||||
}
|
||||
|
||||
[Export("application:didReceiveRemoteNotification:")]
|
||||
public 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/App/Platforms/iOS/Entitlements.plist
Normal file
@@ -0,0 +1,31 @@
|
||||
<?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>
|
||||
141
src/App/Platforms/iOS/Info.plist
Normal file
@@ -0,0 +1,141 @@
|
||||
<?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>2023.9.2</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>
|
||||
91
src/App/Platforms/iOS/LaunchScreen.storyboard
Normal file
@@ -0,0 +1,91 @@
|
||||
<?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>
|
||||
12
src/App/Platforms/iOS/Program.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Bit.iOS;
|
||||
using UIKit;
|
||||
|
||||
namespace Bit.App;
|
||||
|
||||
public class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
UIApplication.Main(args, null, typeof(AppDelegate));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 30 KiB |
BIN
src/App/Platforms/iOS/Resources/Assets.xcassets/AppIcons.appiconset/Icon-120.png
Executable file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
src/App/Platforms/iOS/Resources/Assets.xcassets/AppIcons.appiconset/Icon-152.png
Executable file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
src/App/Platforms/iOS/Resources/Assets.xcassets/AppIcons.appiconset/Icon-167.png
Executable file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
src/App/Platforms/iOS/Resources/Assets.xcassets/AppIcons.appiconset/Icon-180.png
Executable file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
src/App/Platforms/iOS/Resources/Assets.xcassets/AppIcons.appiconset/Icon-20.png
Executable file
|
After Width: | Height: | Size: 738 B |
BIN
src/App/Platforms/iOS/Resources/Assets.xcassets/AppIcons.appiconset/Icon-29.png
Executable file
|
After Width: | Height: | Size: 899 B |
BIN
src/App/Platforms/iOS/Resources/Assets.xcassets/AppIcons.appiconset/Icon-40.png
Executable file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/App/Platforms/iOS/Resources/Assets.xcassets/AppIcons.appiconset/Icon-58.png
Executable file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
src/App/Platforms/iOS/Resources/Assets.xcassets/AppIcons.appiconset/Icon-60.png
Executable file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
src/App/Platforms/iOS/Resources/Assets.xcassets/AppIcons.appiconset/Icon-76.png
Executable file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
src/App/Platforms/iOS/Resources/Assets.xcassets/AppIcons.appiconset/Icon-80.png
Executable file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
src/App/Platforms/iOS/Resources/Assets.xcassets/AppIcons.appiconset/Icon-87.png
Executable file
|
After Width: | Height: | Size: 2.0 KiB |
6
src/App/Platforms/iOS/Resources/Assets.xcassets/Contents.json
Executable file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
68
src/App/Platforms/iOS/Resources/Assets.xcassets/LaunchScreen.imageset/Contents.json
vendored
Executable file
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
BIN
src/App/Platforms/iOS/Resources/Assets.xcassets/LaunchScreen.imageset/logo.png
vendored
Executable file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
src/App/Platforms/iOS/Resources/Assets.xcassets/LaunchScreen.imageset/logo@2x.png
vendored
Executable file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
src/App/Platforms/iOS/Resources/Assets.xcassets/LaunchScreen.imageset/logo@3x.png
vendored
Executable file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
src/App/Platforms/iOS/Resources/Assets.xcassets/LaunchScreen.imageset/logo_white.png
vendored
Executable file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
src/App/Platforms/iOS/Resources/Assets.xcassets/LaunchScreen.imageset/logo_white@2x.png
vendored
Executable file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/App/Platforms/iOS/Resources/Assets.xcassets/LaunchScreen.imageset/logo_white@3x.png
vendored
Executable file
|
After Width: | Height: | Size: 8.2 KiB |
25
src/App/Platforms/iOS/Resources/Assets.xcassets/empty_items_state.imageset/Contents.json
vendored
Executable file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
BIN
src/App/Platforms/iOS/Resources/Assets.xcassets/empty_items_state.imageset/Empty-items-state-dark.pdf
vendored
Executable file
BIN
src/App/Platforms/iOS/Resources/Assets.xcassets/empty_items_state.imageset/Empty-items-state.pdf
vendored
Executable file
25
src/App/Platforms/iOS/Resources/Assets.xcassets/empty_login_requests.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
608
src/App/Platforms/iOS/Resources/Assets.xcassets/ic_warning.imageset/Contents.json
vendored
Executable file
@@ -0,0 +1,608 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
119
src/App/Platforms/iOS/Resources/Assets.xcassets/ic_warning.imageset/ic_warning-1.pdf
vendored
Executable file
@@ -0,0 +1,119 @@
|
||||
%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
|
||||
127
src/App/Platforms/iOS/Services/iOSPushNotificationHandler.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System.Diagnostics;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Models;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Services;
|
||||
using Foundation;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using UserNotifications;
|
||||
|
||||
namespace Bit.iOS.Services
|
||||
{
|
||||
public class iOSPushNotificationHandler : NSObject, IUNUserNotificationCenterDelegate
|
||||
{
|
||||
private const string TokenSetting = "token";
|
||||
const string TAG = "##PUSH NOTIFICATIONS";
|
||||
|
||||
private readonly IPushNotificationListenerService _pushNotificationListenerService;
|
||||
|
||||
public iOSPushNotificationHandler(
|
||||
IPushNotificationListenerService pushNotificationListenerService)
|
||||
{
|
||||
_pushNotificationListenerService = pushNotificationListenerService;
|
||||
}
|
||||
|
||||
public void OnMessageReceived(NSDictionary userInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
Debug.WriteLine($"{TAG} - OnMessageReceived.");
|
||||
|
||||
var json = DictionaryToJson(userInfo);
|
||||
var values = JObject.Parse(json);
|
||||
var keyAps = new NSString("aps");
|
||||
if (userInfo.ContainsKey(keyAps) && userInfo.ValueForKey(keyAps) is NSDictionary aps)
|
||||
{
|
||||
foreach (var apsKey in aps)
|
||||
{
|
||||
if (!values.TryGetValue(apsKey.Key.ToString(), out JToken temp))
|
||||
{
|
||||
values.Add(apsKey.Key.ToString(), apsKey.Value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
_pushNotificationListenerService.OnMessageAsync(values, Device.iOS);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Instance.Exception(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnErrorReceived(NSError error)
|
||||
{
|
||||
Debug.WriteLine($"{TAG} - Registration Failed.");
|
||||
_pushNotificationListenerService.OnError(error.LocalizedDescription, Device.iOS);
|
||||
}
|
||||
|
||||
public void OnRegisteredSuccess(NSData token)
|
||||
{
|
||||
Debug.WriteLine($"{TAG} - Successfully Registered.");
|
||||
|
||||
var hexDeviceToken = BitConverter.ToString(token.ToArray())
|
||||
.Replace("-", string.Empty)
|
||||
.ToLowerInvariant();
|
||||
|
||||
Debug.WriteLine($"{TAG} - Token: {hexDeviceToken}");
|
||||
|
||||
UNUserNotificationCenter.Current.Delegate = this;
|
||||
|
||||
_pushNotificationListenerService.OnRegisteredAsync(hexDeviceToken, Device.iOS);
|
||||
NSUserDefaults.StandardUserDefaults.SetString(hexDeviceToken, TokenSetting);
|
||||
NSUserDefaults.StandardUserDefaults.Synchronize();
|
||||
}
|
||||
|
||||
private static string DictionaryToJson(NSDictionary dictionary)
|
||||
{
|
||||
var json = NSJsonSerialization.Serialize(dictionary, NSJsonWritingOptions.PrettyPrinted, out NSError error);
|
||||
return json.ToString(NSStringEncoding.UTF8);
|
||||
}
|
||||
|
||||
// To receive notifications in foreground on iOS 10 devices.
|
||||
[Export("userNotificationCenter:willPresentNotification:withCompletionHandler:")]
|
||||
public void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
|
||||
{
|
||||
Debug.WriteLine($"{TAG} WillPresentNotification {notification?.Request?.Content?.UserInfo}");
|
||||
OnMessageReceived(notification?.Request?.Content?.UserInfo);
|
||||
completionHandler(UNNotificationPresentationOptions.Alert);
|
||||
}
|
||||
|
||||
[Export("userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:")]
|
||||
public void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler)
|
||||
{
|
||||
Debug.WriteLine($"{TAG} DidReceiveNotificationResponse {response?.Notification?.Request?.Content?.UserInfo}");
|
||||
if ((response?.Notification?.Request?.Content?.UserInfo) == null)
|
||||
{
|
||||
completionHandler();
|
||||
return;
|
||||
}
|
||||
|
||||
var userInfo = response?.Notification?.Request?.Content?.UserInfo;
|
||||
OnMessageReceived(userInfo);
|
||||
|
||||
if (userInfo.TryGetValue(NSString.FromObject(Constants.NotificationData), out NSObject nsObject))
|
||||
{
|
||||
var token = JToken.Parse(NSString.FromObject(nsObject).ToString());
|
||||
var typeToken = token.SelectToken(Constants.NotificationDataType);
|
||||
if (response.IsDefaultAction)
|
||||
{
|
||||
if (typeToken.ToString() == PasswordlessNotificationData.TYPE)
|
||||
{
|
||||
_pushNotificationListenerService.OnNotificationTapped(token.ToObject<PasswordlessNotificationData>());
|
||||
}
|
||||
}
|
||||
else if (response.IsDismissAction)
|
||||
{
|
||||
if (typeToken.ToString() == PasswordlessNotificationData.TYPE)
|
||||
{
|
||||
_pushNotificationListenerService.OnNotificationDismissed(token.ToObject<PasswordlessNotificationData>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Inform caller it has been handled
|
||||
completionHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
116
src/App/Platforms/iOS/Services/iOSPushNotificationService.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using System.Diagnostics;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Models;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Resources.Localization;
|
||||
using Bit.Core.Services;
|
||||
using Foundation;
|
||||
using Newtonsoft.Json;
|
||||
using UIKit;
|
||||
using UserNotifications;
|
||||
|
||||
namespace Bit.iOS.Services
|
||||
{
|
||||
public class iOSPushNotificationService : NSObject, IPushNotificationService, IUNUserNotificationCenterDelegate
|
||||
{
|
||||
private const string TokenSetting = "token";
|
||||
const string TAG = "##PUSH NOTIFICATIONS";
|
||||
|
||||
public Task<string> GetTokenAsync()
|
||||
{
|
||||
return Task.FromResult(NSUserDefaults.StandardUserDefaults.StringForKey(TokenSetting));
|
||||
}
|
||||
|
||||
public bool IsRegisteredForPush => UIApplication.SharedApplication.IsRegisteredForRemoteNotifications;
|
||||
|
||||
public async Task<bool> AreNotificationsSettingsEnabledAsync()
|
||||
{
|
||||
var settings = await UNUserNotificationCenter.Current.GetNotificationSettingsAsync();
|
||||
return settings.AlertSetting == UNNotificationSetting.Enabled;
|
||||
}
|
||||
|
||||
public async Task RegisterAsync()
|
||||
{
|
||||
Debug.WriteLine($"{TAG} RegisterAsync");
|
||||
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
|
||||
var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound;
|
||||
UNUserNotificationCenter.Current.RequestAuthorization(authOptions, (granted, error) =>
|
||||
{
|
||||
if (error != null)
|
||||
{
|
||||
Debug.WriteLine($"{TAG} {error}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine($"{TAG} {granted}");
|
||||
}
|
||||
|
||||
tcs.SetResult(granted);
|
||||
});
|
||||
|
||||
if (await tcs.Task)
|
||||
{
|
||||
Debug.WriteLine($"{TAG} RegisterForRemoteNotifications");
|
||||
UIApplication.SharedApplication.RegisterForRemoteNotifications();
|
||||
}
|
||||
}
|
||||
|
||||
public Task UnregisterAsync()
|
||||
{
|
||||
Debug.WriteLine($"{TAG} UnregisterAsync");
|
||||
|
||||
UIApplication.SharedApplication.UnregisterForRemoteNotifications();
|
||||
// TODO: unregister call
|
||||
// _pushNotificationListener.OnUnregistered(Device.iOS);
|
||||
NSUserDefaults.StandardUserDefaults.SetString(string.Empty, TokenSetting);
|
||||
NSUserDefaults.StandardUserDefaults.Synchronize();
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public void SendLocalNotification(string title, string message, BaseNotificationData data)
|
||||
{
|
||||
if (string.IsNullOrEmpty(data.Id))
|
||||
{
|
||||
throw new ArgumentNullException("notificationId cannot be null or empty.");
|
||||
}
|
||||
|
||||
var content = new UNMutableNotificationContent()
|
||||
{
|
||||
Title = title,
|
||||
Body = message,
|
||||
CategoryIdentifier = Constants.iOSNotificationCategoryId
|
||||
};
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
content.UserInfo = NSDictionary.FromObjectAndKey(NSData.FromString(JsonConvert.SerializeObject(data), NSStringEncoding.UTF8), new NSString(Constants.NotificationData));
|
||||
}
|
||||
|
||||
var actions = new UNNotificationAction[] { UNNotificationAction.FromIdentifier(Constants.iOSNotificationClearActionId, AppResources.Clear, UNNotificationActionOptions.Foreground) };
|
||||
var category = UNNotificationCategory.FromIdentifier(Constants.iOSNotificationCategoryId, actions, new string[] { }, UNNotificationCategoryOptions.CustomDismissAction);
|
||||
UNUserNotificationCenter.Current.SetNotificationCategories(new NSSet<UNNotificationCategory>(category));
|
||||
|
||||
var request = UNNotificationRequest.FromIdentifier(data.Id, content, null);
|
||||
UNUserNotificationCenter.Current.AddNotificationRequest(request, (err) =>
|
||||
{
|
||||
if (err != null)
|
||||
{
|
||||
Logger.Instance.Exception(new Exception($"Failed to schedule notification: {err}"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void DismissLocalNotification(string notificationId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(notificationId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UNUserNotificationCenter.Current.RemovePendingNotificationRequests(new string[] { notificationId });
|
||||
UNUserNotificationCenter.Current.RemoveDeliveredNotifications(new string[] { notificationId });
|
||||
}
|
||||
}
|
||||
}
|
||||