using System; using XLabs.Ioc; using Foundation; using UIKit; using Bit.App.Abstractions; using Bit.App.Services; using Bit.iOS.Services; using Plugin.Connectivity; using Bit.App.Repositories; using Plugin.Fingerprint; using Plugin.Settings.Abstractions; using System.Diagnostics; using Xamarin.Forms; using Bit.iOS.Core.Services; using Plugin.Connectivity.Abstractions; using Bit.App.Pages; using HockeyApp.iOS; using Bit.iOS.Core; using Google.Analytics; using SimpleInjector; using XLabs.Ioc.SimpleInjectorContainer; using CoreNFC; using Bit.App.Resources; using AuthenticationServices; using System.Threading.Tasks; using Bit.App.Models; using System.Linq; using System.Collections.Generic; using Bit.iOS.Core.Utilities; namespace Bit.iOS { [Register("AppDelegate")] public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate { private GaiCompletionHandler _dispatchHandler = null; private NFCNdefReaderSession _nfcSession = null; private ILockService _lockService; private IDeviceInfoService _deviceInfoService; private ICipherService _cipherService; private iOSPushNotificationHandler _pushHandler = null; private NFCReaderDelegate _nfcDelegate = null; public ISettings Settings { get; set; } public override bool FinishedLaunching(UIApplication app, NSDictionary options) { Forms.Init(); if(!Resolver.IsSet) { SetIoc(); } _lockService = Resolver.Resolve(); _deviceInfoService = Resolver.Resolve(); _cipherService = Resolver.Resolve(); _pushHandler = new iOSPushNotificationHandler(Resolver.Resolve()); _nfcDelegate = new NFCReaderDelegate((success, message) => ProcessYubikey(success, message)); var appIdService = Resolver.Resolve(); var crashManagerDelegate = new HockeyAppCrashManagerDelegate( appIdService, Resolver.Resolve()); var manager = BITHockeyManager.SharedHockeyManager; manager.Configure("51f96ae568ba45f699a18ad9f63046c3", crashManagerDelegate); manager.CrashManager.CrashManagerStatus = BITCrashManagerStatus.AutoSend; manager.UserId = appIdService.AppId; manager.StartManager(); manager.Authenticator.AuthenticateInstallation(); manager.DisableMetricsManager = manager.DisableFeedbackManager = manager.DisableUpdateManager = true; LoadApplication(new App.App( null, Resolver.Resolve(), Resolver.Resolve(), Resolver.Resolve(), Resolver.Resolve(), Resolver.Resolve(), _lockService, Resolver.Resolve(), Resolver.Resolve(), Resolver.Resolve(), Resolver.Resolve())); // Appearance stuff var primaryColor = new UIColor(red: 0.24f, green: 0.55f, blue: 0.74f, alpha: 1.0f); var grayLight = new UIColor(red: 0.47f, green: 0.47f, blue: 0.47f, alpha: 1.0f); UINavigationBar.Appearance.ShadowImage = new UIImage(); UINavigationBar.Appearance.SetBackgroundImage(new UIImage(), UIBarMetrics.Default); UIBarButtonItem.AppearanceWhenContainedIn(new Type[] { typeof(UISearchBar) }).TintColor = primaryColor; UIButton.AppearanceWhenContainedIn(new Type[] { typeof(UISearchBar) }).SetTitleColor(primaryColor, UIControlState.Normal); UIButton.AppearanceWhenContainedIn(new Type[] { typeof(UISearchBar) }).TintColor = primaryColor; UIStepper.Appearance.TintColor = grayLight; UISlider.Appearance.TintColor = primaryColor; MessagingCenter.Subscribe( Xamarin.Forms.Application.Current, "ShowAppExtension", (sender, page) => { var itemProvider = new NSItemProvider(new NSDictionary(), Core.Constants.UTTypeAppExtensionSetup); var extensionItem = new NSExtensionItem(); extensionItem.Attachments = new NSItemProvider[] { itemProvider }; var activityViewController = new UIActivityViewController(new NSExtensionItem[] { extensionItem }, null); activityViewController.CompletionHandler = (activityType, completed) => { page.EnabledExtension(completed && activityType == "com.8bit.bitwarden.find-login-action-extension"); }; 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); }); MessagingCenter.Subscribe( Xamarin.Forms.Application.Current, "ListenYubiKeyOTP", (sender, listen) => { if(_deviceInfoService.NfcEnabled) { _nfcSession?.InvalidateSession(); _nfcSession?.Dispose(); _nfcSession = null; if(listen) { _nfcSession = new NFCNdefReaderSession(_nfcDelegate, null, true); _nfcSession.AlertMessage = AppResources.HoldYubikeyNearTop; _nfcSession.BeginSession(); } } }); UIApplication.SharedApplication.StatusBarHidden = false; UIApplication.SharedApplication.StatusBarStyle = UIStatusBarStyle.LightContent; MessagingCenter.Subscribe( Xamarin.Forms.Application.Current, "ShowStatusBar", (sender, show) => { UIApplication.SharedApplication.SetStatusBarHidden(!show, false); }); MessagingCenter.Subscribe( Xamarin.Forms.Application.Current, "FullSyncCompleted", async (sender, successfully) => { if(successfully) { await ASHelpers.ReplaceAllIdentities(_cipherService); } }); MessagingCenter.Subscribe>( Xamarin.Forms.Application.Current, "UpsertedCipher", async (sender, data) => { if(await ASHelpers.IdentitiesCanIncremental()) { if(data.Item2) { var identity = await ASHelpers.GetCipherIdentityAsync(data.Item1, _cipherService); if(identity == null) { return; } await ASCredentialIdentityStore.SharedStore.SaveCredentialIdentitiesAsync( new ASPasswordCredentialIdentity[] { identity }); return; } } await ASHelpers.ReplaceAllIdentities(_cipherService); }); MessagingCenter.Subscribe( Xamarin.Forms.Application.Current, "DeletedCipher", async (sender, cipher) => { if(await ASHelpers.IdentitiesCanIncremental()) { var identity = ASHelpers.ToCredentialIdentity(cipher); if(identity == null) { return; } await ASCredentialIdentityStore.SharedStore.RemoveCredentialIdentitiesAsync( new ASPasswordCredentialIdentity[] { identity }); return; } await ASHelpers.ReplaceAllIdentities(_cipherService); }); MessagingCenter.Subscribe( Xamarin.Forms.Application.Current, "LoggedOut", async (sender) => { await ASCredentialIdentityStore.SharedStore.RemoveAllCredentialIdentitiesAsync(); }); ZXing.Net.Mobile.Forms.iOS.Platform.Init(); return base.FinishedLaunching(app, options); } public override void DidEnterBackground(UIApplication uiApplication) { var view = new UIView(UIApplication.SharedApplication.KeyWindow.Frame) { Tag = 4321 }; var backgroundView = new UIView(UIApplication.SharedApplication.KeyWindow.Frame) { BackgroundColor = new UIColor(red: 0.93f, green: 0.94f, blue: 0.96f, alpha: 1.0f) }; var imageView = new UIImageView(new UIImage("logo.png")) { 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); UIApplication.SharedApplication.SetStatusBarHidden(true, false); // Log the date/time we last backgrounded _lockService.UpdateLastActivity(); // Dispatch Google Analytics SendGoogleAnalyticsHitsInBackground(); base.DidEnterBackground(uiApplication); Debug.WriteLine("DidEnterBackground"); } public override void OnResignActivation(UIApplication uiApplication) { base.OnResignActivation(uiApplication); Debug.WriteLine("OnResignActivation"); } public override void WillTerminate(UIApplication uiApplication) { base.WillTerminate(uiApplication); Debug.WriteLine("WillTerminate"); } public override void OnActivated(UIApplication uiApplication) { base.OnActivated(uiApplication); Debug.WriteLine("OnActivated"); UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0; var view = UIApplication.SharedApplication.KeyWindow.ViewWithTag(4321); if(view != null) { view.RemoveFromSuperview(); UIApplication.SharedApplication.SetStatusBarHidden(false, false); } } public override void WillEnterForeground(UIApplication uiApplication) { SendResumedMessage(); // Restores the dispatch interval because dispatchWithCompletionHandler // has disabled automatic dispatching. Gai.SharedInstance.DispatchInterval = 10; base.WillEnterForeground(uiApplication); Debug.WriteLine("WillEnterForeground"); } public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation) { return true; } 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 completionHandler) { _pushHandler?.OnMessageReceived(userInfo); } public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo) { _pushHandler?.OnMessageReceived(userInfo); } private void SendResumedMessage() { MessagingCenter.Send(Xamarin.Forms.Application.Current, "Resumed", false); } private void SetIoc() { var container = new Container(); // Services container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); // Repositories container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); container.RegisterSingleton(); // Other container.RegisterInstance(CrossConnectivity.Current); container.RegisterInstance(CrossFingerprint.Current); Settings = new Settings("group.com.8bit.bitwarden"); container.RegisterInstance(Settings); // Push container.RegisterSingleton(); container.RegisterSingleton(); FFImageLoading.Forms.Platform.CachedImageRenderer.Init(); Resolver.SetResolver(new SimpleInjectorResolver(container)); } /// /// This method sends any queued hits when the app enters the background. /// ref: https://developers.google.com/analytics/devguides/collection/ios/v3/dispatch /// private void SendGoogleAnalyticsHitsInBackground() { var taskExpired = false; var taskId = UIApplication.SharedApplication.BeginBackgroundTask(() => { taskExpired = true; }); if(taskId == UIApplication.BackgroundTaskInvalid) { return; } _dispatchHandler = (result) => { // Send hits until no hits are left, a dispatch error occurs, or the background task expires. if(_dispatchHandler != null && result == DispatchResult.Good && !taskExpired) { Gai.SharedInstance.Dispatch(_dispatchHandler); } else { UIApplication.SharedApplication.EndBackgroundTask(taskId); } }; Gai.SharedInstance.Dispatch(_dispatchHandler); } private void ProcessYubikey(bool success, string message) { if(success) { Device.BeginInvokeOnMainThread(() => { MessagingCenter.Send(Xamarin.Forms.Application.Current, "GotYubiKeyOTP", message); }); } } } }