Extended controls

This commit is contained in:
Kyle Spearrin 2016-05-09 23:25:37 -04:00
parent b4144a393a
commit 3f251d0d12
14 changed files with 336 additions and 16 deletions

View file

@ -196,6 +196,7 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Controls\ExtendedEntryRenderer.cs" />
<Compile Include="Resources\Resource.Designer.cs" /> <Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Services\ClipboardService.cs" /> <Compile Include="Services\ClipboardService.cs" />
<Compile Include="Services\KeyStoreStorageService.cs" /> <Compile Include="Services\KeyStoreStorageService.cs" />

View file

@ -0,0 +1,75 @@
using System;
using System.ComponentModel;
using Android.Graphics;
using Android.Text;
using Android.Text.Method;
using Bit.Android.Controls;
using Bit.App.Controls;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ExtendedEntry), typeof(ExtendedEntryRenderer))]
namespace Bit.Android.Controls
{
public class ExtendedEntryRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
var view = (ExtendedEntry)Element;
if(Control != null && e.NewElement != null && e.NewElement.IsPassword)
{
Control.SetTypeface(Typeface.Default, TypefaceStyle.Normal);
Control.TransformationMethod = new PasswordTransformationMethod();
}
SetBorder(view);
SetPlaceholderTextColor(view);
SetMaxLength(view);
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var view = (ExtendedEntry)Element;
if(e.PropertyName == ExtendedEntry.HasBorderProperty.PropertyName
|| e.PropertyName == ExtendedEntry.HasOnlyBottomBorderProperty.PropertyName
|| e.PropertyName == ExtendedEntry.BorderColorProperty.PropertyName)
{
SetBorder(view);
}
if(e.PropertyName == ExtendedEntry.PlaceholderTextColorProperty.PropertyName)
{
SetPlaceholderTextColor(view);
}
else
{
base.OnElementPropertyChanged(sender, e);
if(e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
{
Control.SetBackgroundColor(view.BackgroundColor.ToAndroid());
}
}
}
private void SetBorder(ExtendedEntry view)
{
//Not suported on Android
}
private void SetMaxLength(ExtendedEntry view)
{
Control.SetFilters(new IInputFilter[] { new InputFilterLengthFilter(view.MaxLength) });
}
private void SetPlaceholderTextColor(ExtendedEntry view)
{
if(view.PlaceholderTextColor != Xamarin.Forms.Color.Default)
{
Control.SetHintTextColor(view.PlaceholderTextColor.ToAndroid());
}
}
}
}

View file

@ -2,5 +2,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="Bit.Android" android:versionCode="1" android:versionName="0.0.1" android:installLocation="auto"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="Bit.Android" android:versionCode="1" android:versionName="0.0.1" android:installLocation="auto">
<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="23" /> <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="23" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<application android:label="bitwarden"></application> <application android:label="bitwarden" android:theme="@android:style/Theme.Material.Light.DarkActionBar"></application>
</manifest> </manifest>

View file

@ -44,6 +44,10 @@
<Compile Include="Behaviors\EmailValidationBehavior.cs" /> <Compile Include="Behaviors\EmailValidationBehavior.cs" />
<Compile Include="Behaviors\ConnectivityBehavior.cs" /> <Compile Include="Behaviors\ConnectivityBehavior.cs" />
<Compile Include="Behaviors\RequiredValidationBehavior.cs" /> <Compile Include="Behaviors\RequiredValidationBehavior.cs" />
<Compile Include="Controls\EntryLabel.cs" />
<Compile Include="Controls\BottomBorderEntry.cs" />
<Compile Include="Controls\ExtendedEntry.cs" />
<Compile Include="Controls\ExtendedTabbedPage.cs" />
<Compile Include="Models\Api\ApiError.cs" /> <Compile Include="Models\Api\ApiError.cs" />
<Compile Include="Models\Api\ApiResult.cs" /> <Compile Include="Models\Api\ApiResult.cs" />
<Compile Include="Models\Api\Request\FolderRequest.cs" /> <Compile Include="Models\Api\Request\FolderRequest.cs" />

View file

@ -0,0 +1,13 @@
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class BottomBorderEntry : ExtendedEntry
{
public BottomBorderEntry()
{
HasBorder = HasOnlyBottomBorder = true;
BorderColor = Color.FromHex("d2d6de");
}
}
}

View file

@ -0,0 +1,13 @@
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class EntryLabel : Label
{
public EntryLabel()
{
FontSize = 14;
TextColor = Color.FromHex("777777");
}
}
}

View file

@ -0,0 +1,53 @@
using System;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class ExtendedEntry : Entry
{
public static readonly BindableProperty HasBorderProperty =
BindableProperty.Create(nameof(HasBorder), typeof(bool), typeof(ExtendedEntry), true);
public static readonly BindableProperty HasOnlyBottomBorderProperty =
BindableProperty.Create(nameof(HasOnlyBottomBorder), typeof(bool), typeof(ExtendedEntry), false);
public static readonly BindableProperty BorderColorProperty =
BindableProperty.Create(nameof(BorderColor), typeof(Color), typeof(ExtendedEntry), Color.Default);
public static readonly BindableProperty PlaceholderTextColorProperty =
BindableProperty.Create(nameof(PlaceholderTextColor), typeof(Color), typeof(ExtendedEntry), Color.Default);
public static readonly BindableProperty MaxLengthProperty =
BindableProperty.Create(nameof(MaxLength), typeof(int), typeof(ExtendedEntry), int.MaxValue);
public bool HasBorder
{
get { return (bool)GetValue(HasBorderProperty); }
set { SetValue(HasBorderProperty, value); }
}
public bool HasOnlyBottomBorder
{
get { return (bool)GetValue(HasOnlyBottomBorderProperty); }
set { SetValue(HasOnlyBottomBorderProperty, value); }
}
public Color BorderColor
{
get { return (Color)GetValue(BorderColorProperty); }
set { SetValue(BorderColorProperty, value); }
}
public Color PlaceholderTextColor
{
get { return (Color)GetValue(PlaceholderTextColorProperty); }
set { SetValue(PlaceholderTextColorProperty, value); }
}
public int MaxLength
{
get { return (int)GetValue(MaxLengthProperty); }
set { SetValue(MaxLengthProperty, value); }
}
}
}

View file

@ -0,0 +1,26 @@
using System;
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class ExtendedTabbedPage : TabbedPage
{
public static readonly BindableProperty TintColorProperty =
BindableProperty.Create(nameof(TintColor), typeof(Color), typeof(ExtendedTabbedPage), Color.White);
public static readonly BindableProperty BarTintColorProperty =
BindableProperty.Create(nameof(BarTintColor), typeof(Color), typeof(ExtendedTabbedPage), Color.White);
public Color TintColor
{
get { return (Color)GetValue(TintColorProperty); }
set { SetValue(TintColorProperty, value); }
}
public Color BarTintColor
{
get { return (Color)GetValue(BarTintColorProperty); }
set { SetValue(BarTintColorProperty, value); }
}
}
}

View file

@ -1,13 +1,17 @@
using System; using System;
using Bit.App.Controls;
using Bit.App.Resources; using Bit.App.Resources;
using Xamarin.Forms; using Xamarin.Forms;
namespace Bit.App.Pages namespace Bit.App.Pages
{ {
public class MainPage : TabbedPage public class MainPage : ExtendedTabbedPage
{ {
public MainPage() public MainPage()
{ {
BarTintColor = Color.FromHex("222d32");
TintColor = Color.FromHex("ffffff");
var settingsNavigation = new NavigationPage(new SettingsPage()); var settingsNavigation = new NavigationPage(new SettingsPage());
var vaultNavigation = new NavigationPage(new VaultListPage()); var vaultNavigation = new NavigationPage(new VaultListPage());
var syncNavigation = new NavigationPage(new SyncPage()); var syncNavigation = new NavigationPage(new SyncPage());

View file

@ -2,6 +2,7 @@
using System.Linq; using System.Linq;
using Acr.UserDialogs; using Acr.UserDialogs;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Controls;
using Bit.App.Models; using Bit.App.Models;
using Bit.App.Resources; using Bit.App.Resources;
using Plugin.Connectivity.Abstractions; using Plugin.Connectivity.Abstractions;
@ -31,8 +32,8 @@ namespace Bit.App.Pages
{ {
var folders = _folderService.GetAllAsync().GetAwaiter().GetResult().OrderBy(f => f.Name?.Decrypt()); var folders = _folderService.GetAllAsync().GetAwaiter().GetResult().OrderBy(f => f.Name?.Decrypt());
var uriEntry = new Entry { Keyboard = Keyboard.Url }; var uriEntry = new BottomBorderEntry { Keyboard = Keyboard.Url };
var nameEntry = new Entry(); var nameEntry = new BottomBorderEntry();
var folderPicker = new Picker { Title = AppResources.Folder }; var folderPicker = new Picker { Title = AppResources.Folder };
folderPicker.Items.Add(AppResources.FolderNone); folderPicker.Items.Add(AppResources.FolderNone);
folderPicker.SelectedIndex = 0; folderPicker.SelectedIndex = 0;
@ -40,22 +41,22 @@ namespace Bit.App.Pages
{ {
folderPicker.Items.Add(folder.Name.Decrypt()); folderPicker.Items.Add(folder.Name.Decrypt());
} }
var usernameEntry = new Entry(); var usernameEntry = new BottomBorderEntry();
var passwordEntry = new Entry { IsPassword = true }; var passwordEntry = new BottomBorderEntry { IsPassword = true };
var notesEditor = new Editor(); var notesEditor = new Editor();
var stackLayout = new StackLayout(); var stackLayout = new StackLayout { Padding = new Thickness(15) };
stackLayout.Children.Add(new Label { Text = AppResources.URI }); stackLayout.Children.Add(new EntryLabel { Text = AppResources.URI });
stackLayout.Children.Add(uriEntry); stackLayout.Children.Add(uriEntry);
stackLayout.Children.Add(new Label { Text = AppResources.Name }); stackLayout.Children.Add(new EntryLabel { Text = AppResources.Name });
stackLayout.Children.Add(nameEntry); stackLayout.Children.Add(nameEntry);
stackLayout.Children.Add(new Label { Text = AppResources.Folder }); stackLayout.Children.Add(new EntryLabel { Text = AppResources.Folder });
stackLayout.Children.Add(folderPicker); stackLayout.Children.Add(folderPicker);
stackLayout.Children.Add(new Label { Text = AppResources.Username }); stackLayout.Children.Add(new EntryLabel { Text = AppResources.Username });
stackLayout.Children.Add(usernameEntry); stackLayout.Children.Add(usernameEntry);
stackLayout.Children.Add(new Label { Text = AppResources.Password }); stackLayout.Children.Add(new EntryLabel { Text = AppResources.Password });
stackLayout.Children.Add(passwordEntry); stackLayout.Children.Add(passwordEntry);
stackLayout.Children.Add(new Label { Text = AppResources.Notes }); stackLayout.Children.Add(new EntryLabel { Text = AppResources.Notes });
stackLayout.Children.Add(notesEditor); stackLayout.Children.Add(notesEditor);
var scrollView = new ScrollView var scrollView = new ScrollView

View file

@ -0,0 +1,95 @@
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);
SetPlaceholderTextColor(view);
SetMaxLength(view);
}
}
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.BorderColorProperty.PropertyName)
{
SetBorder(view);
}
if(e.PropertyName == ExtendedEntry.PlaceholderTextColorProperty.PropertyName)
{
SetPlaceholderTextColor(view);
}
}
private void SetBorder(ExtendedEntry view)
{
if(view.HasBorder && view.HasOnlyBottomBorder)
{
var borderLayer = new CALayer();
borderLayer.MasksToBounds = true;
borderLayer.Frame = new CGRect(0f, Frame.Height / 2, Frame.Width, 1f);
borderLayer.BorderColor = view.BorderColor.ToCGColor();
borderLayer.BorderWidth = 1.0f;
Control.Layer.AddSublayer(borderLayer);
Control.BorderStyle = UITextBorderStyle.None;
}
else if(view.HasBorder)
{
Control.BorderStyle = UITextBorderStyle.Line;
}
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.MaxLength;
};
}
private void SetPlaceholderTextColor(ExtendedEntry view)
{
if(string.IsNullOrEmpty(view.Placeholder) == false && view.PlaceholderTextColor != Color.Default)
{
var placeholderString = new NSAttributedString(
view.Placeholder,
new UIStringAttributes()
{
ForegroundColor = view.PlaceholderTextColor.ToUIColor()
});
Control.AttributedPlaceholder = placeholderString;
}
}
}
}

View file

@ -0,0 +1,23 @@
using System;
using Bit.App.Controls;
using Bit.iOS.Controls;
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.BarTintColor = page.BarTintColor.ToUIColor();
TabBar.BackgroundColor = page.BackgroundColor.ToUIColor();
}
}
}

View file

@ -22,7 +22,7 @@
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>8.0</string> <string>9.3</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>bitwarden</string> <string>bitwarden</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>

View file

@ -23,9 +23,19 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<ConsolePause>false</ConsolePause> <ConsolePause>false</ConsolePause>
<MtouchArch>i386, x86_64</MtouchArch> <MtouchArch>x86_64</MtouchArch>
<MtouchLink>None</MtouchLink> <MtouchLink>None</MtouchLink>
<MtouchDebug>true</MtouchDebug> <MtouchDebug>True</MtouchDebug>
<MtouchSdkVersion>9.3</MtouchSdkVersion>
<MtouchProfiling>False</MtouchProfiling>
<MtouchFastDev>False</MtouchFastDev>
<MtouchUseLlvm>False</MtouchUseLlvm>
<MtouchUseThumb>False</MtouchUseThumb>
<MtouchUseSGen>False</MtouchUseSGen>
<MtouchUseRefCounting>False</MtouchUseRefCounting>
<OptimizePNGs>True</OptimizePNGs>
<MtouchFloat32>False</MtouchFloat32>
<MtouchI18n />
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
<DebugType>none</DebugType> <DebugType>none</DebugType>
@ -91,6 +101,8 @@
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements> <CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Controls\ExtendedEntryRenderer.cs" />
<Compile Include="Controls\ExtendedTabbedPageRenderer.cs" />
<Compile Include="Services\ClipboardService.cs" /> <Compile Include="Services\ClipboardService.cs" />
<Compile Include="Services\KeyChainStorageService.cs" /> <Compile Include="Services\KeyChainStorageService.cs" />
<Compile Include="Main.cs" /> <Compile Include="Main.cs" />