reset for v2
@@ -1,409 +0,0 @@
|
||||
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 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 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<ILockService>();
|
||||
_deviceInfoService = Resolver.Resolve<IDeviceInfoService>();
|
||||
_cipherService = Resolver.Resolve<ICipherService>();
|
||||
_pushHandler = new iOSPushNotificationHandler(Resolver.Resolve<IPushNotificationListener>());
|
||||
_nfcDelegate = new NFCReaderDelegate((success, message) => ProcessYubikey(success, message));
|
||||
var appIdService = Resolver.Resolve<IAppIdService>();
|
||||
|
||||
var crashManagerDelegate = new HockeyAppCrashManagerDelegate(
|
||||
appIdService, Resolver.Resolve<IAuthService>());
|
||||
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<IAuthService>(),
|
||||
Resolver.Resolve<IConnectivity>(),
|
||||
Resolver.Resolve<IDatabaseService>(),
|
||||
Resolver.Resolve<ISyncService>(),
|
||||
Resolver.Resolve<ISettings>(),
|
||||
_lockService,
|
||||
Resolver.Resolve<ILocalizeService>(),
|
||||
Resolver.Resolve<IAppInfoService>(),
|
||||
Resolver.Resolve<IAppSettingsService>(),
|
||||
Resolver.Resolve<IDeviceActionService>()));
|
||||
|
||||
// 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, ToolsExtensionPage>(
|
||||
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, bool>(
|
||||
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, bool>(
|
||||
Xamarin.Forms.Application.Current, "ShowStatusBar", (sender, show) =>
|
||||
{
|
||||
UIApplication.SharedApplication.SetStatusBarHidden(!show, false);
|
||||
});
|
||||
|
||||
MessagingCenter.Subscribe<Xamarin.Forms.Application, bool>(
|
||||
Xamarin.Forms.Application.Current, "FullSyncCompleted", async (sender, successfully) =>
|
||||
{
|
||||
if(_deviceInfoService.Version >= 12 && successfully)
|
||||
{
|
||||
await ASHelpers.ReplaceAllIdentities(_cipherService);
|
||||
}
|
||||
});
|
||||
|
||||
MessagingCenter.Subscribe<Xamarin.Forms.Application, Tuple<string, bool>>(
|
||||
Xamarin.Forms.Application.Current, "UpsertedCipher", async (sender, data) =>
|
||||
{
|
||||
if(_deviceInfoService.Version >= 12)
|
||||
{
|
||||
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, Cipher>(
|
||||
Xamarin.Forms.Application.Current, "DeletedCipher", async (sender, cipher) =>
|
||||
{
|
||||
if(_deviceInfoService.Version >= 12)
|
||||
{
|
||||
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>(
|
||||
Xamarin.Forms.Application.Current, "LoggedOut", async (sender) =>
|
||||
{
|
||||
if(_deviceInfoService.Version >= 12)
|
||||
{
|
||||
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();
|
||||
|
||||
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<UIBackgroundFetchResult> 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<IDatabaseService, DatabaseService>();
|
||||
container.RegisterSingleton<ISqlService, SqlService>();
|
||||
container.RegisterSingleton<ISecureStorageService, KeyChainStorageService>();
|
||||
container.RegisterSingleton<ICryptoService, CryptoService>();
|
||||
container.RegisterSingleton<IKeyDerivationService, CommonCryptoKeyDerivationService>();
|
||||
container.RegisterSingleton<IAuthService, AuthService>();
|
||||
container.RegisterSingleton<IFolderService, FolderService>();
|
||||
container.RegisterSingleton<ICollectionService, CollectionService>();
|
||||
container.RegisterSingleton<ICipherService, CipherService>();
|
||||
container.RegisterSingleton<ISyncService, SyncService>();
|
||||
container.RegisterSingleton<IDeviceActionService, DeviceActionService>();
|
||||
container.RegisterSingleton<IAppIdService, AppIdService>();
|
||||
container.RegisterSingleton<IPasswordGenerationService, PasswordGenerationService>();
|
||||
container.RegisterSingleton<ILockService, LockService>();
|
||||
container.RegisterSingleton<IAppInfoService, AppInfoService>();
|
||||
container.RegisterSingleton<IGoogleAnalyticsService, GoogleAnalyticsService>();
|
||||
container.RegisterInstance<IDeviceInfoService>(new DeviceInfoService());
|
||||
container.RegisterSingleton<ILocalizeService, LocalizeService>();
|
||||
container.RegisterSingleton<ILogService, LogService>();
|
||||
container.RegisterSingleton<IHttpService, HttpService>();
|
||||
container.RegisterSingleton<ITokenService, TokenService>();
|
||||
container.RegisterSingleton<ISettingsService, SettingsService>();
|
||||
container.RegisterSingleton<IAppSettingsService, AppSettingsService>();
|
||||
|
||||
// Repositories
|
||||
container.RegisterSingleton<IFolderRepository, FolderRepository>();
|
||||
container.RegisterSingleton<IFolderApiRepository, FolderApiRepository>();
|
||||
container.RegisterSingleton<ICipherRepository, CipherRepository>();
|
||||
container.RegisterSingleton<IAttachmentRepository, AttachmentRepository>();
|
||||
container.RegisterSingleton<IConnectApiRepository, ConnectApiRepository>();
|
||||
container.RegisterSingleton<IDeviceApiRepository, DeviceApiRepository>();
|
||||
container.RegisterSingleton<IAccountsApiRepository, AccountsApiRepository>();
|
||||
container.RegisterSingleton<ICipherApiRepository, CipherApiRepository>();
|
||||
container.RegisterSingleton<ISettingsRepository, SettingsRepository>();
|
||||
container.RegisterSingleton<ISettingsApiRepository, SettingsApiRepository>();
|
||||
container.RegisterSingleton<ITwoFactorApiRepository, TwoFactorApiRepository>();
|
||||
container.RegisterSingleton<ISyncApiRepository, SyncApiRepository>();
|
||||
container.RegisterSingleton<ICollectionRepository, CollectionRepository>();
|
||||
container.RegisterSingleton<ICipherCollectionRepository, CipherCollectionRepository>();
|
||||
|
||||
// Other
|
||||
container.RegisterInstance(CrossConnectivity.Current);
|
||||
container.RegisterInstance(CrossFingerprint.Current);
|
||||
|
||||
Settings = new Settings("group.com.8bit.bitwarden");
|
||||
container.RegisterInstance(Settings);
|
||||
|
||||
// Push
|
||||
container.RegisterSingleton<IPushNotificationListener, PushNotificationListener>();
|
||||
container.RegisterSingleton<IPushNotificationService, iOSPushNotificationService>();
|
||||
|
||||
FFImageLoading.Forms.Platform.CachedImageRenderer.Init();
|
||||
Resolver.SetResolver(new SimpleInjectorResolver(container));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method sends any queued hits when the app enters the background.
|
||||
/// ref: https://developers.google.com/analytics/devguides/collection/ios/v3/dispatch
|
||||
/// </summary>
|
||||
private void SendGoogleAnalyticsHitsInBackground()
|
||||
{
|
||||
var taskExpired = false;
|
||||
var taskId = UIApplication.SharedApplication.BeginBackgroundTask(() =>
|
||||
{
|
||||
taskExpired = true;
|
||||
});
|
||||
|
||||
if(taskId == UIApplication.BackgroundTaskInvalid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessYubikey(bool success, string message)
|
||||
{
|
||||
if(success)
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
MessagingCenter.Send(Xamarin.Forms.Application.Current, "GotYubiKeyOTP", message);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Bit.iOS.Controls;
|
||||
using UIKit;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.iOS;
|
||||
|
||||
[assembly: ExportRenderer(typeof(ContentPage), typeof(ContentPageRenderer))]
|
||||
namespace Bit.iOS.Controls
|
||||
{
|
||||
public class ContentPageRenderer : PageRenderer
|
||||
{
|
||||
public override void ViewWillAppear(bool animated)
|
||||
{
|
||||
base.ViewWillAppear(animated);
|
||||
|
||||
var contentPage = Element as ContentPage;
|
||||
if(contentPage == null || NavigationController == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var itemsInfo = contentPage.ToolbarItems;
|
||||
|
||||
var navigationItem = NavigationController.TopViewController.NavigationItem;
|
||||
var leftNativeButtons = (navigationItem.LeftBarButtonItems ?? new UIBarButtonItem[] { }).ToList();
|
||||
var rightNativeButtons = (navigationItem.RightBarButtonItems ?? new UIBarButtonItem[] { }).ToList();
|
||||
|
||||
var newLeftButtons = new List<UIBarButtonItem>();
|
||||
var newRightButtons = new List<UIBarButtonItem>();
|
||||
|
||||
rightNativeButtons.ForEach(nativeItem =>
|
||||
{
|
||||
// Use reflection to get Xamarin private field "_item"
|
||||
var field = nativeItem.GetType().GetField("_item", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if(field == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var info = field.GetValue(nativeItem) as ToolbarItem;
|
||||
if(info == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(info.Priority < 0)
|
||||
{
|
||||
newLeftButtons.Add(nativeItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
newRightButtons.Add(nativeItem);
|
||||
}
|
||||
});
|
||||
|
||||
leftNativeButtons.ForEach(nativeItem =>
|
||||
{
|
||||
newLeftButtons.Add(nativeItem);
|
||||
});
|
||||
|
||||
navigationItem.RightBarButtonItems = newRightButtons.ToArray();
|
||||
navigationItem.LeftBarButtonItems = newLeftButtons.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
using System;
|
||||
using Bit.iOS.Controls;
|
||||
using UIKit;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.iOS;
|
||||
using System.ComponentModel;
|
||||
|
||||
[assembly: ExportRenderer(typeof(Button), typeof(CustomButtonRenderer))]
|
||||
namespace Bit.iOS.Controls
|
||||
{
|
||||
public class CustomButtonRenderer : ButtonRenderer
|
||||
{
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
|
||||
var view = e.NewElement as Button;
|
||||
if(Control != null && view != null)
|
||||
{
|
||||
UpdateFont();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnElementPropertyChanged(sender, e);
|
||||
|
||||
if(e.PropertyName == Button.FontProperty.PropertyName)
|
||||
{
|
||||
UpdateFont();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateFont()
|
||||
{
|
||||
var pointSize = UIFontDescriptor.PreferredBody.PointSize;
|
||||
|
||||
var size = Element.FontSize;
|
||||
if(size == Device.GetNamedSize(NamedSize.Large, typeof(Button)))
|
||||
{
|
||||
pointSize *= 1.3f;
|
||||
}
|
||||
else if(size == Device.GetNamedSize(NamedSize.Small, typeof(Button)))
|
||||
{
|
||||
pointSize *= .8f;
|
||||
}
|
||||
else if(size == Device.GetNamedSize(NamedSize.Micro, typeof(Button)))
|
||||
{
|
||||
pointSize *= .6f;
|
||||
}
|
||||
else if(size != Device.GetNamedSize(NamedSize.Default, typeof(Button)))
|
||||
{
|
||||
// not using dynamic font sizes, return
|
||||
return;
|
||||
}
|
||||
|
||||
Control.Font = UIFont.FromDescriptor(Element.Font.ToUIFont().FontDescriptor, pointSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using Bit.iOS.Controls;
|
||||
using UIKit;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.iOS;
|
||||
|
||||
[assembly: ExportRenderer(typeof(Label), typeof(CustomLabelRenderer))]
|
||||
namespace Bit.iOS.Controls
|
||||
{
|
||||
public class CustomLabelRenderer : LabelRenderer
|
||||
{
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
|
||||
var view = e.NewElement as Label;
|
||||
if(Control != null && view != null)
|
||||
{
|
||||
UpdateFont();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnElementPropertyChanged(sender, e);
|
||||
|
||||
if(e.PropertyName == Label.TextColorProperty.PropertyName)
|
||||
{
|
||||
UpdateFont();
|
||||
}
|
||||
else if(e.PropertyName == Label.FontProperty.PropertyName)
|
||||
{
|
||||
UpdateFont();
|
||||
}
|
||||
else if(e.PropertyName == Label.TextProperty.PropertyName)
|
||||
{
|
||||
UpdateFont();
|
||||
}
|
||||
else if(e.PropertyName == Label.FormattedTextProperty.PropertyName)
|
||||
{
|
||||
UpdateFont();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateFont()
|
||||
{
|
||||
var pointSize = UIFontDescriptor.PreferredBody.PointSize;
|
||||
|
||||
var size = Element.FontSize;
|
||||
if(size == Device.GetNamedSize(NamedSize.Large, typeof(Label)))
|
||||
{
|
||||
pointSize *= 1.3f;
|
||||
}
|
||||
else if(size == Device.GetNamedSize(NamedSize.Small, typeof(Label)))
|
||||
{
|
||||
pointSize *= .8f;
|
||||
}
|
||||
else if(size == Device.GetNamedSize(NamedSize.Micro, typeof(Label)))
|
||||
{
|
||||
pointSize *= .6f;
|
||||
}
|
||||
else if(size != Device.GetNamedSize(NamedSize.Default, typeof(Label)))
|
||||
{
|
||||
// not using dynamic font sizes, return
|
||||
return;
|
||||
}
|
||||
|
||||
Control.Font = UIFont.FromDescriptor(Element.Font.ToUIFont().FontDescriptor, pointSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
using Bit.App.Controls;
|
||||
using Bit.iOS.Controls;
|
||||
using System.ComponentModel;
|
||||
using UIKit;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.iOS;
|
||||
|
||||
[assembly: ExportRenderer(typeof(SearchBar), typeof(CustomSearchBarRenderer))]
|
||||
namespace Bit.iOS.Controls
|
||||
{
|
||||
public class CustomSearchBarRenderer : SearchBarRenderer
|
||||
{
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
|
||||
var view = e.NewElement;
|
||||
if(view != null)
|
||||
{
|
||||
Control.SearchBarStyle = UISearchBarStyle.Minimal;
|
||||
Control.BarStyle = UIBarStyle.BlackTranslucent;
|
||||
Control.ShowsCancelButton = Control.IsFirstResponder;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnElementPropertyChanged(sender, e);
|
||||
var view = Element;
|
||||
Control.ShowsCancelButton = Control.IsFirstResponder;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
using System;
|
||||
using Bit.iOS.Controls;
|
||||
using UIKit;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.iOS;
|
||||
using System.ComponentModel;
|
||||
using Bit.App.Controls;
|
||||
|
||||
[assembly: ExportRenderer(typeof(ExtendedButton), typeof(ExtendedButtonRenderer))]
|
||||
namespace Bit.iOS.Controls
|
||||
{
|
||||
public class ExtendedButtonRenderer : CustomButtonRenderer
|
||||
{
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
SetPadding();
|
||||
SetUppercase();
|
||||
}
|
||||
|
||||
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnElementPropertyChanged(sender, e);
|
||||
if(e.PropertyName == ExtendedButton.PaddingProperty.PropertyName)
|
||||
{
|
||||
SetPadding();
|
||||
}
|
||||
else if(e.PropertyName == ExtendedButton.UppercaseProperty.PropertyName)
|
||||
{
|
||||
SetUppercase();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetPadding()
|
||||
{
|
||||
var element = Element as ExtendedButton;
|
||||
if(element != null)
|
||||
{
|
||||
Control.ContentEdgeInsets = new UIEdgeInsets(
|
||||
(int)element.Padding.Top,
|
||||
(int)element.Padding.Left,
|
||||
(int)element.Padding.Bottom,
|
||||
(int)element.Padding.Right);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetUppercase()
|
||||
{
|
||||
var element = Element as ExtendedButton;
|
||||
if(element != null && element.Uppercase)
|
||||
{
|
||||
Control.TitleLabel.Text = Control.TitleLabel.Text.ToUpper();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using Bit.App.Controls;
|
||||
using Bit.iOS.Controls;
|
||||
using UIKit;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.iOS;
|
||||
|
||||
[assembly: ExportRenderer(typeof(ExtendedEditor), typeof(ExtendedEditorRenderer))]
|
||||
namespace Bit.iOS.Controls
|
||||
{
|
||||
public class ExtendedEditorRenderer : EditorRenderer
|
||||
{
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
|
||||
var view = e.NewElement as ExtendedEditor;
|
||||
if(view != null)
|
||||
{
|
||||
var descriptor = UIFontDescriptor.PreferredBody;
|
||||
Control.Font = UIFont.FromDescriptor(descriptor, descriptor.PointSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using Bit.App.Controls;
|
||||
using Bit.iOS.Controls;
|
||||
using CoreAnimation;
|
||||
using CoreGraphics;
|
||||
using Foundation;
|
||||
using UIKit;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.iOS;
|
||||
|
||||
[assembly: ExportRenderer(typeof(ExtendedEntry), typeof(ExtendedEntryRenderer))]
|
||||
namespace Bit.iOS.Controls
|
||||
{
|
||||
public class ExtendedEntryRenderer : EntryRenderer
|
||||
{
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
|
||||
var view = e.NewElement as ExtendedEntry;
|
||||
if(view != null)
|
||||
{
|
||||
SetBorder(view);
|
||||
SetMaxLength(view);
|
||||
UpdateKeyboard();
|
||||
UpdateFont();
|
||||
|
||||
if(view.AllowClear)
|
||||
{
|
||||
Control.ClearButtonMode = UITextFieldViewMode.WhileEditing;
|
||||
}
|
||||
|
||||
if(view.DisableAutocapitalize)
|
||||
{
|
||||
Control.AutocapitalizationType = UITextAutocapitalizationType.None;
|
||||
}
|
||||
|
||||
if(view.Autocorrect.HasValue)
|
||||
{
|
||||
Control.AutocorrectionType = view.Autocorrect.Value ? UITextAutocorrectionType.Yes : UITextAutocorrectionType.No;
|
||||
}
|
||||
|
||||
if(view.HideCursor)
|
||||
{
|
||||
Control.TintColor = UIColor.Clear;
|
||||
}
|
||||
|
||||
if(view.TargetReturnType.HasValue)
|
||||
{
|
||||
switch(view.TargetReturnType.Value)
|
||||
{
|
||||
case App.Enums.ReturnType.Done:
|
||||
Control.ReturnKeyType = UIReturnKeyType.Done;
|
||||
break;
|
||||
case App.Enums.ReturnType.Go:
|
||||
Control.ReturnKeyType = UIReturnKeyType.Go;
|
||||
break;
|
||||
case App.Enums.ReturnType.Next:
|
||||
Control.ReturnKeyType = UIReturnKeyType.Next;
|
||||
break;
|
||||
case App.Enums.ReturnType.Search:
|
||||
Control.ReturnKeyType = UIReturnKeyType.Search;
|
||||
break;
|
||||
case App.Enums.ReturnType.Send:
|
||||
Control.ReturnKeyType = UIReturnKeyType.Send;
|
||||
break;
|
||||
default:
|
||||
Control.ReturnKeyType = UIReturnKeyType.Default;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Control.ShouldReturn += (UITextField tf) =>
|
||||
{
|
||||
view.InvokeCompleted();
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnElementPropertyChanged(sender, e);
|
||||
|
||||
var view = (ExtendedEntry)Element;
|
||||
|
||||
if(e.PropertyName == ExtendedEntry.HasBorderProperty.PropertyName
|
||||
|| e.PropertyName == ExtendedEntry.HasOnlyBottomBorderProperty.PropertyName
|
||||
|| e.PropertyName == ExtendedEntry.BottomBorderColorProperty.PropertyName)
|
||||
{
|
||||
SetBorder(view);
|
||||
}
|
||||
else if(e.PropertyName == Xamarin.Forms.InputView.KeyboardProperty.PropertyName)
|
||||
{
|
||||
UpdateKeyboard();
|
||||
}
|
||||
else if(e.PropertyName == Entry.FontAttributesProperty.PropertyName)
|
||||
{
|
||||
UpdateFont();
|
||||
}
|
||||
else if(e.PropertyName == Entry.FontFamilyProperty.PropertyName)
|
||||
{
|
||||
UpdateFont();
|
||||
}
|
||||
else if(e.PropertyName == Entry.FontSizeProperty.PropertyName)
|
||||
{
|
||||
UpdateFont();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateFont()
|
||||
{
|
||||
var descriptor = UIFontDescriptor.PreferredBody;
|
||||
var pointSize = descriptor.PointSize;
|
||||
|
||||
var size = Element.FontSize;
|
||||
if(size == Device.GetNamedSize(NamedSize.Large, typeof(ExtendedEntry)))
|
||||
{
|
||||
pointSize *= 1.3f;
|
||||
}
|
||||
else if(size == Device.GetNamedSize(NamedSize.Small, typeof(ExtendedEntry)))
|
||||
{
|
||||
pointSize *= .8f;
|
||||
}
|
||||
else if(size == Device.GetNamedSize(NamedSize.Micro, typeof(ExtendedEntry)))
|
||||
{
|
||||
pointSize *= .6f;
|
||||
}
|
||||
else if(size != Device.GetNamedSize(NamedSize.Default, typeof(ExtendedEntry)))
|
||||
{
|
||||
// not using dynamic font sizes, return
|
||||
return;
|
||||
}
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(Element.FontFamily))
|
||||
{
|
||||
Control.Font = UIFont.FromName(Element.FontFamily, pointSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
Control.Font = UIFont.FromDescriptor(descriptor, pointSize);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetBorder(ExtendedEntry view)
|
||||
{
|
||||
if(view.HasOnlyBottomBorder)
|
||||
{
|
||||
var borderLayer = new CALayer();
|
||||
borderLayer.MasksToBounds = true;
|
||||
borderLayer.Frame = new CGRect(0f, Frame.Height / 2, Frame.Width * 2, 1f);
|
||||
borderLayer.BorderColor = view.BottomBorderColor.ToCGColor();
|
||||
borderLayer.BorderWidth = 1f;
|
||||
|
||||
Control.Layer.AddSublayer(borderLayer);
|
||||
Control.BorderStyle = UITextBorderStyle.None;
|
||||
}
|
||||
else if(view.HasBorder)
|
||||
{
|
||||
Control.BorderStyle = UITextBorderStyle.RoundedRect;
|
||||
}
|
||||
else
|
||||
{
|
||||
Control.BorderStyle = UITextBorderStyle.None;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetMaxLength(ExtendedEntry view)
|
||||
{
|
||||
Control.ShouldChangeCharacters = (textField, range, replacementString) =>
|
||||
{
|
||||
var newLength = textField.Text.Length + replacementString.Length - range.Length;
|
||||
return newLength <= view.TargetMaxLength;
|
||||
};
|
||||
}
|
||||
|
||||
private void UpdateKeyboard()
|
||||
{
|
||||
if(Element.Keyboard == Keyboard.Numeric)
|
||||
{
|
||||
Control.KeyboardType = UIKeyboardType.NumberPad;
|
||||
}
|
||||
else
|
||||
{
|
||||
Control.ApplyKeyboard(Element.Keyboard);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
using Bit.App.Controls;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using UIKit;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.iOS;
|
||||
|
||||
[assembly: ExportRenderer(typeof(ExtendedListView), typeof(Bit.iOS.Controls.ExtendedListViewRenderer))]
|
||||
namespace Bit.iOS.Controls
|
||||
{
|
||||
public class ExtendedListViewRenderer : ListViewRenderer
|
||||
{
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
|
||||
// primary color
|
||||
Control.SectionIndexColor = new UIColor(red: 0.24f, green: 0.55f, blue: 0.74f, alpha: 1.0f);
|
||||
|
||||
if(e.NewElement is ExtendedListView view)
|
||||
{
|
||||
SetMargin(view);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnElementPropertyChanged(sender, e);
|
||||
if(e.PropertyName == View.MarginProperty.PropertyName)
|
||||
{
|
||||
SetMargin(Element);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetMargin(ListView view)
|
||||
{
|
||||
Control.ContentInset = new UIEdgeInsets(
|
||||
new nfloat(view.Margin.Top),
|
||||
new nfloat(view.Margin.Left),
|
||||
new nfloat(view.Margin.Bottom),
|
||||
new nfloat(view.Margin.Right));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using Bit.App.Controls;
|
||||
using Bit.iOS.Controls;
|
||||
using UIKit;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.iOS;
|
||||
|
||||
[assembly: ExportRenderer(typeof(ExtendedPicker), typeof(ExtendedPickerRenderer))]
|
||||
namespace Bit.iOS.Controls
|
||||
{
|
||||
public class ExtendedPickerRenderer : PickerRenderer
|
||||
{
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
|
||||
var view = e.NewElement as ExtendedPicker;
|
||||
if(view != null)
|
||||
{
|
||||
var descriptor = UIFontDescriptor.PreferredBody;
|
||||
Control.Font = UIFont.FromDescriptor(descriptor, descriptor.PointSize);
|
||||
|
||||
SetBorder(view);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnElementPropertyChanged(sender, e);
|
||||
|
||||
var view = (ExtendedPicker)Element;
|
||||
|
||||
if(e.PropertyName == ExtendedPicker.HasBorderProperty.PropertyName)
|
||||
{
|
||||
SetBorder(view);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetBorder(ExtendedPicker view)
|
||||
{
|
||||
Control.BorderStyle = view.HasBorder ? UITextBorderStyle.RoundedRect : UITextBorderStyle.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using Bit.App.Controls;
|
||||
using Bit.iOS.Controls;
|
||||
using UIKit;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.iOS;
|
||||
|
||||
[assembly: ExportRenderer(typeof(ExtendedSwitchCell), typeof(ExtendedSwitchCellRenderer))]
|
||||
namespace Bit.iOS.Controls
|
||||
{
|
||||
public class ExtendedSwitchCellRenderer : SwitchCellRenderer
|
||||
{
|
||||
public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
|
||||
{
|
||||
var extendedCell = (ExtendedSwitchCell)item;
|
||||
var cell = base.GetCell(item, reusableCell, tv);
|
||||
|
||||
if(cell != null)
|
||||
{
|
||||
cell.BackgroundColor = extendedCell.BackgroundColor.ToUIColor();
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
using System;
|
||||
using Bit.App.Controls;
|
||||
using Bit.iOS.Controls;
|
||||
using Foundation;
|
||||
using UIKit;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.iOS;
|
||||
|
||||
[assembly: ExportRenderer(typeof(ExtendedTabbedPage), typeof(ExtendedTabbedPageRenderer))]
|
||||
namespace Bit.iOS.Controls
|
||||
{
|
||||
public class ExtendedTabbedPageRenderer : TabbedRenderer
|
||||
{
|
||||
protected override void OnElementChanged(VisualElementChangedEventArgs e)
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
|
||||
var page = (ExtendedTabbedPage)Element;
|
||||
|
||||
TabBar.TintColor = page.TintColor.ToUIColor();
|
||||
TabBar.BackgroundColor = page.BackgroundColor.ToUIColor();
|
||||
|
||||
if(page.NoBorder)
|
||||
{
|
||||
// remove top border
|
||||
// ref: http://stackoverflow.com/questions/14371343/ios-uitabbar-remove-top-shadow-gradient-line
|
||||
TabBar.SetValueForKeyPath(FromObject(true), new NSString("_hidesShadow"));
|
||||
}
|
||||
}
|
||||
|
||||
public override void ViewWillAppear(bool animated)
|
||||
{
|
||||
if(TabBar?.Items == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(Element is TabbedPage tabs)
|
||||
{
|
||||
for(int i = 0; i < TabBar.Items.Length; i++)
|
||||
{
|
||||
UpdateItem(TabBar.Items[i], tabs.Children[i].Icon);
|
||||
}
|
||||
}
|
||||
|
||||
base.ViewWillAppear(animated);
|
||||
}
|
||||
|
||||
private void UpdateItem(UITabBarItem item, string icon)
|
||||
{
|
||||
if(item == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Only show icon
|
||||
// ref: https://stackoverflow.com/questions/26494130/remove-tab-bar-item-text-show-only-image
|
||||
item.Title = string.Empty;
|
||||
if(UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone)
|
||||
{
|
||||
item.ImageInsets = new UIEdgeInsets(6, 0, -6, 0);
|
||||
}
|
||||
|
||||
// Set selected icon
|
||||
icon = string.Concat(icon, "_selected");
|
||||
if(item?.SelectedImage?.AccessibilityIdentifier == icon)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
item.SelectedImage = UIImage.FromBundle(icon);
|
||||
item.SelectedImage.AccessibilityIdentifier = icon;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using Bit.App.Controls;
|
||||
using Bit.iOS.Controls;
|
||||
using CoreGraphics;
|
||||
using Foundation;
|
||||
using UIKit;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.iOS;
|
||||
|
||||
[assembly: ExportRenderer(typeof(ExtendedTableView), typeof(ExtendedTableViewRenderer))]
|
||||
namespace Bit.iOS.Controls
|
||||
{
|
||||
public class ExtendedTableViewRenderer : TableViewRenderer
|
||||
{
|
||||
public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
|
||||
{
|
||||
Control.LayoutIfNeeded();
|
||||
var size = new Size(Control.ContentSize.Width, Control.ContentSize.Height);
|
||||
return new SizeRequest(size);
|
||||
}
|
||||
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<TableView> e)
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
|
||||
var view = e.NewElement as ExtendedTableView;
|
||||
if(view != null)
|
||||
{
|
||||
SetScrolling(view);
|
||||
SetSelection(view);
|
||||
UpdateRowHeight(view);
|
||||
UpdateEstimatedRowHeight(view);
|
||||
UpdateSeparatorColor(view);
|
||||
SetSource();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnElementPropertyChanged(sender, e);
|
||||
|
||||
var view = (ExtendedTableView)Element;
|
||||
|
||||
if(e.PropertyName == ExtendedTableView.EnableScrollingProperty.PropertyName)
|
||||
{
|
||||
SetScrolling(view);
|
||||
}
|
||||
else if(e.PropertyName == ExtendedTableView.RowHeightProperty.PropertyName)
|
||||
{
|
||||
UpdateRowHeight(view);
|
||||
}
|
||||
else if(e.PropertyName == ExtendedTableView.EnableSelectionProperty.PropertyName)
|
||||
{
|
||||
SetSelection(view);
|
||||
}
|
||||
else if(e.PropertyName == TableView.HasUnevenRowsProperty.PropertyName)
|
||||
{
|
||||
SetSource();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetSource()
|
||||
{
|
||||
var view = (ExtendedTableView)Element;
|
||||
Control.Source = new CustomTableViewModelRenderer(view);
|
||||
}
|
||||
|
||||
private void SetScrolling(ExtendedTableView view)
|
||||
{
|
||||
Control.ScrollEnabled = view.EnableScrolling;
|
||||
}
|
||||
|
||||
private void SetSelection(ExtendedTableView view)
|
||||
{
|
||||
Control.AllowsSelection = view.EnableSelection;
|
||||
}
|
||||
|
||||
private void UpdateRowHeight(ExtendedTableView view)
|
||||
{
|
||||
var rowHeight = view.RowHeight;
|
||||
if(view.HasUnevenRows && rowHeight == -1)
|
||||
{
|
||||
Control.RowHeight = UITableView.AutomaticDimension;
|
||||
}
|
||||
else
|
||||
{
|
||||
Control.RowHeight = rowHeight <= 0 ? 44 : rowHeight;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateEstimatedRowHeight(ExtendedTableView view)
|
||||
{
|
||||
if(view.HasUnevenRows && view.RowHeight == -1)
|
||||
{
|
||||
Control.EstimatedRowHeight = view.EstimatedRowHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
Control.EstimatedRowHeight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSeparatorColor(ExtendedTableView view)
|
||||
{
|
||||
Control.SeparatorColor = view.SeparatorColor.ToUIColor(UIColor.Gray);
|
||||
}
|
||||
|
||||
public class CustomTableViewModelRenderer : UnEvenTableViewModelRenderer
|
||||
{
|
||||
private readonly ExtendedTableView _view;
|
||||
private bool _didRedraw = false;
|
||||
|
||||
public CustomTableViewModelRenderer(ExtendedTableView model)
|
||||
: base(model)
|
||||
{
|
||||
_view = model;
|
||||
}
|
||||
|
||||
public override async void WillDisplay(UITableView tableView, UITableViewCell cell, NSIndexPath indexPath)
|
||||
{
|
||||
// ref https://stackoverflow.com/a/48076188/1090359
|
||||
if(!_didRedraw && _view.WrappingStackLayout != null)
|
||||
{
|
||||
_didRedraw = true;
|
||||
await _view.WrappingStackLayout()?.RedrawIfNeededAsync(10, false);
|
||||
}
|
||||
}
|
||||
|
||||
public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath)
|
||||
{
|
||||
if(_view.HasUnevenRows)
|
||||
{
|
||||
return UITableView.AutomaticDimension;
|
||||
}
|
||||
|
||||
return base.GetHeightForRow(tableView, indexPath);
|
||||
}
|
||||
|
||||
public override nfloat GetHeightForHeader(UITableView tableView, nint section)
|
||||
{
|
||||
if(_view.NoHeader && section == 0)
|
||||
{
|
||||
return 0.00001f;
|
||||
}
|
||||
|
||||
return base.GetHeightForHeader(tableView, section);
|
||||
}
|
||||
|
||||
public override UIView GetViewForHeader(UITableView tableView, nint section)
|
||||
{
|
||||
if(_view.NoHeader && section == 0)
|
||||
{
|
||||
return new UIView(new CGRect(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override nfloat GetHeightForFooter(UITableView tableView, nint section)
|
||||
{
|
||||
if(_view.NoFooter && (section + 1) == NumberOfSections(tableView))
|
||||
{
|
||||
return 0.00001f;
|
||||
}
|
||||
|
||||
return 10f;
|
||||
}
|
||||
|
||||
public override UIView GetViewForFooter(UITableView tableView, nint section)
|
||||
{
|
||||
if(_view.NoFooter && (section + 1) == NumberOfSections(tableView))
|
||||
{
|
||||
return new UIView(new CGRect(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
using Bit.App.Controls;
|
||||
using Bit.iOS.Controls;
|
||||
using UIKit;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.iOS;
|
||||
using CoreGraphics;
|
||||
|
||||
[assembly: ExportRenderer(typeof(ExtendedTextCell), typeof(ExtendedTextCellRenderer))]
|
||||
namespace Bit.iOS.Controls
|
||||
{
|
||||
public class ExtendedTextCellRenderer : TextCellRenderer
|
||||
{
|
||||
public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
|
||||
{
|
||||
var extendedCell = (ExtendedTextCell)item;
|
||||
var cell = base.GetCell(item, reusableCell, tv);
|
||||
|
||||
if(cell != null)
|
||||
{
|
||||
cell.BackgroundColor = extendedCell.BackgroundColor.ToUIColor();
|
||||
if(extendedCell.ShowDisclousure)
|
||||
{
|
||||
cell.Accessory = UITableViewCellAccessory.DisclosureIndicator;
|
||||
if(!string.IsNullOrEmpty(extendedCell.DisclousureImage))
|
||||
{
|
||||
var detailDisclosureButton = UIButton.FromType(UIButtonType.Custom);
|
||||
detailDisclosureButton.SetImage(UIImage.FromBundle(extendedCell.DisclousureImage), UIControlState.Normal);
|
||||
|
||||
try
|
||||
{
|
||||
detailDisclosureButton.SetImage(UIImage.FromBundle(extendedCell.DisclousureImage + "_selected"), UIControlState.Selected);
|
||||
}
|
||||
catch
|
||||
{
|
||||
detailDisclosureButton.SetImage(UIImage.FromBundle(extendedCell.DisclousureImage), UIControlState.Selected);
|
||||
}
|
||||
|
||||
detailDisclosureButton.Frame = new CGRect(0f, 0f, 50f, 100f);
|
||||
detailDisclosureButton.TouchUpInside += (sender, e) =>
|
||||
{
|
||||
extendedCell.OnDisclousureTapped();
|
||||
};
|
||||
cell.AccessoryView = detailDisclosureButton;
|
||||
}
|
||||
}
|
||||
|
||||
WireUpForceUpdateSizeRequested(item, cell, tv);
|
||||
UpdateLineBreakMode(cell.DetailTextLabel, extendedCell.DetailLineBreakMode);
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
private void UpdateLineBreakMode(UILabel label, LineBreakMode lineBreakMode)
|
||||
{
|
||||
if(label == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch(lineBreakMode)
|
||||
{
|
||||
case LineBreakMode.NoWrap:
|
||||
label.LineBreakMode = UILineBreakMode.Clip;
|
||||
label.Lines = 1;
|
||||
break;
|
||||
case LineBreakMode.WordWrap:
|
||||
label.LineBreakMode = UILineBreakMode.WordWrap;
|
||||
label.Lines = 0;
|
||||
break;
|
||||
case LineBreakMode.CharacterWrap:
|
||||
label.LineBreakMode = UILineBreakMode.CharacterWrap;
|
||||
label.Lines = 0;
|
||||
break;
|
||||
case LineBreakMode.HeadTruncation:
|
||||
label.LineBreakMode = UILineBreakMode.HeadTruncation;
|
||||
label.Lines = 1;
|
||||
break;
|
||||
case LineBreakMode.MiddleTruncation:
|
||||
label.LineBreakMode = UILineBreakMode.MiddleTruncation;
|
||||
label.Lines = 1;
|
||||
break;
|
||||
case LineBreakMode.TailTruncation:
|
||||
label.LineBreakMode = UILineBreakMode.TailTruncation;
|
||||
label.Lines = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using Bit.App.Controls;
|
||||
using Bit.iOS.Controls;
|
||||
using UIKit;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.iOS;
|
||||
|
||||
[assembly: ExportRenderer(typeof(ExtendedViewCell), typeof(ExtendedViewCellRenderer))]
|
||||
namespace Bit.iOS.Controls
|
||||
{
|
||||
public class ExtendedViewCellRenderer : ViewCellRenderer
|
||||
{
|
||||
public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
|
||||
{
|
||||
var extendedCell = (ExtendedViewCell)item;
|
||||
var cell = base.GetCell(item, reusableCell, tv);
|
||||
|
||||
if(cell != null)
|
||||
{
|
||||
cell.BackgroundColor = extendedCell.BackgroundColor.ToUIColor();
|
||||
if(extendedCell.ShowDisclousure)
|
||||
{
|
||||
cell.Accessory = UITableViewCellAccessory.DisclosureIndicator;
|
||||
}
|
||||
}
|
||||
|
||||
WireUpForceUpdateSizeRequested(item, cell, tv);
|
||||
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
using System.IO;
|
||||
using Foundation;
|
||||
using WebKit;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.iOS;
|
||||
using Bit.App.Controls;
|
||||
using Bit.iOS.Controls;
|
||||
|
||||
[assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
|
||||
namespace Bit.iOS.Controls
|
||||
{
|
||||
public class HybridWebViewRenderer : ViewRenderer<HybridWebView, WKWebView>, IWKScriptMessageHandler
|
||||
{
|
||||
private const string JSFunction =
|
||||
"function invokeCSharpAction(data){window.webkit.messageHandlers.invokeAction.postMessage(data);}";
|
||||
|
||||
private WKUserContentController _userController;
|
||||
|
||||
protected override void OnElementChanged(ElementChangedEventArgs<HybridWebView> e)
|
||||
{
|
||||
base.OnElementChanged(e);
|
||||
|
||||
if(Control == null)
|
||||
{
|
||||
_userController = new WKUserContentController();
|
||||
var script = new WKUserScript(new NSString(JSFunction), WKUserScriptInjectionTime.AtDocumentEnd, false);
|
||||
_userController.AddUserScript(script);
|
||||
_userController.AddScriptMessageHandler(this, "invokeAction");
|
||||
|
||||
var config = new WKWebViewConfiguration { UserContentController = _userController };
|
||||
var webView = new WKWebView(Frame, config);
|
||||
SetNativeControl(webView);
|
||||
}
|
||||
|
||||
if(e.OldElement != null)
|
||||
{
|
||||
_userController.RemoveAllUserScripts();
|
||||
_userController.RemoveScriptMessageHandler("invokeAction");
|
||||
var hybridWebView = e.OldElement as HybridWebView;
|
||||
hybridWebView.Cleanup();
|
||||
}
|
||||
|
||||
if(e.NewElement != null)
|
||||
{
|
||||
Control.LoadRequest(new NSUrlRequest(new NSUrl(Element.Uri)));
|
||||
}
|
||||
}
|
||||
|
||||
public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
|
||||
{
|
||||
Element.InvokeAction(message.Body.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +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>
|
||||
</array>
|
||||
<key>com.apple.developer.associated-domains</key>
|
||||
<array>
|
||||
<string>webcredentials:bitwarden.com</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,115 +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>10.0</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>CFBundleDisplayName</key>
|
||||
<string>Bitwarden</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.8bit.bitwarden</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>46</string>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.22.0</string>
|
||||
<key>UIMainStoryboardFile~ipad</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>bitwarden</string>
|
||||
<string>org-appextension-feature-password-management</string>
|
||||
</array>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>com.8bit.bitwarden.url</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<true/>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<true/>
|
||||
<key>ITSEncryptionExportComplianceCode</key>
|
||||
<string>ecf076d3-4824-4d7b-b716-2a9a47d7d296</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Bitwarden</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<dict>
|
||||
<key>arm64</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<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>
|
||||
</array>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</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>XSAppIconAssets</key>
|
||||
<string>Resources/Assets.xcassets/AppIcons.appiconset</string>
|
||||
<key>CFBundleIconName</key>
|
||||
<string>AppIcon</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>
|
||||
@@ -1,92 +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" customColorSpace="calibratedWhite" colorSpace="calibratedRGB" red="0.92549019607843142" green="0.94117647058823528" blue="0.96078431372549022" alpha="1"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" id="16" translatesAutoresizingMaskIntoConstraints="NO" image="logo.png">
|
||||
<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>
|
||||
<connections/>
|
||||
</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="logo.png" 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>
|
||||
@@ -1,22 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Foundation;
|
||||
using UIKit;
|
||||
|
||||
namespace Bit.iOS
|
||||
{
|
||||
public class Application
|
||||
{
|
||||
// This is the main entry point of the application.
|
||||
static void Main(string[] args)
|
||||
{
|
||||
ObjCRuntime.Dlfcn.dlopen(ObjCRuntime.Constants.libSystemLibrary, 0);
|
||||
|
||||
// if you want to use a different Application Delegate class from "AppDelegate"
|
||||
// you can specify it here.
|
||||
UIApplication.Main(args, null, "AppDelegate");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
using CoreNFC;
|
||||
using Foundation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Bit.iOS
|
||||
{
|
||||
public class NFCReaderDelegate : NFCNdefReaderSessionDelegate
|
||||
{
|
||||
private Regex _otpPattern = new Regex("^.*?([cbdefghijklnrtuv]{32,64})$");
|
||||
private Action<bool, string> _callback;
|
||||
|
||||
public NFCReaderDelegate(Action<bool, string> callback)
|
||||
{
|
||||
_callback = callback;
|
||||
}
|
||||
|
||||
public override void DidDetect(NFCNdefReaderSession session, NFCNdefMessage[] messages)
|
||||
{
|
||||
var results = new List<string>();
|
||||
foreach(var message in messages)
|
||||
{
|
||||
foreach(var record in message.Records)
|
||||
{
|
||||
try
|
||||
{
|
||||
results.Add(new NSString(record.Payload, NSStringEncoding.UTF8));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
foreach(var result in results)
|
||||
{
|
||||
var matches = _otpPattern.Matches(result);
|
||||
if(matches.Count > 0 && matches[0].Groups.Count > 1)
|
||||
{
|
||||
var otp = matches[0].Groups[1].ToString();
|
||||
_callback.Invoke(true, otp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_callback.Invoke(false, "No tags were read.");
|
||||
}
|
||||
|
||||
public override void DidInvalidate(NFCNdefReaderSession session, NSError error)
|
||||
{
|
||||
_callback.Invoke(false, error?.LocalizedDescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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("8bit Solutions LLC")]
|
||||
[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")]
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 800 B |
|
Before Width: | Height: | Size: 863 B |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 406 B |
|
Before Width: | Height: | Size: 659 B |
|
Before Width: | Height: | Size: 994 B |
|
Before Width: | Height: | Size: 446 B |
|
Before Width: | Height: | Size: 741 B |
|
Before Width: | Height: | Size: 1005 B |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 478 B |
|
Before Width: | Height: | Size: 802 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 229 B |
|
Before Width: | Height: | Size: 326 B |
|
Before Width: | Height: | Size: 234 B |
|
Before Width: | Height: | Size: 350 B |
|
Before Width: | Height: | Size: 487 B |
|
Before Width: | Height: | Size: 677 B |
|
Before Width: | Height: | Size: 779 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 610 B |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 590 B |
|
Before Width: | Height: | Size: 1005 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 878 B |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 533 B |
|
Before Width: | Height: | Size: 941 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 378 B |
|
Before Width: | Height: | Size: 556 B |
|
Before Width: | Height: | Size: 707 B |
|
Before Width: | Height: | Size: 374 B |
|
Before Width: | Height: | Size: 629 B |
|
Before Width: | Height: | Size: 884 B |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 607 B |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 659 B |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 437 B |
|
Before Width: | Height: | Size: 663 B |
|
Before Width: | Height: | Size: 877 B |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 388 B |
|
Before Width: | Height: | Size: 598 B |
|
Before Width: | Height: | Size: 810 B |
|
Before Width: | Height: | Size: 401 B |