mirror of
https://github.com/bitwarden/android.git
synced 2024-12-26 19:08:32 +03:00
PM-3350 Fix Avatar toolbar icon on extensions to load properly and to take advantage of using directly SkiaSharp to do the native conversion to UIImage. Also improved the toolbar item so that size is set appropriately.
This commit is contained in:
parent
f539bf051d
commit
63904fd303
16 changed files with 350 additions and 400 deletions
62
src/Core/Controls/Avatar/AvatarImageSource.cs
Normal file
62
src/Core/Controls/Avatar/AvatarImageSource.cs
Normal file
|
@ -0,0 +1,62 @@
|
|||
using SkiaSharp;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
public class AvatarImageSource : StreamImageSource
|
||||
{
|
||||
private readonly string _text;
|
||||
private readonly string _id;
|
||||
private readonly string _color;
|
||||
private readonly AvatarInfo _avatarInfo;
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj is AvatarImageSource avatar)
|
||||
{
|
||||
return avatar._id == _id && avatar._text == _text && avatar._color == _color;
|
||||
}
|
||||
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode() => _id?.GetHashCode() ?? _text?.GetHashCode() ?? -1;
|
||||
|
||||
public AvatarImageSource(string userId = null, string name = null, string email = null, string color = null)
|
||||
{
|
||||
_id = userId;
|
||||
_text = name;
|
||||
if (string.IsNullOrWhiteSpace(_text))
|
||||
{
|
||||
_text = email;
|
||||
}
|
||||
_color = color;
|
||||
|
||||
//Workaround: [MAUI-Migration] There is currently a bug in MAUI where the actual size of the image is used instead of the size it should occupy in the Toolbar.
|
||||
//This causes some issues with the position of the icon. As a workaround we make the icon smaller until this is fixed.
|
||||
//Github issues: https://github.com/dotnet/maui/issues/12359 and https://github.com/dotnet/maui/pull/17120
|
||||
_avatarInfo = new AvatarInfo(userId, name, email, color, DeviceInfo.Platform == DevicePlatform.iOS ? 20 : 50);
|
||||
}
|
||||
|
||||
public override Func<CancellationToken, Task<Stream>> Stream => GetStreamAsync;
|
||||
|
||||
private Task<Stream> GetStreamAsync(CancellationToken userToken = new CancellationToken())
|
||||
{
|
||||
var result = Draw();
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
private Stream Draw()
|
||||
{
|
||||
using (var img = SKAvatarImageHelper.Draw(_avatarInfo))
|
||||
{
|
||||
var data = img.Encode(SKEncodedImageFormat.Png, 100);
|
||||
return data?.AsStream(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
63
src/Core/Controls/Avatar/AvatarInfo.cs
Normal file
63
src/Core/Controls/Avatar/AvatarInfo.cs
Normal file
|
@ -0,0 +1,63 @@
|
|||
using Bit.Core.Utilities;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
public struct AvatarInfo
|
||||
{
|
||||
private const string DEFAULT_BACKGROUND_COLOR = "#33ffffff";
|
||||
|
||||
public AvatarInfo(string? userId = null, string? name = null, string? email = null, string? color = null, int size = 50)
|
||||
{
|
||||
Size = size;
|
||||
var text = string.IsNullOrWhiteSpace(name) ? email : name;
|
||||
|
||||
string? upperCaseText = null;
|
||||
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
CharsToDraw = "..";
|
||||
}
|
||||
else if (text.Length > 1)
|
||||
{
|
||||
upperCaseText = text.ToUpper();
|
||||
CharsToDraw = GetFirstLetters(upperCaseText, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
CharsToDraw = upperCaseText = text.ToUpper();
|
||||
}
|
||||
|
||||
BackgroundColor = color ?? CoreHelpers.StringToColor(userId ?? upperCaseText, DEFAULT_BACKGROUND_COLOR);
|
||||
TextColor = CoreHelpers.TextColorFromBgColor(BackgroundColor);
|
||||
}
|
||||
|
||||
public string CharsToDraw { get; }
|
||||
public string BackgroundColor { get; }
|
||||
public string TextColor { get; }
|
||||
public int Size { get; }
|
||||
|
||||
private static string GetFirstLetters(string data, int charCount)
|
||||
{
|
||||
var sanitizedData = data.Trim();
|
||||
var parts = sanitizedData.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (parts.Length > 1 && charCount <= 2)
|
||||
{
|
||||
var text = string.Empty;
|
||||
for (var i = 0; i < charCount; i++)
|
||||
{
|
||||
text += parts[i][0];
|
||||
}
|
||||
return text;
|
||||
}
|
||||
if (sanitizedData.Length > 2)
|
||||
{
|
||||
return sanitizedData.Substring(0, 2);
|
||||
}
|
||||
return sanitizedData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
63
src/Core/Controls/Avatar/SKAvatarImageHelper.cs
Normal file
63
src/Core/Controls/Avatar/SKAvatarImageHelper.cs
Normal file
|
@ -0,0 +1,63 @@
|
|||
using SkiaSharp;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
public static class SKAvatarImageHelper
|
||||
{
|
||||
public static SKImage Draw(AvatarInfo avatarInfo)
|
||||
{
|
||||
using (var bitmap = new SKBitmap(avatarInfo.Size * 2,
|
||||
avatarInfo.Size * 2,
|
||||
SKImageInfo.PlatformColorType,
|
||||
SKAlphaType.Premul))
|
||||
{
|
||||
using (var canvas = new SKCanvas(bitmap))
|
||||
{
|
||||
canvas.Clear(SKColors.Transparent);
|
||||
using (var paint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
Style = SKPaintStyle.Fill,
|
||||
StrokeJoin = SKStrokeJoin.Miter,
|
||||
Color = SKColor.Parse(avatarInfo.BackgroundColor)
|
||||
})
|
||||
{
|
||||
var midX = canvas.LocalClipBounds.Size.ToSizeI().Width / 2;
|
||||
var midY = canvas.LocalClipBounds.Size.ToSizeI().Height / 2;
|
||||
var radius = midX - midX / 5;
|
||||
|
||||
using (var circlePaint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
Style = SKPaintStyle.Fill,
|
||||
StrokeJoin = SKStrokeJoin.Miter,
|
||||
Color = SKColor.Parse(avatarInfo.BackgroundColor)
|
||||
})
|
||||
{
|
||||
canvas.DrawCircle(midX, midY, radius, circlePaint);
|
||||
|
||||
var typeface = SKTypeface.FromFamilyName("Arial", SKFontStyle.Normal);
|
||||
var textSize = midX / 1.3f;
|
||||
using (var textPaint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
Style = SKPaintStyle.Fill,
|
||||
Color = SKColor.Parse(avatarInfo.TextColor),
|
||||
TextSize = textSize,
|
||||
TextAlign = SKTextAlign.Center,
|
||||
Typeface = typeface
|
||||
})
|
||||
{
|
||||
var rect = new SKRect();
|
||||
textPaint.MeasureText(avatarInfo.CharsToDraw, ref rect);
|
||||
canvas.DrawText(avatarInfo.CharsToDraw, midX, midY + rect.Height / 2, textPaint);
|
||||
|
||||
return SKImage.FromBitmap(bitmap);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,179 +0,0 @@
|
|||
using Bit.Core.Utilities;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
public class AvatarImageSource : StreamImageSource
|
||||
{
|
||||
private readonly string _text;
|
||||
private readonly string _id;
|
||||
private readonly string _color;
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj is AvatarImageSource avatar)
|
||||
{
|
||||
return avatar._id == _id && avatar._text == _text && avatar._color == _color;
|
||||
}
|
||||
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode() => _id?.GetHashCode() ?? _text?.GetHashCode() ?? -1;
|
||||
|
||||
public AvatarImageSource(string userId = null, string name = null, string email = null, string color = null)
|
||||
{
|
||||
_id = userId;
|
||||
_text = name;
|
||||
if (string.IsNullOrWhiteSpace(_text))
|
||||
{
|
||||
_text = email;
|
||||
}
|
||||
_color = color;
|
||||
}
|
||||
|
||||
public override Func<CancellationToken, Task<Stream>> Stream => GetStreamAsync;
|
||||
|
||||
private Task<Stream> GetStreamAsync(CancellationToken userToken = new CancellationToken())
|
||||
{
|
||||
var result = Draw();
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
private Stream Draw()
|
||||
{
|
||||
string chars;
|
||||
string upperCaseText = null;
|
||||
|
||||
if (string.IsNullOrEmpty(_text))
|
||||
{
|
||||
chars = "..";
|
||||
}
|
||||
else if (_text?.Length > 1)
|
||||
{
|
||||
upperCaseText = _text.ToUpper();
|
||||
chars = GetFirstLetters(upperCaseText, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
chars = upperCaseText = _text.ToUpper();
|
||||
}
|
||||
|
||||
var bgColor = _color ?? CoreHelpers.StringToColor(_id ?? upperCaseText, "#33ffffff");
|
||||
var textColor = CoreHelpers.TextColorFromBgColor(bgColor);
|
||||
var size = 50;
|
||||
|
||||
//Workaround: [MAUI-Migration] There is currently a bug in MAUI where the actual size of the image is used instead of the size it should occupy in the Toolbar.
|
||||
//This causes some issues with the position of the icon. As a workaround we make the icon smaller until this is fixed.
|
||||
//Github issues: https://github.com/dotnet/maui/issues/12359 and https://github.com/dotnet/maui/pull/17120
|
||||
if (DeviceInfo.Platform == DevicePlatform.iOS)
|
||||
{
|
||||
size = 20;
|
||||
}
|
||||
|
||||
using (var bitmap = new SKBitmap(size * 2,
|
||||
size * 2,
|
||||
SKImageInfo.PlatformColorType,
|
||||
SKAlphaType.Premul))
|
||||
{
|
||||
using (var canvas = new SKCanvas(bitmap))
|
||||
{
|
||||
canvas.Clear(SKColors.Transparent);
|
||||
using (var paint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
Style = SKPaintStyle.Fill,
|
||||
StrokeJoin = SKStrokeJoin.Miter,
|
||||
Color = SKColor.Parse(bgColor)
|
||||
})
|
||||
{
|
||||
var midX = canvas.LocalClipBounds.Size.ToSizeI().Width / 2;
|
||||
var midY = canvas.LocalClipBounds.Size.ToSizeI().Height / 2;
|
||||
var radius = midX - midX / 5;
|
||||
|
||||
using (var circlePaint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
Style = SKPaintStyle.Fill,
|
||||
StrokeJoin = SKStrokeJoin.Miter,
|
||||
Color = SKColor.Parse(bgColor)
|
||||
})
|
||||
{
|
||||
canvas.DrawCircle(midX, midY, radius, circlePaint);
|
||||
|
||||
var typeface = SKTypeface.FromFamilyName("Arial", SKFontStyle.Normal);
|
||||
var textSize = midX / 1.3f;
|
||||
using (var textPaint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
Style = SKPaintStyle.Fill,
|
||||
Color = SKColor.Parse(textColor),
|
||||
TextSize = textSize,
|
||||
TextAlign = SKTextAlign.Center,
|
||||
Typeface = typeface
|
||||
})
|
||||
{
|
||||
var rect = new SKRect();
|
||||
textPaint.MeasureText(chars, ref rect);
|
||||
canvas.DrawText(chars, midX, midY + rect.Height / 2, textPaint);
|
||||
|
||||
using (var img = SKImage.FromBitmap(bitmap))
|
||||
{
|
||||
var data = img.Encode(SKEncodedImageFormat.Png, 100);
|
||||
return data?.AsStream(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetFirstLetters(string data, int charCount)
|
||||
{
|
||||
var sanitizedData = data.Trim();
|
||||
var parts = sanitizedData.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (parts.Length > 1 && charCount <= 2)
|
||||
{
|
||||
var text = string.Empty;
|
||||
for (var i = 0; i < charCount; i++)
|
||||
{
|
||||
text += parts[i][0];
|
||||
}
|
||||
return text;
|
||||
}
|
||||
if (sanitizedData.Length > 2)
|
||||
{
|
||||
return sanitizedData.Substring(0, 2);
|
||||
}
|
||||
return sanitizedData;
|
||||
}
|
||||
|
||||
private Color StringToColor(string str)
|
||||
{
|
||||
if (str == null)
|
||||
{
|
||||
return Color.FromArgb("#33ffffff");
|
||||
}
|
||||
var hash = 0;
|
||||
for (var i = 0; i < str.Length; i++)
|
||||
{
|
||||
hash = str[i] + ((hash << 5) - hash);
|
||||
}
|
||||
var color = "#FF";
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
var value = (hash >> (i * 8)) & 0xff;
|
||||
var base16 = "00" + Convert.ToString(value, 16);
|
||||
color += base16.Substring(base16.Length - 2);
|
||||
}
|
||||
return Color.FromArgb(color);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -76,6 +76,7 @@
|
|||
<Folder Include="Utilities\Prompts\" />
|
||||
<Folder Include="Resources\Localization\" />
|
||||
<Folder Include="Controls\Picker\" />
|
||||
<Folder Include="Controls\Avatar\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<MauiImage Include="Resources\Images\dotnet_bot.svg">
|
||||
|
@ -105,5 +106,6 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Controls\Picker\" />
|
||||
<None Remove="Controls\Avatar\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -2,12 +2,15 @@ using System;
|
|||
using Bit.App.Controls;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.iOS.Core.Utilities;
|
||||
using MapKit;
|
||||
using UIKit;
|
||||
|
||||
namespace Bit.iOS.Autofill
|
||||
{
|
||||
public partial class LockPasswordViewController : Core.Controllers.BaseLockPasswordViewController
|
||||
{
|
||||
UIBarButtonItem _cancelButton;
|
||||
UIControl _accountSwitchButton;
|
||||
AccountSwitchingOverlayView _accountSwitchingOverlayView;
|
||||
AccountSwitchingOverlayHelper _accountSwitchingOverlayHelper;
|
||||
|
||||
|
@ -23,22 +26,37 @@ namespace Bit.iOS.Autofill
|
|||
|
||||
public CredentialProviderViewController CPViewController { get; set; }
|
||||
public override UINavigationItem BaseNavItem => NavItem;
|
||||
public override UIBarButtonItem BaseCancelButton => CancelButton;
|
||||
public override UIBarButtonItem BaseCancelButton => _cancelButton;
|
||||
public override UIBarButtonItem BaseSubmitButton => SubmitButton;
|
||||
public override Action Success => () => CPViewController.DismissLockAndContinue();
|
||||
public override Action Cancel => () => CPViewController.CompleteRequest();
|
||||
|
||||
public override async void ViewDidLoad()
|
||||
{
|
||||
_cancelButton = new UIBarButtonItem(UIBarButtonSystemItem.Cancel, CancelButton_TouchUpInside);
|
||||
|
||||
base.ViewDidLoad();
|
||||
|
||||
_accountSwitchingOverlayHelper = new AccountSwitchingOverlayHelper();
|
||||
AccountSwitchingBarButton.Image = await _accountSwitchingOverlayHelper.CreateAvatarImageAsync();
|
||||
|
||||
_accountSwitchButton = await _accountSwitchingOverlayHelper.CreateAccountSwitchToolbarButtonItemCustomViewAsync();
|
||||
_accountSwitchButton.TouchUpInside += AccountSwitchedButton_TouchUpInside;
|
||||
|
||||
NavItem.SetLeftBarButtonItems(new UIBarButtonItem[]
|
||||
{
|
||||
_cancelButton,
|
||||
new UIBarButtonItem(_accountSwitchButton)
|
||||
}, false);
|
||||
|
||||
_accountSwitchingOverlayView = _accountSwitchingOverlayHelper.CreateAccountSwitchingOverlayView(OverlayView);
|
||||
}
|
||||
|
||||
partial void AccountSwitchingBarButton_Activated(UIBarButtonItem sender)
|
||||
private void CancelButton_TouchUpInside(object sender, EventArgs e)
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
|
||||
private void AccountSwitchedButton_TouchUpInside(object sender, EventArgs e)
|
||||
{
|
||||
_accountSwitchingOverlayHelper.OnToolbarItemActivated(_accountSwitchingOverlayView, OverlayView);
|
||||
}
|
||||
|
@ -48,9 +66,19 @@ namespace Bit.iOS.Autofill
|
|||
CheckPasswordAsync().FireAndForget();
|
||||
}
|
||||
|
||||
partial void CancelButton_Activated(UIBarButtonItem sender)
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
Cancel();
|
||||
if (disposing)
|
||||
{
|
||||
if (_accountSwitchButton != null)
|
||||
{
|
||||
_accountSwitchingOverlayHelper.DisposeAccountSwitchToolbarButtonItemImage(_accountSwitchButton);
|
||||
|
||||
_accountSwitchButton.TouchUpInside -= AccountSwitchedButton_TouchUpInside;
|
||||
}
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,13 +12,6 @@ namespace Bit.iOS.Autofill
|
|||
[Register ("LockPasswordViewController")]
|
||||
partial class LockPasswordViewController
|
||||
{
|
||||
[Outlet]
|
||||
UIKit.UIBarButtonItem AccountSwitchingBarButton { get; set; }
|
||||
|
||||
[Outlet]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
UIKit.UIBarButtonItem CancelButton { get; set; }
|
||||
|
||||
[Outlet]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
UIKit.UITableView MainTableView { get; set; }
|
||||
|
@ -34,27 +27,11 @@ namespace Bit.iOS.Autofill
|
|||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
UIKit.UIBarButtonItem SubmitButton { get; set; }
|
||||
|
||||
[Action ("AccountSwitchingBarButton_Activated:")]
|
||||
partial void AccountSwitchingBarButton_Activated (UIKit.UIBarButtonItem sender);
|
||||
|
||||
[Action ("CancelButton_Activated:")]
|
||||
partial void CancelButton_Activated (UIKit.UIBarButtonItem sender);
|
||||
|
||||
[Action ("SubmitButton_Activated:")]
|
||||
partial void SubmitButton_Activated (UIKit.UIBarButtonItem sender);
|
||||
|
||||
void ReleaseDesignerOutlets ()
|
||||
{
|
||||
if (AccountSwitchingBarButton != null) {
|
||||
AccountSwitchingBarButton.Dispose ();
|
||||
AccountSwitchingBarButton = null;
|
||||
}
|
||||
|
||||
if (CancelButton != null) {
|
||||
CancelButton.Dispose ();
|
||||
CancelButton = null;
|
||||
}
|
||||
|
||||
if (MainTableView != null) {
|
||||
MainTableView.Dispose ();
|
||||
MainTableView = null;
|
||||
|
@ -65,15 +42,15 @@ namespace Bit.iOS.Autofill
|
|||
NavItem = null;
|
||||
}
|
||||
|
||||
if (SubmitButton != null) {
|
||||
SubmitButton.Dispose ();
|
||||
SubmitButton = null;
|
||||
}
|
||||
|
||||
if (OverlayView != null) {
|
||||
OverlayView.Dispose ();
|
||||
OverlayView = null;
|
||||
}
|
||||
|
||||
if (SubmitButton != null) {
|
||||
SubmitButton.Dispose ();
|
||||
SubmitButton = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@ namespace Bit.iOS.Autofill
|
|||
{
|
||||
public partial class LoginListViewController : ExtendedUIViewController
|
||||
{
|
||||
UIBarButtonItem _cancelButton;
|
||||
UIControl _accountSwitchButton;
|
||||
|
||||
public LoginListViewController(IntPtr handle)
|
||||
: base(handle)
|
||||
{
|
||||
|
@ -37,12 +40,14 @@ namespace Bit.iOS.Autofill
|
|||
|
||||
public async override void ViewDidLoad()
|
||||
{
|
||||
_cancelButton = new UIBarButtonItem(UIBarButtonSystemItem.Cancel, CancelButton_TouchUpInside);
|
||||
|
||||
base.ViewDidLoad();
|
||||
|
||||
SubscribeSyncCompleted();
|
||||
|
||||
NavItem.Title = AppResources.Items;
|
||||
CancelBarButton.Title = AppResources.Cancel;
|
||||
_cancelButton.Title = AppResources.Cancel;
|
||||
|
||||
TableView.RowHeight = UITableView.AutomaticDimension;
|
||||
TableView.EstimatedRowHeight = 44;
|
||||
|
@ -61,21 +66,29 @@ namespace Bit.iOS.Autofill
|
|||
}
|
||||
|
||||
_accountSwitchingOverlayHelper = new AccountSwitchingOverlayHelper();
|
||||
AccountSwitchingBarButton.Image = await _accountSwitchingOverlayHelper.CreateAvatarImageAsync();
|
||||
|
||||
_accountSwitchButton = await _accountSwitchingOverlayHelper.CreateAccountSwitchToolbarButtonItemCustomViewAsync();
|
||||
_accountSwitchButton.TouchUpInside += AccountSwitchedButton_TouchUpInside;
|
||||
|
||||
NavItem.SetLeftBarButtonItems(new UIBarButtonItem[]
|
||||
{
|
||||
_cancelButton,
|
||||
new UIBarButtonItem(_accountSwitchButton)
|
||||
}, false);
|
||||
|
||||
_accountSwitchingOverlayView = _accountSwitchingOverlayHelper.CreateAccountSwitchingOverlayView(OverlayView);
|
||||
}
|
||||
|
||||
partial void AccountSwitchingBarButton_Activated(UIBarButtonItem sender)
|
||||
{
|
||||
_accountSwitchingOverlayHelper.OnToolbarItemActivated(_accountSwitchingOverlayView, OverlayView);
|
||||
}
|
||||
|
||||
partial void CancelBarButton_Activated(UIBarButtonItem sender)
|
||||
private void CancelButton_TouchUpInside(object sender, EventArgs e)
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
|
||||
private void AccountSwitchedButton_TouchUpInside(object sender, EventArgs e)
|
||||
{
|
||||
_accountSwitchingOverlayHelper.OnToolbarItemActivated(_accountSwitchingOverlayView, OverlayView);
|
||||
}
|
||||
|
||||
private void Cancel()
|
||||
{
|
||||
CPViewController.CompleteRequest();
|
||||
|
@ -151,6 +164,21 @@ namespace Bit.iOS.Autofill
|
|||
});
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (_accountSwitchButton != null)
|
||||
{
|
||||
_accountSwitchingOverlayHelper.DisposeAccountSwitchToolbarButtonItemImage(_accountSwitchButton);
|
||||
|
||||
_accountSwitchButton.TouchUpInside -= AccountSwitchedButton_TouchUpInside;
|
||||
}
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public class TableSource : ExtensionTableSource
|
||||
{
|
||||
private LoginListViewController _controller;
|
||||
|
|
23
src/iOS.Autofill/LoginListViewController.designer.cs
generated
23
src/iOS.Autofill/LoginListViewController.designer.cs
generated
|
@ -12,17 +12,10 @@ namespace Bit.iOS.Autofill
|
|||
[Register ("LoginListViewController")]
|
||||
partial class LoginListViewController
|
||||
{
|
||||
[Outlet]
|
||||
UIKit.UIBarButtonItem AccountSwitchingBarButton { get; set; }
|
||||
|
||||
[Outlet]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
UIKit.UIBarButtonItem AddBarButton { get; set; }
|
||||
|
||||
[Outlet]
|
||||
[GeneratedCode ("iOS Designer", "1.0")]
|
||||
UIKit.UIBarButtonItem CancelBarButton { get; set; }
|
||||
|
||||
[Outlet]
|
||||
UIKit.UIView MainView { get; set; }
|
||||
|
||||
|
@ -36,15 +29,9 @@ namespace Bit.iOS.Autofill
|
|||
[Outlet]
|
||||
UIKit.UITableView TableView { get; set; }
|
||||
|
||||
[Action ("AccountSwitchingBarButton_Activated:")]
|
||||
partial void AccountSwitchingBarButton_Activated (UIKit.UIBarButtonItem sender);
|
||||
|
||||
[Action ("AddBarButton_Activated:")]
|
||||
partial void AddBarButton_Activated (UIKit.UIBarButtonItem sender);
|
||||
|
||||
[Action ("CancelBarButton_Activated:")]
|
||||
partial void CancelBarButton_Activated (UIKit.UIBarButtonItem sender);
|
||||
|
||||
[Action ("SearchBarButton_Activated:")]
|
||||
partial void SearchBarButton_Activated (UIKit.UIBarButtonItem sender);
|
||||
|
||||
|
@ -55,11 +42,6 @@ namespace Bit.iOS.Autofill
|
|||
AddBarButton = null;
|
||||
}
|
||||
|
||||
if (CancelBarButton != null) {
|
||||
CancelBarButton.Dispose ();
|
||||
CancelBarButton = null;
|
||||
}
|
||||
|
||||
if (MainView != null) {
|
||||
MainView.Dispose ();
|
||||
MainView = null;
|
||||
|
@ -79,11 +61,6 @@ namespace Bit.iOS.Autofill
|
|||
TableView.Dispose ();
|
||||
TableView = null;
|
||||
}
|
||||
|
||||
if (AccountSwitchingBarButton != null) {
|
||||
AccountSwitchingBarButton.Dispose ();
|
||||
AccountSwitchingBarButton = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22154" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="43">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="43">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22130"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22504"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
|
@ -185,20 +185,6 @@
|
|||
</view>
|
||||
<toolbarItems/>
|
||||
<navigationItem key="navigationItem" title="Logins" id="3734">
|
||||
<leftBarButtonItems>
|
||||
<barButtonItem title="Cancel" id="3735">
|
||||
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<connections>
|
||||
<action selector="CancelBarButton_Activated:" destination="2304" id="3750"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem title="Account" image="person.2" catalog="system" style="plain" id="I0b-et-FGw" userLabel="Accoutn Switching Button">
|
||||
<color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<connections>
|
||||
<action selector="AccountSwitchingBarButton_Activated:" destination="2304" id="dZn-bd-bC6"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</leftBarButtonItems>
|
||||
<rightBarButtonItems>
|
||||
<barButtonItem systemItem="add" id="3736">
|
||||
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
|
@ -216,9 +202,7 @@
|
|||
</navigationItem>
|
||||
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
|
||||
<connections>
|
||||
<outlet property="AccountSwitchingBarButton" destination="I0b-et-FGw" id="KZj-EO-7wd"/>
|
||||
<outlet property="AddBarButton" destination="3736" id="name-outlet-3736"/>
|
||||
<outlet property="CancelBarButton" destination="3735" id="name-outlet-3735"/>
|
||||
<outlet property="MainView" destination="q9o-3n-3xL" id="gjJ-12-71Q"/>
|
||||
<outlet property="NavItem" destination="3734" id="name-outlet-3734"/>
|
||||
<outlet property="OverlayView" destination="Tq0-Ep-tHr" id="igj-R2-gXJ"/>
|
||||
|
@ -410,19 +394,6 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<navigationItem key="navigationItem" title="Verify Master Password" id="NCb-RV-Vqq">
|
||||
<leftBarButtonItems>
|
||||
<barButtonItem title="Cancel" id="Xoh-Zv-hhd">
|
||||
<connections>
|
||||
<action selector="CancelButton_Activated:" destination="cn5-F4-59n" id="1gM-mE-phn"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem title="Account" image="person.2" catalog="system" style="plain" id="nwd-aM-kFD" userLabel="Accoutn Switching Button">
|
||||
<color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<connections>
|
||||
<action selector="AccountSwitchingBarButton_Activated:" destination="cn5-F4-59n" id="vVZ-IM-rkU"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</leftBarButtonItems>
|
||||
<barButtonItem key="rightBarButtonItem" title="Submit" id="gju-yD-EmI">
|
||||
<connections>
|
||||
<action selector="SubmitButton_Activated:" destination="cn5-F4-59n" id="O1U-fk-BDh"/>
|
||||
|
@ -430,8 +401,6 @@
|
|||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="AccountSwitchingBarButton" destination="nwd-aM-kFD" id="T8F-CN-2il"/>
|
||||
<outlet property="CancelButton" destination="Xoh-Zv-hhd" id="mwi-4K-maj"/>
|
||||
<outlet property="MainTableView" destination="FcI-Ph-m9e" id="Ybv-5r-VGA"/>
|
||||
<outlet property="NavItem" destination="NCb-RV-Vqq" id="L9b-At-x0A"/>
|
||||
<outlet property="OverlayView" destination="sDX-BN-qLw" id="veu-q4-CeW"/>
|
||||
|
@ -601,13 +570,12 @@
|
|||
</scene>
|
||||
</scenes>
|
||||
<inferredMetricsTieBreakers>
|
||||
<segue reference="12959"/>
|
||||
<segue reference="12574"/>
|
||||
<segue reference="3731"/>
|
||||
</inferredMetricsTieBreakers>
|
||||
<resources>
|
||||
<image name="check.png" width="90" height="90"/>
|
||||
<image name="logo.png" width="282" height="44"/>
|
||||
<image name="person.2" catalog="system" width="128" height="87"/>
|
||||
<systemColor name="darkTextColor">
|
||||
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
using Bit.App.Controls;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using CoreGraphics;
|
||||
using Microsoft.Maui.Platform;
|
||||
using SkiaSharp.Views.iOS;
|
||||
using UIKit;
|
||||
|
||||
namespace Bit.iOS.Core.Utilities
|
||||
|
@ -30,12 +32,19 @@ namespace Bit.iOS.Core.Utilities
|
|||
throw new NullReferenceException(nameof(_stateService));
|
||||
}
|
||||
|
||||
var avatarImageSource = new AvatarImageSource(await _stateService.GetActiveUserIdAsync(),
|
||||
await _stateService.GetNameAsync(), await _stateService.GetEmailAsync(),
|
||||
await _stateService.GetAvatarColorAsync());
|
||||
using (var avatarUIImage = await avatarImageSource.GetNativeImageAsync())
|
||||
var avatarInfo = await _stateService.GetActiveUserCustomDataAsync<AvatarInfo?>(a => a?.Profile is null
|
||||
? null
|
||||
: new AvatarInfo(a.Profile.UserId, a.Profile.Name, a.Profile.Email, a.Profile.AvatarColor));
|
||||
|
||||
if (!avatarInfo.HasValue)
|
||||
{
|
||||
return avatarUIImage?.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal) ?? UIImage.GetSystemImage(DEFAULT_SYSTEM_AVATAR_IMAGE);
|
||||
return UIImage.GetSystemImage(DEFAULT_SYSTEM_AVATAR_IMAGE);
|
||||
}
|
||||
|
||||
using (var avatarUIImage = SKAvatarImageHelper.Draw(avatarInfo.Value))
|
||||
{
|
||||
return avatarUIImage?.ToUIImage()?.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal)
|
||||
?? UIImage.GetSystemImage(DEFAULT_SYSTEM_AVATAR_IMAGE);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -100,5 +109,32 @@ namespace Bit.iOS.Core.Utilities
|
|||
containerView.UserInteractionEnabled = !overlayVisible;
|
||||
containerView.Subviews[0].UserInteractionEnabled = !overlayVisible;
|
||||
}
|
||||
|
||||
public async Task<UIControl> CreateAccountSwitchToolbarButtonItemCustomViewAsync()
|
||||
{
|
||||
const float size = 40f;
|
||||
var image = await CreateAvatarImageAsync();
|
||||
var accountSwitchButton = new UIControl(new CGRect(0, 0, size, size));
|
||||
if (image != null)
|
||||
{
|
||||
var accountSwitchAvatarImageView = new UIImageView(new CGRect(0, 0, size, size))
|
||||
{
|
||||
Image = image
|
||||
};
|
||||
accountSwitchButton.AddSubview(accountSwitchAvatarImageView);
|
||||
}
|
||||
|
||||
return accountSwitchButton;
|
||||
}
|
||||
|
||||
public void DisposeAccountSwitchToolbarButtonItemImage(UIControl accountSwitchButton)
|
||||
{
|
||||
if (accountSwitchButton?.Subviews?.FirstOrDefault() is UIImageView accountSwitchImageView && accountSwitchImageView.Image != null)
|
||||
{
|
||||
var img = accountSwitchImageView.Image;
|
||||
accountSwitchImageView.Image = null;
|
||||
img.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Services;
|
||||
using Microsoft.Maui.Controls.Compatibility.Platform.iOS;
|
||||
using UIKit;
|
||||
|
||||
namespace Bit.iOS.Core.Utilities
|
||||
{
|
||||
public static class ImageSourceExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the native image from the ImageSource.
|
||||
/// Taken from https://github.com/xamarin/Xamarin.Forms/blob/02dee20dfa1365d0104758e534581d1fa5958990/Xamarin.Forms.Platform.iOS/Renderers/ImageElementManager.cs#L264
|
||||
/// </summary>
|
||||
public static async Task<UIImage> GetNativeImageAsync(this ImageSource source, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
if (source == null || source.IsEmpty)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var handler = Microsoft.Maui.Controls.Internals.Registrar.Registered.GetHandlerForObject<IImageSourceHandler>(source);
|
||||
if (handler == null)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(new InvalidOperationException("GetNativeImageAsync failed cause IImageSourceHandler couldn't be found"));
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
float scale = (float)UIScreen.MainScreen.Scale;
|
||||
return await handler.LoadImageAsync(source, scale: scale, cancelationToken: cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(new OperationCanceledException("GetNativeImageAsync was cancelled"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(new InvalidOperationException("GetNativeImageAsync failed", ex));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,17 @@
|
|||
using System;
|
||||
using Bit.App.Controls;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.iOS.Core.Utilities;
|
||||
using System;
|
||||
using UIKit;
|
||||
|
||||
namespace Bit.iOS.ShareExtension
|
||||
{
|
||||
public partial class LockPasswordViewController : Core.Controllers.BaseLockPasswordViewController
|
||||
{
|
||||
UIBarButtonItem _cancelButton;
|
||||
UIControl _accountSwitchButton;
|
||||
AccountSwitchingOverlayView _accountSwitchingOverlayView;
|
||||
AccountSwitchingOverlayHelper _accountSwitchingOverlayHelper;
|
||||
private Lazy<AccountSwitchingOverlayHelper> _accountSwitchingOverlayHelper = new Lazy<AccountSwitchingOverlayHelper>(() => new AccountSwitchingOverlayHelper());
|
||||
|
||||
public LockPasswordViewController()
|
||||
{
|
||||
|
@ -43,15 +45,33 @@ namespace Bit.iOS.ShareExtension
|
|||
|
||||
public override async void ViewDidLoad()
|
||||
{
|
||||
_cancelButton = new UIBarButtonItem(UIBarButtonSystemItem.Cancel, CancelButton_TouchUpInside);
|
||||
|
||||
base.ViewDidLoad();
|
||||
|
||||
_cancelButton.TintColor = ThemeHelpers.NavBarTextColor;
|
||||
_submitButton.TintColor = ThemeHelpers.NavBarTextColor;
|
||||
|
||||
_accountSwitchingOverlayHelper = new AccountSwitchingOverlayHelper();
|
||||
_accountSwitchingButton.Image = await _accountSwitchingOverlayHelper.CreateAvatarImageAsync();
|
||||
_accountSwitchButton = await _accountSwitchingOverlayHelper.Value.CreateAccountSwitchToolbarButtonItemCustomViewAsync();
|
||||
_accountSwitchButton.TouchUpInside += AccountSwitchedButton_TouchUpInside;
|
||||
|
||||
_accountSwitchingOverlayView = _accountSwitchingOverlayHelper.CreateAccountSwitchingOverlayView(_overlayView);
|
||||
_navItem.SetLeftBarButtonItems(new UIBarButtonItem[]
|
||||
{
|
||||
_cancelButton,
|
||||
new UIBarButtonItem(_accountSwitchButton)
|
||||
}, false);
|
||||
|
||||
_accountSwitchingOverlayView = _accountSwitchingOverlayHelper.Value.CreateAccountSwitchingOverlayView(_overlayView);
|
||||
}
|
||||
|
||||
private void CancelButton_TouchUpInside(object sender, EventArgs e)
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
|
||||
private void AccountSwitchedButton_TouchUpInside(object sender, EventArgs e)
|
||||
{
|
||||
_accountSwitchingOverlayHelper.Value.OnToolbarItemActivated(_accountSwitchingOverlayView, _overlayView);
|
||||
}
|
||||
|
||||
protected override void UpdateNavigationBarTheme()
|
||||
|
@ -59,21 +79,11 @@ namespace Bit.iOS.ShareExtension
|
|||
UpdateNavigationBarTheme(_navBar);
|
||||
}
|
||||
|
||||
partial void AccountSwitchingButton_Activated(UIBarButtonItem sender)
|
||||
{
|
||||
_accountSwitchingOverlayHelper.OnToolbarItemActivated(_accountSwitchingOverlayView, _overlayView);
|
||||
}
|
||||
|
||||
partial void SubmitButton_Activated(UIBarButtonItem sender)
|
||||
{
|
||||
CheckPasswordAsync().FireAndForget();
|
||||
}
|
||||
|
||||
partial void CancelButton_Activated(UIBarButtonItem sender)
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
|
@ -82,11 +92,11 @@ namespace Bit.iOS.ShareExtension
|
|||
{
|
||||
TableView.Source?.Dispose();
|
||||
}
|
||||
if (_accountSwitchingButton?.Image != null)
|
||||
if (_accountSwitchButton != null)
|
||||
{
|
||||
var img = _accountSwitchingButton.Image;
|
||||
_accountSwitchingButton.Image = null;
|
||||
img.Dispose();
|
||||
_accountSwitchingOverlayHelper.Value.DisposeAccountSwitchToolbarButtonItemImage(_accountSwitchButton);
|
||||
|
||||
_accountSwitchButton.TouchUpInside -= AccountSwitchedButton_TouchUpInside;
|
||||
}
|
||||
if (_accountSwitchingOverlayView != null && _overlayView?.Subviews != null)
|
||||
{
|
||||
|
|
|
@ -12,12 +12,6 @@ namespace Bit.iOS.ShareExtension
|
|||
[Register ("LockPasswordViewController")]
|
||||
partial class LockPasswordViewController
|
||||
{
|
||||
[Outlet]
|
||||
UIKit.UIBarButtonItem _accountSwitchingButton { get; set; }
|
||||
|
||||
[Outlet]
|
||||
UIKit.UIBarButtonItem _cancelButton { get; set; }
|
||||
|
||||
[Outlet]
|
||||
UIKit.UITableView _mainTableView { get; set; }
|
||||
|
||||
|
@ -33,32 +27,21 @@ namespace Bit.iOS.ShareExtension
|
|||
[Outlet]
|
||||
UIKit.UIBarButtonItem _submitButton { get; set; }
|
||||
|
||||
[Action ("AccountSwitchingButton_Activated:")]
|
||||
partial void AccountSwitchingButton_Activated (UIKit.UIBarButtonItem sender);
|
||||
|
||||
[Action ("CancelButton_Activated:")]
|
||||
partial void CancelButton_Activated (UIKit.UIBarButtonItem sender);
|
||||
|
||||
[Action ("SubmitButton_Activated:")]
|
||||
partial void SubmitButton_Activated (UIKit.UIBarButtonItem sender);
|
||||
|
||||
void ReleaseDesignerOutlets ()
|
||||
{
|
||||
if (_accountSwitchingButton != null) {
|
||||
_accountSwitchingButton.Dispose ();
|
||||
_accountSwitchingButton = null;
|
||||
}
|
||||
|
||||
if (_cancelButton != null) {
|
||||
_cancelButton.Dispose ();
|
||||
_cancelButton = null;
|
||||
}
|
||||
|
||||
if (_mainTableView != null) {
|
||||
_mainTableView.Dispose ();
|
||||
_mainTableView = null;
|
||||
}
|
||||
|
||||
if (_navBar != null) {
|
||||
_navBar.Dispose ();
|
||||
_navBar = null;
|
||||
}
|
||||
|
||||
if (_navItem != null) {
|
||||
_navItem.Dispose ();
|
||||
_navItem = null;
|
||||
|
@ -73,11 +56,6 @@ namespace Bit.iOS.ShareExtension
|
|||
_submitButton.Dispose ();
|
||||
_submitButton = null;
|
||||
}
|
||||
|
||||
if (_navBar != null) {
|
||||
_navBar.Dispose ();
|
||||
_navBar = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="2vH-Do-uhk">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="2vH-Do-uhk">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22504"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
|
@ -14,11 +14,11 @@
|
|||
<objects>
|
||||
<viewController id="bHU-LX-EpF" customClass="LoadingViewController" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="z2O-Vp-jY9">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="808"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="804"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" image="logo.png" translatesAutoresizingMaskIntoConstraints="NO" id="Zdy-yw-n0p">
|
||||
<rect key="frame" x="66" y="352" width="282" height="44"/>
|
||||
<rect key="frame" x="66" y="350" width="282" height="44"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="jNx-Vd-K6U"/>
|
||||
|
@ -41,7 +41,7 @@
|
|||
<objects>
|
||||
<navigationController definesPresentationContext="YES" id="2vH-Do-uhk" customClass="ExtensionNavigationController" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" hidden="YES" contentMode="scaleToFill" translucent="NO" id="JoO-jQ-16M">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
|
||||
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
|
@ -61,30 +61,17 @@
|
|||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" estimatedSectionHeaderHeight="-1" sectionFooterHeight="18" estimatedSectionFooterHeight="-1" translatesAutoresizingMaskIntoConstraints="NO" id="M1A-84-x5l">
|
||||
<rect key="frame" x="0.0" y="88" width="414" height="774"/>
|
||||
<rect key="frame" x="0.0" y="92" width="414" height="770"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</tableView>
|
||||
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ijE-Pa-OBq" userLabel="OverlayView">
|
||||
<rect key="frame" x="0.0" y="88" width="414" height="774"/>
|
||||
<rect key="frame" x="0.0" y="92" width="414" height="770"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
<navigationBar contentMode="scaleToFill" translucent="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fav-Fz-6ZK">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
|
||||
<rect key="frame" x="0.0" y="48" width="414" height="44"/>
|
||||
<items>
|
||||
<navigationItem title="Verify Master Password" id="aka-In-IYk">
|
||||
<leftBarButtonItems>
|
||||
<barButtonItem title="Cancel" id="LrG-Qx-w4Q">
|
||||
<connections>
|
||||
<action selector="CancelButton_Activated:" destination="Vi7-LV-nWW" id="qyZ-i9-Dwz"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem title="Item" image="person.2" catalog="system" style="plain" id="nlD-Xn-HtM" userLabel="Account Switching Button">
|
||||
<color key="tintColor" systemColor="systemBackgroundColor"/>
|
||||
<connections>
|
||||
<action selector="AccountSwitchingButton_Activated:" destination="Vi7-LV-nWW" id="G3U-rv-UOl"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</leftBarButtonItems>
|
||||
<barButtonItem key="rightBarButtonItem" title="Submit" id="oQD-QK-YPB">
|
||||
<connections>
|
||||
<action selector="SubmitButton_Activated:" destination="Vi7-LV-nWW" id="DgO-TS-MPf"/>
|
||||
|
@ -116,8 +103,6 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="_accountSwitchingButton" destination="nlD-Xn-HtM" id="SSG-zv-bAc"/>
|
||||
<outlet property="_cancelButton" destination="LrG-Qx-w4Q" id="aag-ZZ-Ifs"/>
|
||||
<outlet property="_mainTableView" destination="M1A-84-x5l" id="pA4-ao-Fhu"/>
|
||||
<outlet property="_navBar" destination="fav-Fz-6ZK" id="Q9p-Dw-ipx"/>
|
||||
<outlet property="_navItem" destination="aka-In-IYk" id="www-Lt-x1g"/>
|
||||
|
@ -132,7 +117,6 @@
|
|||
</scenes>
|
||||
<resources>
|
||||
<image name="logo.png" width="282" height="44"/>
|
||||
<image name="person.2" catalog="system" width="128" height="81"/>
|
||||
<systemColor name="systemBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
|
|
Loading…
Reference in a new issue