1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-11 05:43:30 +00:00

Forms update with CollectionView conversion (#1374)

* Forms update with CollectionView conversion

* updates

* removed unnecessary import
This commit is contained in:
Matt Portune
2021-05-13 14:36:20 -04:00
committed by GitHub
parent 29979f6b04
commit 1d4e742d66
53 changed files with 940 additions and 1483 deletions

View File

@@ -77,19 +77,19 @@
<PackageReference Include="Portable.BouncyCastle"> <PackageReference Include="Portable.BouncyCastle">
<Version>1.8.10</Version> <Version>1.8.10</Version>
</PackageReference> </PackageReference>
<PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.3-beta01" /> <PackageReference Include="Xamarin.AndroidX.AutoFill" Version="1.1.0.6" />
<PackageReference Include="Xamarin.Essentials"> <PackageReference Include="Xamarin.Essentials">
<Version>1.5.3.2</Version> <Version>1.6.1</Version>
</PackageReference> </PackageReference>
<PackageReference Include="Xamarin.Firebase.Messaging"> <PackageReference Include="Xamarin.Firebase.Messaging">
<Version>121.0.1</Version> <Version>121.0.1</Version>
</PackageReference> </PackageReference>
<PackageReference Include="Xamarin.Google.Android.Material" Version="1.0.0" /> <PackageReference Include="Xamarin.Google.Android.Material" Version="1.3.0.1" />
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.1.0" /> <PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.2.0.7" />
<PackageReference Include="Xamarin.AndroidX.Legacy.Support.V4" Version="1.0.0" /> <PackageReference Include="Xamarin.AndroidX.Legacy.Support.V4" Version="1.0.0.7" />
<PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0" /> <PackageReference Include="Xamarin.AndroidX.CardView" Version="1.0.0.8" />
<PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.1.0" /> <PackageReference Include="Xamarin.AndroidX.MediaRouter" Version="1.2.2.1" />
<PackageReference Include="Xamarin.AndroidX.Migration" Version="1.0.6" /> <PackageReference Include="Xamarin.AndroidX.Migration" Version="1.0.8" />
<PackageReference Include="Xamarin.Google.Dagger" Version="2.27.0" /> <PackageReference Include="Xamarin.Google.Dagger" Version="2.27.0" />
<PackageReference Include="Xamarin.GooglePlayServices.SafetyNet"> <PackageReference Include="Xamarin.GooglePlayServices.SafetyNet">
<Version>117.0.0</Version> <Version>117.0.0</Version>
@@ -120,18 +120,17 @@
<Compile Include="Receivers\EventUploadReceiver.cs" /> <Compile Include="Receivers\EventUploadReceiver.cs" />
<Compile Include="Receivers\LockAlarmReceiver.cs" /> <Compile Include="Receivers\LockAlarmReceiver.cs" />
<Compile Include="Receivers\PackageReplacedReceiver.cs" /> <Compile Include="Receivers\PackageReplacedReceiver.cs" />
<Compile Include="Renderers\CipherViewCellRenderer.cs" /> <Compile Include="Renderers\ExtendedGridRenderer.cs" />
<Compile Include="Renderers\ExtendedDatePickerRenderer.cs" /> <Compile Include="Renderers\ExtendedDatePickerRenderer.cs" />
<Compile Include="Renderers\CustomTabbedRenderer.cs" /> <Compile Include="Renderers\CustomTabbedRenderer.cs" />
<Compile Include="Renderers\ExtendedStackLayoutRenderer.cs" />
<Compile Include="Renderers\ExtendedTimePickerRenderer.cs" /> <Compile Include="Renderers\ExtendedTimePickerRenderer.cs" />
<Compile Include="Renderers\ExtendedSliderRenderer.cs" /> <Compile Include="Renderers\ExtendedSliderRenderer.cs" />
<Compile Include="Renderers\CustomEditorRenderer.cs" /> <Compile Include="Renderers\CustomEditorRenderer.cs" />
<Compile Include="Renderers\CustomPickerRenderer.cs" /> <Compile Include="Renderers\CustomPickerRenderer.cs" />
<Compile Include="Renderers\CustomEntryRenderer.cs" /> <Compile Include="Renderers\CustomEntryRenderer.cs" />
<Compile Include="Renderers\CustomSearchBarRenderer.cs" /> <Compile Include="Renderers\CustomSearchBarRenderer.cs" />
<Compile Include="Renderers\ExtendedListViewRenderer.cs" />
<Compile Include="Renderers\HybridWebViewRenderer.cs" /> <Compile Include="Renderers\HybridWebViewRenderer.cs" />
<Compile Include="Renderers\SendViewCellRenderer.cs" />
<Compile Include="Services\AndroidPushNotificationService.cs" /> <Compile Include="Services\AndroidPushNotificationService.cs" />
<Compile Include="Services\AndroidLogService.cs" /> <Compile Include="Services\AndroidLogService.cs" />
<Compile Include="MainApplication.cs" /> <Compile Include="MainApplication.cs" />
@@ -173,6 +172,9 @@
<AndroidResource Include="Resources\drawable\ic_launcher_foreground.xml" /> <AndroidResource Include="Resources\drawable\ic_launcher_foreground.xml" />
<AndroidResource Include="Resources\drawable\id.xml" /> <AndroidResource Include="Resources\drawable\id.xml" />
<AndroidResource Include="Resources\drawable\info.xml" /> <AndroidResource Include="Resources\drawable\info.xml" />
<AndroidResource Include="Resources\drawable\list_item_bg.xml" />
<AndroidResource Include="Resources\drawable\list_item_bg_dark.xml" />
<AndroidResource Include="Resources\drawable\list_item_bg_nord.xml" />
<AndroidResource Include="Resources\drawable\lock.xml" /> <AndroidResource Include="Resources\drawable\lock.xml" />
<AndroidResource Include="Resources\drawable\login.xml" /> <AndroidResource Include="Resources\drawable\login.xml" />
<AndroidResource Include="Resources\drawable\logo.xml" /> <AndroidResource Include="Resources\drawable\logo.xml" />
@@ -185,7 +187,6 @@
<AndroidResource Include="Resources\drawable\shield.xml" /> <AndroidResource Include="Resources\drawable\shield.xml" />
<AndroidResource Include="Resources\drawable-v23\splash_screen.xml" /> <AndroidResource Include="Resources\drawable-v23\splash_screen.xml" />
<AndroidResource Include="Resources\drawable-v23\splash_screen_dark.xml" /> <AndroidResource Include="Resources\drawable-v23\splash_screen_dark.xml" />
<AndroidResource Include="Resources\layout\SendViewCell.axml" />
<AndroidResource Include="Resources\layout\Tabbar.axml" /> <AndroidResource Include="Resources\layout\Tabbar.axml" />
<AndroidResource Include="Resources\layout\Toolbar.axml" /> <AndroidResource Include="Resources\layout\Toolbar.axml" />
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher.xml" /> <AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher.xml" />
@@ -262,12 +263,6 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
</AndroidResource> </AndroidResource>
</ItemGroup> </ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\layout\CipherViewCell.axml">
<Generator>MSBuild:UpdateGeneratedFiles</Generator>
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<ItemGroup> <ItemGroup>
<AndroidResource Include="Resources\xml\app_restrictions.xml"> <AndroidResource Include="Resources\xml\app_restrictions.xml">
<Generator>MSBuild:UpdateGeneratedFiles</Generator> <Generator>MSBuild:UpdateGeneratedFiles</Generator>

View File

@@ -1,242 +0,0 @@
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Views.InputMethods;
using Android.Widget;
using Bit.App.Controls;
using Bit.App.Utilities;
using Bit.Droid.Renderers;
using FFImageLoading;
using FFImageLoading.Views;
using FFImageLoading.Work;
using System;
using System.ComponentModel;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(CipherViewCell), typeof(CipherViewCellRenderer))]
namespace Bit.Droid.Renderers
{
public class CipherViewCellRenderer : ViewCellRenderer
{
private static Typeface _faTypeface;
private static Typeface _miTypeface;
private static Android.Graphics.Color _textColor;
private static Android.Graphics.Color _mutedColor;
private static Android.Graphics.Color _disabledIconColor;
private static bool _usingLightTheme;
private AndroidCipherCell _cell;
protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView,
ViewGroup parent, Context context)
{
// TODO expand beyond light/dark detection once we support custom theme switching without app restart
var themeChanged = _usingLightTheme != ThemeManager.UsingLightTheme;
if (_faTypeface == null)
{
_faTypeface = Typeface.CreateFromAsset(context.Assets, "FontAwesome.ttf");
}
if (_miTypeface == null)
{
_miTypeface = Typeface.CreateFromAsset(context.Assets, "MaterialIcons_Regular.ttf");
}
if (_textColor == default(Android.Graphics.Color) || themeChanged)
{
_textColor = ThemeManager.GetResourceColor("TextColor").ToAndroid();
}
if (_mutedColor == default(Android.Graphics.Color) || themeChanged)
{
_mutedColor = ThemeManager.GetResourceColor("MutedColor").ToAndroid();
}
if (_disabledIconColor == default(Android.Graphics.Color) || themeChanged)
{
_disabledIconColor = ThemeManager.GetResourceColor("DisabledIconColor").ToAndroid();
}
_usingLightTheme = ThemeManager.UsingLightTheme;
var cipherCell = item as CipherViewCell;
_cell = convertView as AndroidCipherCell;
if (_cell == null)
{
_cell = new AndroidCipherCell(context, cipherCell, _faTypeface, _miTypeface);
}
else
{
_cell.CipherViewCell.PropertyChanged -= CellPropertyChanged;
}
cipherCell.PropertyChanged += CellPropertyChanged;
_cell.UpdateCell(cipherCell);
_cell.UpdateColors(_textColor, _mutedColor, _disabledIconColor);
return _cell;
}
public void CellPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var cipherCell = sender as CipherViewCell;
_cell.CipherViewCell = cipherCell;
if (e.PropertyName == CipherViewCell.CipherProperty.PropertyName)
{
_cell.UpdateCell(cipherCell);
}
else if (e.PropertyName == CipherViewCell.WebsiteIconsEnabledProperty.PropertyName)
{
_cell.UpdateIconImage(cipherCell);
}
}
}
public class AndroidCipherCell : LinearLayout, INativeElementView
{
private readonly Typeface _faTypeface;
private readonly Typeface _miTypeface;
private IScheduledWork _currentTask;
public AndroidCipherCell(Context context, CipherViewCell cipherView, Typeface faTypeface, Typeface miTypeface)
: base(context)
{
CipherViewCell = cipherView;
_faTypeface = faTypeface;
_miTypeface = miTypeface;
var view = (context as Activity).LayoutInflater.Inflate(Resource.Layout.CipherViewCell, null);
IconImage = view.FindViewById<IconImageView>(Resource.Id.CipherCellIconImage);
Icon = view.FindViewById<TextView>(Resource.Id.CipherCellIcon);
Name = view.FindViewById<TextView>(Resource.Id.CipherCellName);
SubTitle = view.FindViewById<TextView>(Resource.Id.CipherCellSubTitle);
SharedIcon = view.FindViewById<TextView>(Resource.Id.CipherCellSharedIcon);
AttachmentsIcon = view.FindViewById<TextView>(Resource.Id.CipherCellAttachmentsIcon);
MoreButton = view.FindViewById<Android.Widget.Button>(Resource.Id.CipherCellButton);
MoreButton.Click += MoreButton_Click;
Icon.Typeface = _faTypeface;
SharedIcon.Typeface = _faTypeface;
AttachmentsIcon.Typeface = _faTypeface;
MoreButton.Typeface = _miTypeface;
var small = (float)Device.GetNamedSize(NamedSize.Small, typeof(Label));
Icon.SetTextSize(ComplexUnitType.Pt, 10);
Name.SetTextSize(ComplexUnitType.Sp, (float)Device.GetNamedSize(NamedSize.Medium, typeof(Label)));
SubTitle.SetTextSize(ComplexUnitType.Sp, small);
SharedIcon.SetTextSize(ComplexUnitType.Sp, small);
AttachmentsIcon.SetTextSize(ComplexUnitType.Sp, small);
MoreButton.SetTextSize(ComplexUnitType.Sp, 25);
AddView(view);
}
public CipherViewCell CipherViewCell { get; set; }
public Element Element => CipherViewCell;
public IconImageView IconImage { get; set; }
public TextView Icon { get; set; }
public TextView Name { get; set; }
public TextView SubTitle { get; set; }
public TextView SharedIcon { get; set; }
public TextView AttachmentsIcon { get; set; }
public Android.Widget.Button MoreButton { get; set; }
public void UpdateCell(CipherViewCell cipherCell)
{
UpdateIconImage(cipherCell);
var cipher = cipherCell.Cipher;
Name.Text = cipher.Name;
if (!string.IsNullOrWhiteSpace(cipher.SubTitle))
{
SubTitle.Text = cipher.SubTitle;
SubTitle.Visibility = ViewStates.Visible;
}
else
{
SubTitle.Visibility = ViewStates.Invisible;
}
SharedIcon.Visibility = cipher.Shared ? ViewStates.Visible : ViewStates.Gone;
AttachmentsIcon.Visibility = cipher.HasAttachments ? ViewStates.Visible : ViewStates.Gone;
}
public void UpdateIconImage(CipherViewCell cipherCell)
{
if (_currentTask != null && !_currentTask.IsCancelled && !_currentTask.IsCompleted)
{
_currentTask.Cancel();
}
var cipher = cipherCell.Cipher;
var iconImage = cipherCell.GetIconImage(cipher);
if (iconImage.Item2 != null)
{
IconImage.SetImageResource(Resource.Drawable.login);
IconImage.Visibility = ViewStates.Visible;
Icon.Visibility = ViewStates.Gone;
_currentTask = ImageService.Instance.LoadUrl(iconImage.Item2).DownSample(64).Into(IconImage);
IconImage.Key = iconImage.Item2;
}
else
{
IconImage.Visibility = ViewStates.Gone;
Icon.Visibility = ViewStates.Visible;
Icon.Text = iconImage.Item1;
}
}
public void UpdateColors(Android.Graphics.Color textColor, Android.Graphics.Color mutedColor,
Android.Graphics.Color iconDisabledColor)
{
Name.SetTextColor(textColor);
SubTitle.SetTextColor(mutedColor);
Icon.SetTextColor(mutedColor);
SharedIcon.SetTextColor(mutedColor);
AttachmentsIcon.SetTextColor(mutedColor);
MoreButton.SetTextColor(iconDisabledColor);
}
private void MoreButton_Click(object sender, EventArgs e)
{
if (CipherViewCell.ButtonCommand?.CanExecute(CipherViewCell.Cipher) ?? false)
{
CipherViewCell.ButtonCommand.Execute(CipherViewCell.Cipher);
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
MoreButton.Click -= MoreButton_Click;
}
base.Dispose(disposing);
}
}
[Android.Runtime.Preserve(AllMembers = true)]
[Register("bit.droid.renderers.IconImageView")]
public class IconImageView : ImageViewAsync
{
public IconImageView(Context context) : base(context)
{ }
public IconImageView(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{ }
public IconImageView(Context context, IAttributeSet attrs)
: base(context, attrs)
{ }
public string Key { get; set; }
protected override void JavaFinalize()
{
SetImageDrawable(null);
SetImageBitmap(null);
ImageService.Instance.InvalidateCacheEntryAsync(Key, FFImageLoading.Cache.CacheType.Memory);
base.JavaFinalize();
}
}
}

View File

@@ -0,0 +1,43 @@
using Android.Content;
using Bit.App.Controls;
using Bit.App.Utilities;
using Bit.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ExtendedGrid), typeof(ExtendedGridRenderer))]
namespace Bit.Droid.Renderers
{
public class ExtendedGridRenderer : ViewRenderer
{
private static int? _bgResId;
public ExtendedGridRenderer(Context context) : base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<View> elementChangedEvent)
{
base.OnElementChanged(elementChangedEvent);
if (elementChangedEvent.NewElement != null)
{
SetBackgroundResource(GetBgResId());
}
}
private int GetBgResId()
{
if (_bgResId == null)
{
if (ThemeManager.GetTheme(true) == "nord")
{
_bgResId = Resource.Drawable.list_item_bg_nord;
}
else
{
_bgResId ??= ThemeManager.UsingLightTheme ? Resource.Drawable.list_item_bg :
Resource.Drawable.list_item_bg_dark;
}
}
return _bgResId.Value;
}
}
}

View File

@@ -1,29 +0,0 @@
using Android.Content;
using Android.Views;
using Bit.App.Controls;
using Bit.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ExtendedListView), typeof(ExtendedListViewRenderer))]
namespace Bit.Droid.Renderers
{
public class ExtendedListViewRenderer : ListViewRenderer
{
public ExtendedListViewRenderer(Context context)
: base(context)
{ }
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
if (Control != null && e.NewElement != null && e.NewElement is ExtendedListView listView)
{
// Pad for FAB
Control.SetPadding(0, 0, 0, 170);
Control.SetClipToPadding(false);
Control.ScrollBarStyle = ScrollbarStyles.OutsideOverlay;
}
}
}
}

View File

@@ -0,0 +1,43 @@
using Android.Content;
using Bit.App.Controls;
using Bit.App.Utilities;
using Bit.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ExtendedStackLayout), typeof(ExtendedStackLayoutRenderer))]
namespace Bit.Droid.Renderers
{
public class ExtendedStackLayoutRenderer : ViewRenderer
{
private static int? _bgResId;
public ExtendedStackLayoutRenderer(Context context) : base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<View> elementChangedEvent)
{
base.OnElementChanged(elementChangedEvent);
if (elementChangedEvent.NewElement != null)
{
SetBackgroundResource(GetBgResId());
}
}
private int GetBgResId()
{
if (_bgResId == null)
{
if (ThemeManager.GetTheme(true) == "nord")
{
_bgResId = Resource.Drawable.list_item_bg_nord;
}
else
{
_bgResId ??= ThemeManager.UsingLightTheme ? Resource.Drawable.list_item_bg :
Resource.Drawable.list_item_bg_dark;
}
}
return _bgResId.Value;
}
}
}

View File

@@ -1,210 +0,0 @@
using System;
using System.ComponentModel;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.Util;
using Android.Views;
using Android.Widget;
using Bit.App.Controls;
using Bit.App.Utilities;
using Bit.Droid.Renderers;
using FFImageLoading.Work;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Button = Android.Widget.Button;
using Color = Android.Graphics.Color;
using View = Android.Views.View;
[assembly: ExportRenderer(typeof(SendViewCell), typeof(SendViewCellRenderer))]
namespace Bit.Droid.Renderers
{
public class SendViewCellRenderer : ViewCellRenderer
{
private static Typeface _faTypeface;
private static Typeface _miTypeface;
private static Color _textColor;
private static Color _mutedColor;
private static Color _disabledIconColor;
private static bool _usingLightTheme;
private AndroidSendCell _cell;
protected override View GetCellCore(Cell item, View convertView,
ViewGroup parent, Context context)
{
// TODO expand beyond light/dark detection once we support custom theme switching without app restart
var themeChanged = _usingLightTheme != ThemeManager.UsingLightTheme;
if (_faTypeface == null)
{
_faTypeface = Typeface.CreateFromAsset(context.Assets, "FontAwesome.ttf");
}
if (_miTypeface == null)
{
_miTypeface = Typeface.CreateFromAsset(context.Assets, "MaterialIcons_Regular.ttf");
}
if (_textColor == default(Color) || themeChanged)
{
_textColor = ThemeManager.GetResourceColor("TextColor").ToAndroid();
}
if (_mutedColor == default(Color) || themeChanged)
{
_mutedColor = ThemeManager.GetResourceColor("MutedColor").ToAndroid();
}
if (_disabledIconColor == default(Color) || themeChanged)
{
_disabledIconColor = ThemeManager.GetResourceColor("DisabledIconColor").ToAndroid();
}
_usingLightTheme = ThemeManager.UsingLightTheme;
var sendCell = item as SendViewCell;
_cell = convertView as AndroidSendCell;
if (_cell == null)
{
_cell = new AndroidSendCell(context, sendCell, _faTypeface, _miTypeface);
}
else
{
_cell.SendViewCell.PropertyChanged -= CellPropertyChanged;
}
sendCell.PropertyChanged += CellPropertyChanged;
_cell.UpdateCell(sendCell);
_cell.UpdateColors(_textColor, _mutedColor, _disabledIconColor);
return _cell;
}
public void CellPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var sendCell = sender as SendViewCell;
_cell.SendViewCell = sendCell;
if (e.PropertyName == SendViewCell.SendProperty.PropertyName)
{
_cell.UpdateCell(sendCell);
}
}
}
public class AndroidSendCell : LinearLayout, INativeElementView
{
private readonly Typeface _faTypeface;
private readonly Typeface _miTypeface;
private IScheduledWork _currentTask;
public AndroidSendCell(Context context, SendViewCell sendView, Typeface faTypeface, Typeface miTypeface)
: base(context)
{
SendViewCell = sendView;
_faTypeface = faTypeface;
_miTypeface = miTypeface;
var view = (context as Activity).LayoutInflater.Inflate(Resource.Layout.SendViewCell, null);
Icon = view.FindViewById<TextView>(Resource.Id.SendCellIcon);
Name = view.FindViewById<TextView>(Resource.Id.SendCellName);
SubTitle = view.FindViewById<TextView>(Resource.Id.SendCellSubTitle);
DisabledIcon = view.FindViewById<TextView>(Resource.Id.SendCellDisabledIcon);
HasPasswordIcon = view.FindViewById<TextView>(Resource.Id.SendCellHasPasswordIcon);
MaxAccessCountReachedIcon = view.FindViewById<TextView>(Resource.Id.SendCellMaxAccessCountReachedIcon);
ExpiredIcon = view.FindViewById<TextView>(Resource.Id.SendCellExpiredIcon);
PendingDeleteIcon = view.FindViewById<TextView>(Resource.Id.SendCellPendingDeleteIcon);
MoreButton = view.FindViewById<Button>(Resource.Id.SendCellButton);
MoreButton.Click += MoreButton_Click;
Icon.Typeface = _faTypeface;
DisabledIcon.Typeface = _faTypeface;
HasPasswordIcon.Typeface = _faTypeface;
MaxAccessCountReachedIcon.Typeface = _faTypeface;
ExpiredIcon.Typeface = _faTypeface;
PendingDeleteIcon.Typeface = _faTypeface;
MoreButton.Typeface = _miTypeface;
var small = (float)Device.GetNamedSize(NamedSize.Small, typeof(Label));
Icon.SetTextSize(ComplexUnitType.Pt, 10);
Name.SetTextSize(ComplexUnitType.Sp, (float)Device.GetNamedSize(NamedSize.Medium, typeof(Label)));
SubTitle.SetTextSize(ComplexUnitType.Sp, small);
DisabledIcon.SetTextSize(ComplexUnitType.Sp, small);
HasPasswordIcon.SetTextSize(ComplexUnitType.Sp, small);
MaxAccessCountReachedIcon.SetTextSize(ComplexUnitType.Sp, small);
ExpiredIcon.SetTextSize(ComplexUnitType.Sp, small);
PendingDeleteIcon.SetTextSize(ComplexUnitType.Sp, small);
MoreButton.SetTextSize(ComplexUnitType.Sp, 25);
if (!SendViewCell.ShowOptions)
{
MoreButton.Visibility = ViewStates.Gone;
}
AddView(view);
}
public SendViewCell SendViewCell { get; set; }
public Element Element => SendViewCell;
public TextView Icon { get; set; }
public TextView Name { get; set; }
public TextView SubTitle { get; set; }
public TextView DisabledIcon { get; set; }
public TextView HasPasswordIcon { get; set; }
public TextView MaxAccessCountReachedIcon { get; set; }
public TextView ExpiredIcon { get; set; }
public TextView PendingDeleteIcon { get; set; }
public Button MoreButton { get; set; }
public void UpdateCell(SendViewCell sendCell)
{
UpdateIconImage(sendCell);
var send = sendCell.Send;
Name.Text = send.Name;
SubTitle.Text = send.DisplayDate;
DisabledIcon.Visibility = send.Disabled ? ViewStates.Visible : ViewStates.Gone;
HasPasswordIcon.Visibility = send.HasPassword ? ViewStates.Visible : ViewStates.Gone;
MaxAccessCountReachedIcon.Visibility = send.MaxAccessCountReached ? ViewStates.Visible : ViewStates.Gone;
ExpiredIcon.Visibility = send.Expired ? ViewStates.Visible : ViewStates.Gone;
PendingDeleteIcon.Visibility = send.PendingDelete ? ViewStates.Visible : ViewStates.Gone;
}
public void UpdateIconImage(SendViewCell sendCell)
{
if (_currentTask != null && !_currentTask.IsCancelled && !_currentTask.IsCompleted)
{
_currentTask.Cancel();
}
var send = sendCell.Send;
var iconImage = sendCell.GetIconImage(send);
Icon.Text = iconImage;
}
public void UpdateColors(Color textColor, Color mutedColor,
Color iconDisabledColor)
{
Name.SetTextColor(textColor);
SubTitle.SetTextColor(mutedColor);
Icon.SetTextColor(mutedColor);
DisabledIcon.SetTextColor(mutedColor);
HasPasswordIcon.SetTextColor(mutedColor);
MaxAccessCountReachedIcon.SetTextColor(mutedColor);
ExpiredIcon.SetTextColor(mutedColor);
PendingDeleteIcon.SetTextColor(mutedColor);
MoreButton.SetTextColor(iconDisabledColor);
}
private void MoreButton_Click(object sender, EventArgs e)
{
if (SendViewCell.ButtonCommand?.CanExecute(SendViewCell.Send) ?? false)
{
SendViewCell.ButtonCommand.Execute(SendViewCell.Send);
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
MoreButton.Click -= MoreButton_Click;
}
base.Dispose(disposing);
}
}
}

View File

@@ -0,0 +1,7 @@
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/itemPressed">
<item
android:id="@android:id/mask"
android:drawable="@android:color/white" />
</ripple>

View File

@@ -0,0 +1,7 @@
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/dark_primary">
<item
android:id="@android:id/mask"
android:drawable="@android:color/white" />
</ripple>

View File

@@ -0,0 +1,7 @@
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/nord_primary">
<item
android:id="@android:id/mask"
android:drawable="@android:color/white" />
</ripple>

View File

@@ -1,86 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="44dp"
android:gravity="center_vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="2.2dp">
<LinearLayout
android:orientation="horizontal"
android:layout_width="39.8dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:gravity="center">
<bit.droid.renderers.IconImageView
android:id="@+id/CipherCellIconImage"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_gravity="center"
android:gravity="center" />
<TextView
android:id="@+id/CipherCellIcon"
android:layout_width="26dp"
android:layout_height="26dp"
android:layout_gravity="center"
android:gravity="center"
android:text="[X]" />
</LinearLayout>
<LinearLayout
android:id="@+id/CipherCellContent"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:gravity="center"
android:paddingVertical="7.65dp">
<LinearLayout
android:id="@+id/CipherCellContentTop"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/CipherCellName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:text="Name" />
<TextView
android:id="@+id/CipherCellSharedIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingLeft="5dp"
android:singleLine="true"
android:text="&#xf1e0;" />
<TextView
android:id="@+id/CipherCellAttachmentsIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingLeft="5dp"
android:singleLine="true"
android:text="&#xf0c6;" />
</LinearLayout>
<TextView
android:id="@+id/CipherCellSubTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:text="SubTitle" />
</LinearLayout>
<Button
android:id="@+id/CipherCellButton"
android:layout_width="37dp"
android:layout_height="match_parent"
android:text="&#xe5d4;"
android:gravity="center"
android:padding="0dp"
android:background="@android:color/transparent" />
</LinearLayout>
</RelativeLayout>

View File

@@ -1,104 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="44dp"
android:gravity="center_vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="2.2dp">
<LinearLayout
android:orientation="horizontal"
android:layout_width="39.8dp"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:gravity="center">
<TextView
android:id="@+id/SendCellIcon"
android:layout_width="26dp"
android:layout_height="26dp"
android:layout_gravity="center"
android:gravity="center"
android:text="[X]" />
</LinearLayout>
<LinearLayout
android:id="@+id/SendCellContent"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:gravity="center"
android:paddingVertical="7.65dp">
<LinearLayout
android:id="@+id/SendCellContentTop"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/SendCellName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:text="Name" />
<TextView
android:id="@+id/SendCellDisabledIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingLeft="5dp"
android:singleLine="true"
android:text="&#xf071;" />
<TextView
android:id="@+id/SendCellHasPasswordIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingLeft="5dp"
android:singleLine="true"
android:text="&#xf084;" />
<TextView
android:id="@+id/SendCellMaxAccessCountReachedIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingLeft="5dp"
android:singleLine="true"
android:text="&#xf05e;" />
<TextView
android:id="@+id/SendCellExpiredIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingLeft="5dp"
android:singleLine="true"
android:text="&#xf017;" />
<TextView
android:id="@+id/SendCellPendingDeleteIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingLeft="5dp"
android:singleLine="true"
android:text="&#xf1f8;" />
</LinearLayout>
<TextView
android:id="@+id/SendCellSubTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end"
android:text="SubTitle" />
</LinearLayout>
<Button
android:id="@+id/SendCellButton"
android:layout_width="37dp"
android:layout_height="match_parent"
android:text="&#xe5d4;"
android:gravity="center"
android:padding="0dp"
android:background="@android:color/transparent" />
</LinearLayout>
</RelativeLayout>

View File

@@ -6,6 +6,7 @@
<color name="primary">#175DDC</color> <color name="primary">#175DDC</color>
<color name="notificationBar">#1452BC</color> <color name="notificationBar">#1452BC</color>
<color name="border">#dddddd</color> <color name="border">#dddddd</color>
<color name="itemPressed">#bbbbbb</color>
<!-- Dark theme --> <!-- Dark theme -->
<color name="dark_primary">#52bdfb</color> <color name="dark_primary">#52bdfb</color>

View File

@@ -15,9 +15,9 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.2.0" /> <PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.2.0" />
<PackageReference Include="Plugin.Fingerprint" Version="2.1.4" /> <PackageReference Include="Plugin.Fingerprint" Version="2.1.4" />
<PackageReference Include="Xamarin.Essentials" Version="1.5.3.2" /> <PackageReference Include="Xamarin.Essentials" Version="1.6.1" />
<PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" /> <PackageReference Include="Xamarin.FFImageLoading.Forms" Version="2.4.11.982" />
<PackageReference Include="Xamarin.Forms" Version="4.5.0.725" /> <PackageReference Include="Xamarin.Forms" Version="5.0.0.2012" />
<PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" /> <PackageReference Include="ZXing.Net.Mobile" Version="2.4.1" />
<PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" /> <PackageReference Include="ZXing.Net.Mobile.Forms" Version="2.4.1" />
</ItemGroup> </ItemGroup>
@@ -411,4 +411,4 @@
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,114 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms" <controls:ExtendedGrid xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Controls.CipherViewCell" x:Class="Bit.App.Controls.CipherViewCell"
xmlns:controls="clr-namespace:Bit.App.Controls" xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:u="clr-namespace:Bit.App.Utilities" xmlns:u="clr-namespace:Bit.App.Utilities"
xmlns:ff="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"> xmlns:ff="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
StyleClass="list-row, list-row-platform"
RowSpacing="0"
ColumnSpacing="0"
x:DataType="controls:CipherViewCellViewModel">
<Grid <Grid.Resources>
x:Name="_grid" <u:IconGlyphConverter x:Key="iconGlyphConverter"/>
StyleClass="list-row, list-row-platform" <u:IconImageConverter x:Key="iconImageConverter"/>
RowSpacing="0" <u:InverseBoolConverter x:Key="inverseBool" />
ColumnSpacing="0" </Grid.Resources>
x:DataType="controls:CipherViewCellViewModel">
<Grid.BindingContext> <Grid.RowDefinitions>
<controls:CipherViewCellViewModel /> <RowDefinition Height="Auto" />
</Grid.BindingContext> </Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<controls:FaLabel
Grid.Column="0"
HorizontalOptions="Center"
VerticalOptions="Center"
StyleClass="list-icon, list-icon-platform"
IsVisible="{Binding ShowIconImage, Converter={StaticResource inverseBool}}"
Text="{Binding Cipher, Converter={StaticResource iconGlyphConverter}}"
AutomationProperties.IsInAccessibleTree="False" />
<ff:CachedImage
Grid.Column="0"
BitmapOptimizations="True"
ErrorPlaceholder="login.png"
LoadingPlaceholder="login.png"
HorizontalOptions="Center"
VerticalOptions="Center"
WidthRequest="22"
HeightRequest="22"
IsVisible="{Binding ShowIconImage}"
Source="{Binding Cipher, Converter={StaticResource iconImageConverter}}"
AutomationProperties.IsInAccessibleTree="False" />
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center" Padding="0, 7">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="40" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Label
LineBreakMode="TailTruncation"
Grid.Column="0"
Grid.Row="0"
StyleClass="list-title, list-title-platform"
Text="{Binding Cipher.Name}" />
<Label
LineBreakMode="TailTruncation"
Grid.Column="0"
Grid.Row="1"
Grid.ColumnSpan="3"
StyleClass="list-subtitle, list-subtitle-platform"
Text="{Binding Cipher.SubTitle}" />
<controls:FaLabel <controls:FaLabel
x:Name="_icon" Grid.Column="1"
Grid.Row="0" Grid.Row="0"
Grid.Column="0" HorizontalOptions="Start"
HorizontalOptions="Center"
VerticalOptions="Center" VerticalOptions="Center"
StyleClass="list-icon, list-icon-platform" StyleClass="list-title-icon"
AutomationProperties.IsInAccessibleTree="False" /> Margin="5, 0, 0, 0"
Text="&#xf1e0;"
<ff:CachedImage IsVisible="{Binding Cipher.Shared, Mode=OneTime}"
x:Name="_image"
Grid.Row="0"
Grid.Column="0"
BitmapOptimizations="True"
ErrorPlaceholder="login.png"
HorizontalOptions="Center"
VerticalOptions="Center"
WidthRequest="22"
HeightRequest="22"
IsVisible="False"
AutomationProperties.IsInAccessibleTree="False" />
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label
LineBreakMode="TailTruncation"
Grid.Column="0"
Grid.Row="0"
StyleClass="list-title, list-title-platform"
Text="{Binding Cipher.Name, Mode=OneWay}" />
<Label
LineBreakMode="TailTruncation"
Grid.Column="0"
Grid.Row="1"
Grid.ColumnSpan="3"
StyleClass="list-subtitle, list-subtitle-platform"
Text="{Binding Cipher.SubTitle, Mode=OneWay}" />
<controls:FaLabel
Grid.Column="1"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf1e0;"
IsVisible="{Binding Cipher.Shared, Mode=OneWay}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Shared}" />
<controls:FaLabel
Grid.Column="2"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf0c6;"
IsVisible="{Binding Cipher.HasAttachments, Mode=OneWay}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Attachments}" />
</Grid>
<controls:MiButton
Grid.Row="0"
Grid.Column="2"
Text="&#xe5d3;"
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
Clicked="MoreButton_Clicked"
VerticalOptions="CenterAndExpand"
HorizontalOptions="EndAndExpand"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" /> AutomationProperties.Name="{u:I18n Shared}" />
<controls:FaLabel
Grid.Column="2"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf0c6;"
IsVisible="{Binding Cipher.HasAttachments, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Attachments}" />
</Grid> </Grid>
</ViewCell> <controls:MiButton
Grid.Row="0"
Grid.Column="2"
Text="&#xe5d3;"
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
Clicked="MoreButton_Clicked"
VerticalOptions="CenterAndExpand"
HorizontalOptions="EndAndExpand"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" />
</controls:ExtendedGrid>

View File

@@ -1,45 +1,26 @@
using Bit.App.Pages; using System;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.View; using Bit.Core.Models.View;
using Bit.Core.Utilities;
using System;
using Xamarin.Forms; using Xamarin.Forms;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {
public partial class CipherViewCell : ViewCell public partial class CipherViewCell : ExtendedGrid
{ {
public static readonly BindableProperty CipherProperty = BindableProperty.Create( public static readonly BindableProperty CipherProperty = BindableProperty.Create(
nameof(Cipher), typeof(CipherView), typeof(CipherViewCell), default(CipherView), BindingMode.OneWay); nameof(Cipher), typeof(CipherView), typeof(CipherViewCell), default(CipherView), BindingMode.OneWay);
public static readonly BindableProperty WebsiteIconsEnabledProperty = BindableProperty.Create( public static readonly BindableProperty WebsiteIconsEnabledProperty = BindableProperty.Create(
nameof(WebsiteIconsEnabled), typeof(bool), typeof(CipherViewCell), true, BindingMode.OneWay); nameof(WebsiteIconsEnabled), typeof(bool?), typeof(CipherViewCell));
public static readonly BindableProperty ButtonCommandProperty = BindableProperty.Create( public static readonly BindableProperty ButtonCommandProperty = BindableProperty.Create(
nameof(ButtonCommand), typeof(Command<CipherView>), typeof(CipherViewCell)); nameof(ButtonCommand), typeof(Command<CipherView>), typeof(CipherViewCell));
private readonly IEnvironmentService _environmentService;
private CipherViewCellViewModel _viewModel;
private bool _usingNativeCell;
public CipherViewCell() public CipherViewCell()
{ {
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService"); InitializeComponent();
if (Device.RuntimePlatform == Device.iOS)
{
InitializeComponent();
_viewModel = _grid.BindingContext as CipherViewCellViewModel;
}
else
{
_usingNativeCell = true;
}
} }
public bool WebsiteIconsEnabled public bool? WebsiteIconsEnabled
{ {
get => (bool)GetValue(WebsiteIconsEnabledProperty); get => (bool)GetValue(WebsiteIconsEnabledProperty);
set => SetValue(WebsiteIconsEnabledProperty, value); set => SetValue(WebsiteIconsEnabledProperty, value);
@@ -60,130 +41,31 @@ namespace Bit.App.Controls
protected override void OnPropertyChanged(string propertyName = null) protected override void OnPropertyChanged(string propertyName = null)
{ {
base.OnPropertyChanged(propertyName); base.OnPropertyChanged(propertyName);
if (_usingNativeCell)
{
return;
}
if (propertyName == CipherProperty.PropertyName) if (propertyName == CipherProperty.PropertyName)
{ {
_viewModel.Cipher = Cipher; if (Cipher == null)
{
return;
}
BindingContext = new CipherViewCellViewModel(Cipher, WebsiteIconsEnabled ?? false);
} }
} else if (propertyName == WebsiteIconsEnabledProperty.PropertyName)
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if (_usingNativeCell)
{ {
return; if (Cipher == null)
{
return;
}
((CipherViewCellViewModel)BindingContext).WebsiteIconsEnabled = WebsiteIconsEnabled ?? false;
} }
_image.Source = null;
CipherView cipher = null;
if (BindingContext is GroupingsPageListItem groupingsPageListItem)
{
cipher = groupingsPageListItem.Cipher;
}
else if (BindingContext is CipherView cv)
{
cipher = cv;
}
if (cipher != null)
{
var iconImage = GetIconImage(cipher);
if (iconImage.Item2 != null)
{
_image.IsVisible = true;
_icon.IsVisible = false;
_image.Source = iconImage.Item2;
_image.LoadingPlaceholder = "login.png";
}
else
{
_image.IsVisible = false;
_icon.IsVisible = true;
_icon.Text = iconImage.Item1;
}
}
}
public Tuple<string, string> GetIconImage(CipherView cipher)
{
string icon = null;
string image = null;
switch (cipher.Type)
{
case CipherType.Login:
var loginIconImage = GetLoginIconImage(cipher);
icon = loginIconImage.Item1;
image = loginIconImage.Item2;
break;
case CipherType.SecureNote:
icon = "";
break;
case CipherType.Card:
icon = "";
break;
case CipherType.Identity:
icon = "";
break;
default:
break;
}
return new Tuple<string, string>(icon, image);
}
private Tuple<string, string> GetLoginIconImage(CipherView cipher)
{
string icon = "";
string image = null;
if (cipher.Login.Uri != null)
{
var hostnameUri = cipher.Login.Uri;
var isWebsite = false;
if (hostnameUri.StartsWith(Constants.AndroidAppProtocol))
{
icon = "";
}
else if (hostnameUri.StartsWith(Constants.iOSAppProtocol))
{
icon = "";
}
else if (WebsiteIconsEnabled && !hostnameUri.Contains("://") && hostnameUri.Contains("."))
{
hostnameUri = string.Concat("http://", hostnameUri);
isWebsite = true;
}
else if (WebsiteIconsEnabled)
{
isWebsite = hostnameUri.StartsWith("http") && hostnameUri.Contains(".");
}
if (WebsiteIconsEnabled && isWebsite)
{
var hostname = CoreHelpers.GetHostname(hostnameUri);
var iconsUrl = _environmentService.IconsUrl;
if (string.IsNullOrWhiteSpace(iconsUrl))
{
if (!string.IsNullOrWhiteSpace(_environmentService.BaseUrl))
{
iconsUrl = string.Format("{0}/icons", _environmentService.BaseUrl);
}
else
{
iconsUrl = "https://icons.bitwarden.net";
}
}
image = string.Format("{0}/{1}/icon.png", iconsUrl, hostname);
}
}
return new Tuple<string, string>(icon, image);
} }
private void MoreButton_Clicked(object sender, EventArgs e) private void MoreButton_Clicked(object sender, EventArgs e)
{ {
ButtonCommand?.Execute(Cipher); var cipher = ((sender as MiButton)?.BindingContext as CipherViewCellViewModel)?.Cipher;
if (cipher != null)
{
ButtonCommand?.Execute(cipher);
}
} }
} }
} }

View File

@@ -6,11 +6,30 @@ namespace Bit.App.Controls
public class CipherViewCellViewModel : ExtendedViewModel public class CipherViewCellViewModel : ExtendedViewModel
{ {
private CipherView _cipher; private CipherView _cipher;
private bool _websiteIconsEnabled;
public CipherViewCellViewModel(CipherView cipherView, bool websiteIconsEnabled)
{
Cipher = cipherView;
WebsiteIconsEnabled = websiteIconsEnabled;
}
public CipherView Cipher public CipherView Cipher
{ {
get => _cipher; get => _cipher;
set => SetProperty(ref _cipher, value); set => SetProperty(ref _cipher, value);
} }
public bool WebsiteIconsEnabled
{
get => _websiteIconsEnabled;
set => SetProperty(ref _websiteIconsEnabled, value);
}
public bool ShowIconImage
{
get => WebsiteIconsEnabled && !string.IsNullOrWhiteSpace(Cipher.Login?.Uri) &&
Cipher.Login.Uri.StartsWith("http");
}
} }
} }

View File

@@ -0,0 +1,8 @@
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class ExtendedCollectionView : CollectionView
{
}
}

View File

@@ -0,0 +1,8 @@
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class ExtendedGrid : Grid
{
}
}

View File

@@ -1,12 +0,0 @@
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class ExtendedListView : ListView
{
public ExtendedListView() { }
public ExtendedListView(ListViewCachingStrategy cachingStrategy)
: base(cachingStrategy) { }
}
}

View File

@@ -0,0 +1,8 @@
using Xamarin.Forms;
namespace Bit.App.Controls
{
public class ExtendedStackLayout : StackLayout
{
}
}

View File

@@ -1,137 +1,132 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms" <controls:ExtendedGrid xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Controls.SendViewCell" x:Class="Bit.App.Controls.SendViewCell"
xmlns:controls="clr-namespace:Bit.App.Controls" xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:u="clr-namespace:Bit.App.Utilities"> xmlns:u="clr-namespace:Bit.App.Utilities"
StyleClass="list-row, list-row-platform"
RowSpacing="0"
ColumnSpacing="0"
x:DataType="controls:SendViewCellViewModel">
<Grid <Grid.Resources>
x:Name="_grid" <u:SendIconGlyphConverter x:Key="sendIconGlyphConverter"/>
StyleClass="list-row, list-row-platform" </Grid.Resources>
RowSpacing="0"
ColumnSpacing="0"
x:DataType="controls:SendViewCellViewModel">
<Grid.BindingContext> <Grid.RowDefinitions>
<controls:SendViewCellViewModel /> <RowDefinition Height="Auto" />
</Grid.BindingContext> </Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<controls:FaLabel
Grid.Row="0"
Grid.Column="0"
HorizontalOptions="Center"
VerticalOptions="Center"
StyleClass="list-icon, list-icon-platform"
Text="{Binding Send, Converter={StaticResource sendIconGlyphConverter}}"
AutomationProperties.IsInAccessibleTree="False" />
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center" Padding="0, 7">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="40" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<controls:FaLabel <Label
x:Name="_icon" LineBreakMode="TailTruncation"
Grid.Row="0"
Grid.Column="0" Grid.Column="0"
HorizontalOptions="Center"
VerticalOptions="Center"
StyleClass="list-icon, list-icon-platform"
AutomationProperties.IsInAccessibleTree="False" />
<Grid RowSpacing="0" ColumnSpacing="0" Grid.Row="0" Grid.Column="1" VerticalOptions="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label
LineBreakMode="TailTruncation"
Grid.Column="0"
Grid.Row="0"
StyleClass="list-title, list-title-platform"
Text="{Binding Send.Name, Mode=OneWay}" />
<Label
LineBreakMode="TailTruncation"
Grid.Column="0"
Grid.Row="1"
Grid.ColumnSpan="6"
StyleClass="list-subtitle, list-subtitle-platform"
Text="{Binding Send.DisplayDate, Mode=OneWay}" />
<controls:FaLabel
Grid.Column="1"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf071;"
IsVisible="{Binding Send.Disabled, Mode=OneWay}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Disabled}" />
<controls:FaLabel
Grid.Column="2"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf084;"
IsVisible="{Binding Send.HasPassword, Mode=OneWay}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Password}" />
<controls:FaLabel
Grid.Column="3"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf05e;"
IsVisible="{Binding Send.MaxAccessCountReached, Mode=OneWay}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n MaxAccessCountReached}" />
<controls:FaLabel
Grid.Column="4"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf017;"
IsVisible="{Binding Send.Expired, Mode=OneWay}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Expired}" />
<controls:FaLabel
Grid.Column="5"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf1f8;"
IsVisible="{Binding Send.PendingDelete, Mode=OneWay}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n PendingDelete}" />
</Grid>
<controls:MiButton
Grid.Row="0" Grid.Row="0"
Grid.Column="2" StyleClass="list-title, list-title-platform"
Text="&#xe5d3;" Text="{Binding Send.Name}" />
IsVisible="{Binding ShowOptions, Mode=OneWay}" <Label
StyleClass="list-row-button, list-row-button-platform, btn-disabled" LineBreakMode="TailTruncation"
Clicked="MoreButton_Clicked" Grid.Column="0"
VerticalOptions="CenterAndExpand" Grid.Row="1"
HorizontalOptions="EndAndExpand" Grid.ColumnSpan="6"
StyleClass="list-subtitle, list-subtitle-platform"
Text="{Binding Send.DisplayDate}" />
<controls:FaLabel
Grid.Column="1"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf071;"
IsVisible="{Binding Send.Disabled, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" /> AutomationProperties.Name="{u:I18n Disabled}" />
<controls:FaLabel
Grid.Column="2"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf084;"
IsVisible="{Binding Send.HasPassword, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Password}" />
<controls:FaLabel
Grid.Column="3"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf05e;"
IsVisible="{Binding Send.MaxAccessCountReached, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n MaxAccessCountReached}" />
<controls:FaLabel
Grid.Column="4"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf017;"
IsVisible="{Binding Send.Expired, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Expired}" />
<controls:FaLabel
Grid.Column="5"
Grid.Row="0"
HorizontalOptions="Start"
VerticalOptions="Center"
StyleClass="list-title-icon"
Margin="5, 0, 0, 0"
Text="&#xf1f8;"
IsVisible="{Binding Send.PendingDelete, Mode=OneTime}"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n PendingDelete}" />
</Grid> </Grid>
</ViewCell> <controls:MiButton
Grid.Row="0"
Grid.Column="2"
Text="&#xe5d3;"
IsVisible="{Binding ShowOptions, Mode=OneWay}"
StyleClass="list-row-button, list-row-button-platform, btn-disabled"
Clicked="MoreButton_Clicked"
VerticalOptions="CenterAndExpand"
HorizontalOptions="EndAndExpand"
AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n Options}" />
</controls:ExtendedGrid>

View File

@@ -1,14 +1,10 @@
using System; using System;
using Bit.App.Pages;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.View; using Bit.Core.Models.View;
using Bit.Core.Utilities;
using Xamarin.Forms; using Xamarin.Forms;
namespace Bit.App.Controls namespace Bit.App.Controls
{ {
public partial class SendViewCell : ViewCell public partial class SendViewCell : ExtendedGrid
{ {
public static readonly BindableProperty SendProperty = BindableProperty.Create( public static readonly BindableProperty SendProperty = BindableProperty.Create(
nameof(Send), typeof(SendView), typeof(SendViewCell), default(SendView), BindingMode.OneWay); nameof(Send), typeof(SendView), typeof(SendViewCell), default(SendView), BindingMode.OneWay);
@@ -17,25 +13,11 @@ namespace Bit.App.Controls
nameof(ButtonCommand), typeof(Command<SendView>), typeof(SendViewCell)); nameof(ButtonCommand), typeof(Command<SendView>), typeof(SendViewCell));
public static readonly BindableProperty ShowOptionsProperty = BindableProperty.Create( public static readonly BindableProperty ShowOptionsProperty = BindableProperty.Create(
nameof(ShowOptions), typeof(bool), typeof(SendViewCell)); nameof(ShowOptions), typeof(bool), typeof(SendViewCell), true, BindingMode.OneWay);
private readonly IEnvironmentService _environmentService;
private SendViewCellViewModel _viewModel;
private bool _usingNativeCell;
public SendViewCell() public SendViewCell()
{ {
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService"); InitializeComponent();
if (Device.RuntimePlatform == Device.iOS)
{
InitializeComponent();
_viewModel = _grid.BindingContext as SendViewCellViewModel;
}
else
{
_usingNativeCell = true;
}
} }
public SendView Send public SendView Send
@@ -52,72 +34,30 @@ namespace Bit.App.Controls
public bool ShowOptions public bool ShowOptions
{ {
get => GetValue(ShowOptionsProperty) is bool && (bool)GetValue(ShowOptionsProperty); get => (bool)GetValue(ShowOptionsProperty);
set => SetValue(ShowOptionsProperty, value); set => SetValue(ShowOptionsProperty, value);
} }
protected override void OnPropertyChanged(string propertyName = null) protected override void OnPropertyChanged(string propertyName = null)
{ {
base.OnPropertyChanged(propertyName); base.OnPropertyChanged(propertyName);
if (_usingNativeCell)
{
return;
}
if (propertyName == SendProperty.PropertyName) if (propertyName == SendProperty.PropertyName)
{ {
_viewModel.Send = Send; if (Send == null)
{
return;
}
BindingContext = new SendViewCellViewModel(Send, ShowOptions);
} }
else if (propertyName == ShowOptionsProperty.PropertyName)
{
_viewModel.ShowOptions = ShowOptions;
}
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if (_usingNativeCell)
{
return;
}
SendView send = null;
if (BindingContext is SendGroupingsPageListItem sendGroupingsPageListItem)
{
send = sendGroupingsPageListItem.Send;
}
else if (BindingContext is SendView sv)
{
send = sv;
}
if (send != null)
{
var iconImage = GetIconImage(send);
_icon.IsVisible = true;
_icon.Text = iconImage;
}
}
public string GetIconImage(SendView send)
{
string icon = null;
switch (send.Type)
{
case SendType.Text:
icon = "\uf0f6"; // fa-file-text-o
break;
case SendType.File:
icon = "\uf016"; // fa-file-o
break;
default:
break;
}
return icon;
} }
private void MoreButton_Clicked(object sender, EventArgs e) private void MoreButton_Clicked(object sender, EventArgs e)
{ {
ButtonCommand?.Execute(Send); var send = ((sender as MiButton)?.BindingContext as SendViewCellViewModel)?.Send;
if (send != null)
{
ButtonCommand?.Execute(send);
}
} }
} }
} }

View File

@@ -8,6 +8,12 @@ namespace Bit.App.Controls
private SendView _send; private SendView _send;
private bool _showOptions; private bool _showOptions;
public SendViewCellViewModel(SendView sendView, bool showOptions)
{
Send = sendView;
ShowOptions = showOptions;
}
public SendView Send public SendView Send
{ {
get => _send; get => _send;

View File

@@ -43,57 +43,53 @@
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand"
HorizontalTextAlignment="Center"></Label> HorizontalTextAlignment="Center"></Label>
<ListView x:Name="_listView" <controls:ExtendedCollectionView
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}" IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
ItemsSource="{Binding History}" ItemsSource="{Binding History}"
VerticalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
HasUnevenRows="true"
CachingStrategy="RecycleElement"
StyleClass="list, list-platform"> StyleClass="list, list-platform">
<ListView.ItemTemplate> <CollectionView.ItemTemplate>
<DataTemplate x:DataType="domain:GeneratedPasswordHistory"> <DataTemplate x:DataType="domain:GeneratedPasswordHistory">
<ViewCell> <Grid
<Grid StyleClass="list-row, list-row-platform"
StyleClass="list-row, list-row-platform" Padding="10"
Padding="10" RowSpacing="0"
RowSpacing="0" ColumnSpacing="10">
ColumnSpacing="10">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<controls:MonoLabel LineBreakMode="CharacterWrap" <controls:MonoLabel LineBreakMode="CharacterWrap"
Grid.Column="0" Grid.Column="0"
Grid.Row="0" Grid.Row="0"
StyleClass="list-title, list-title-platform" StyleClass="list-title, list-title-platform"
TextType="Html" TextType="Html"
Text="{Binding Password, Mode=OneWay, Converter={StaticResource coloredPassword}}" /> Text="{Binding Password, Mode=OneWay, Converter={StaticResource coloredPassword}}" />
<Label LineBreakMode="TailTruncation" <Label LineBreakMode="TailTruncation"
Grid.Column="0" Grid.Column="0"
Grid.Row="1" Grid.Row="1"
StyleClass="list-subtitle, list-subtitle-platform" StyleClass="list-subtitle, list-subtitle-platform"
Text="{Binding Date, Mode=OneWay, Converter={StaticResource dateTime}}" /> Text="{Binding Date, Mode=OneWay, Converter={StaticResource dateTime}}" />
<controls:FaButton <controls:FaButton
StyleClass="list-row-button, list-row-button-platform" StyleClass="list-row-button, list-row-button-platform"
Text="&#xf24d;" Text="&#xf0ea;"
Command="{Binding BindingContext.CopyCommand, Source={x:Reference _page}}" Command="{Binding BindingContext.CopyCommand, Source={x:Reference _page}}"
CommandParameter="{Binding .}" CommandParameter="{Binding .}"
Grid.Row="0" Grid.Row="0"
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CopyPassword}" /> AutomationProperties.Name="{u:I18n CopyPassword}" />
</Grid> </Grid>
</ViewCell>
</DataTemplate> </DataTemplate>
</ListView.ItemTemplate> </CollectionView.ItemTemplate>
</ListView> </controls:ExtendedCollectionView>
</StackLayout> </StackLayout>
</pages:BaseContentPage> </pages:BaseContentPage>

View File

@@ -44,34 +44,32 @@
<controls:SendViewCell <controls:SendViewCell
Send="{Binding Send}" Send="{Binding Send}"
ButtonCommand="{Binding BindingContext.SendOptionsCommand, Source={x:Reference _page}}" ButtonCommand="{Binding BindingContext.SendOptionsCommand, Source={x:Reference _page}}"
ShowOptions="{Binding ShowOptions}" /> ShowOptions="{Binding BindingContext.SendEnabled, Source={x:Reference _page}}" />
</DataTemplate> </DataTemplate>
<DataTemplate x:Key="sendGroupTemplate" <DataTemplate x:Key="sendGroupTemplate"
x:DataType="pages:SendGroupingsPageListItem"> x:DataType="pages:SendGroupingsPageListItem">
<ViewCell> <controls:ExtendedStackLayout Orientation="Horizontal"
<StackLayout Orientation="Horizontal" StyleClass="list-row, list-row-platform">
StyleClass="list-row, list-row-platform"> <controls:FaLabel Text="{Binding Icon, Mode=OneWay}"
<controls:FaLabel Text="{Binding Icon, Mode=OneWay}" HorizontalOptions="Start"
HorizontalOptions="Start" VerticalOptions="Center"
VerticalOptions="Center" StyleClass="list-icon, list-icon-platform">
StyleClass="list-icon, list-icon-platform"> <controls:FaLabel.Effects>
<controls:FaLabel.Effects> <effects:FixedSizeEffect />
<effects:FixedSizeEffect /> </controls:FaLabel.Effects>
</controls:FaLabel.Effects> </controls:FaLabel>
</controls:FaLabel> <Label Text="{Binding Name, Mode=OneWay}"
<Label Text="{Binding Name, Mode=OneWay}" LineBreakMode="TailTruncation"
LineBreakMode="TailTruncation" HorizontalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand" VerticalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand" StyleClass="list-title" />
StyleClass="list-title" /> <Label Text="{Binding ItemCount, Mode=OneWay}"
<Label Text="{Binding ItemCount, Mode=OneWay}" HorizontalOptions="End"
HorizontalOptions="End" VerticalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand" HorizontalTextAlignment="End"
HorizontalTextAlignment="End" StyleClass="list-sub" />
StyleClass="list-sub" /> </controls:ExtendedStackLayout>
</StackLayout>
</ViewCell>
</DataTemplate> </DataTemplate>
<pages:SendGroupingsPageListItemSelector x:Key="sendListItemDataTemplateSelector" <pages:SendGroupingsPageListItemSelector x:Key="sendListItemDataTemplateSelector"
@@ -103,32 +101,25 @@
Text="{Binding NoDataText}" Text="{Binding NoDataText}"
HorizontalTextAlignment="Center" /> HorizontalTextAlignment="Center" />
<Button <Button
Text="{u:I18n AddAnItem}" Text="{u:I18n AddASend}"
Clicked="AddButton_Clicked" Clicked="AddButton_Clicked" />
IsVisible="{Binding ShowAddSendButton}" />
</StackLayout> </StackLayout>
<controls:ExtendedListView <RefreshView
x:Name="_listView"
IsVisible="{Binding ShowList}" IsVisible="{Binding ShowList}"
ItemsSource="{Binding GroupedSends}"
VerticalOptions="FillAndExpand"
HasUnevenRows="True"
RowHeight="-1"
RefreshCommand="{Binding RefreshCommand}"
IsPullToRefreshEnabled="True"
IsRefreshing="{Binding Refreshing}" IsRefreshing="{Binding Refreshing}"
ItemTemplate="{StaticResource sendListItemDataTemplateSelector}" Command="{Binding RefreshCommand}">
IsGroupingEnabled="True" <controls:ExtendedCollectionView
ItemSelected="RowSelected" ItemsSource="{Binding GroupedSends}"
StyleClass="list, list-platform"> VerticalOptions="FillAndExpand"
<x:Arguments> ItemTemplate="{StaticResource sendListItemDataTemplateSelector}"
<ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy> IsGrouped="True"
</x:Arguments> SelectionMode="Single"
SelectionChanged="RowSelected"
StyleClass="list, list-platform">
<ListView.GroupHeaderTemplate> <CollectionView.GroupHeaderTemplate>
<DataTemplate x:DataType="pages:SendGroupingsPageListGroup"> <DataTemplate x:DataType="pages:SendGroupingsPageListGroup">
<ViewCell>
<StackLayout <StackLayout
Spacing="0" Padding="0" VerticalOptions="FillAndExpand" Spacing="0" Padding="0" VerticalOptions="FillAndExpand"
StyleClass="list-row-header-container, list-row-header-container-platform"> StyleClass="list-row-header-container, list-row-header-container-platform">
@@ -146,10 +137,10 @@
<BoxView <BoxView
StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" /> StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
</StackLayout> </StackLayout>
</ViewCell> </DataTemplate>
</DataTemplate> </CollectionView.GroupHeaderTemplate>
</ListView.GroupHeaderTemplate> </controls:ExtendedCollectionView>
</controls:ExtendedListView> </RefreshView>
</StackLayout> </StackLayout>
</ResourceDictionary> </ResourceDictionary>
</ContentPage.Resources> </ContentPage.Resources>

View File

@@ -20,14 +20,13 @@ namespace Bit.App.Pages
private readonly string _pageName; private readonly string _pageName;
private AppOptions _appOptions; private AppOptions _appOptions;
private PreviousPageInfo _previousPage;
public SendGroupingsPage(bool mainPage, SendType? type = null, string pageTitle = null, public SendGroupingsPage(bool mainPage, SendType? type = null, string pageTitle = null,
AppOptions appOptions = null, PreviousPageInfo previousPage = null) AppOptions appOptions = null)
{ {
_pageName = string.Concat(nameof(GroupingsPage), "_", DateTime.UtcNow.Ticks); _pageName = string.Concat(nameof(SendGroupingsPage), "_", DateTime.UtcNow.Ticks);
InitializeComponent(); InitializeComponent();
ListView = _listView; SetActivityIndicator(_mainContent);
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService"); _broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService"); _syncService = ServiceContainer.Resolve<ISyncService>("syncService");
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService"); _vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
@@ -37,7 +36,6 @@ namespace Bit.App.Pages
_vm.MainPage = mainPage; _vm.MainPage = mainPage;
_vm.Type = type; _vm.Type = type;
_appOptions = appOptions; _appOptions = appOptions;
_previousPage = previousPage;
if (pageTitle != null) if (pageTitle != null)
{ {
_vm.PageTitle = pageTitle; _vm.PageTitle = pageTitle;
@@ -46,7 +44,10 @@ namespace Bit.App.Pages
if (Device.RuntimePlatform == Device.iOS) if (Device.RuntimePlatform == Device.iOS)
{ {
_absLayout.Children.Remove(_fab); _absLayout.Children.Remove(_fab);
ToolbarItems.Add(_aboutIconItem); if (type == null)
{
ToolbarItems.Add(_aboutIconItem);
}
ToolbarItems.Add(_addItem); ToolbarItems.Add(_addItem);
} }
else else
@@ -57,8 +58,6 @@ namespace Bit.App.Pages
} }
} }
public ExtendedListView ListView { get; set; }
protected override async void OnAppearing() protected override async void OnAppearing()
{ {
base.OnAppearing(); base.OnAppearing();
@@ -136,14 +135,14 @@ namespace Bit.App.Pages
} }
} }
private async void RowSelected(object sender, SelectedItemChangedEventArgs e) private async void RowSelected(object sender, SelectionChangedEventArgs e)
{ {
((ListView)sender).SelectedItem = null; ((ExtendedCollectionView)sender).SelectedItem = null;
if (!DoOnce()) if (!DoOnce())
{ {
return; return;
} }
if (!(e.SelectedItem is SendGroupingsPageListItem item)) if (!(e.CurrentSelection?.FirstOrDefault() is SendGroupingsPageListItem item))
{ {
return; return;
} }

View File

@@ -7,10 +7,10 @@ namespace Bit.App.Pages
public SendGroupingsPageListGroup(string name, int count, bool doUpper = true, bool first = false) public SendGroupingsPageListGroup(string name, int count, bool doUpper = true, bool first = false)
: this(new List<SendGroupingsPageListItem>(), name, count, doUpper, first) { } : this(new List<SendGroupingsPageListItem>(), name, count, doUpper, first) { }
public SendGroupingsPageListGroup(List<SendGroupingsPageListItem> groupItems, string name, int count, public SendGroupingsPageListGroup(List<SendGroupingsPageListItem> sendGroupItems, string name, int count,
bool doUpper = true, bool first = false) bool doUpper = true, bool first = false)
{ {
AddRange(groupItems); AddRange(sendGroupItems);
if (string.IsNullOrWhiteSpace(name)) if (string.IsNullOrWhiteSpace(name))
{ {
Name = "-"; Name = "-";

View File

@@ -23,7 +23,6 @@ namespace Bit.App.Pages
private bool _doingLoad; private bool _doingLoad;
private bool _loading; private bool _loading;
private bool _loaded; private bool _loaded;
private bool _showAddSendButton;
private bool _showNoData; private bool _showNoData;
private bool _showList; private bool _showList;
private bool _syncRefreshing; private bool _syncRefreshing;
@@ -91,11 +90,6 @@ namespace Bit.App.Pages
get => _loaded; get => _loaded;
set => SetProperty(ref _loaded, value); set => SetProperty(ref _loaded, value);
} }
public bool ShowAddSendButton
{
get => _showAddSendButton;
set => SetProperty(ref _showAddSendButton, value);
}
public bool ShowNoData public bool ShowNoData
{ {
get => _showNoData; get => _showNoData;

View File

@@ -60,23 +60,22 @@
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand"
HorizontalTextAlignment="Center" /> HorizontalTextAlignment="Center" />
<ListView x:Name="_listView" <controls:ExtendedCollectionView
IsVisible="{Binding ShowList}" IsVisible="{Binding ShowList}"
ItemsSource="{Binding Sends}" ItemsSource="{Binding Sends}"
VerticalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
HasUnevenRows="true" SelectionMode="Single"
CachingStrategy="RecycleElement" SelectionChanged="RowSelected"
ItemSelected="RowSelected"
StyleClass="list, list-platform"> StyleClass="list, list-platform">
<ListView.ItemTemplate> <CollectionView.ItemTemplate>
<DataTemplate x:DataType="views:SendView"> <DataTemplate x:DataType="views:SendView">
<controls:SendViewCell <controls:SendViewCell
Send="{Binding .}" Send="{Binding .}"
ButtonCommand="{Binding BindingContext.SendOptionsCommand, Source={x:Reference _page}}" ButtonCommand="{Binding BindingContext.SendOptionsCommand, Source={x:Reference _page}}"
ShowOptions="{Binding BindingContext.SendEnabled, Source={x:Reference _page}}" /> ShowOptions="{Binding BindingContext.SendEnabled, Source={x:Reference _page}}" />
</DataTemplate> </DataTemplate>
</ListView.ItemTemplate> </CollectionView.ItemTemplate>
</ListView> </controls:ExtendedCollectionView>
</StackLayout> </StackLayout>
</pages:BaseContentPage> </pages:BaseContentPage>

View File

@@ -1,4 +1,5 @@
using System; using System;
using Bit.App.Controls;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core.Models.View; using Bit.Core.Models.View;
using Xamarin.Forms; using Xamarin.Forms;
@@ -87,15 +88,15 @@ namespace Bit.App.Pages
Navigation.PopModalAsync(false); Navigation.PopModalAsync(false);
} }
private async void RowSelected(object sender, SelectedItemChangedEventArgs e) private async void RowSelected(object sender, SelectionChangedEventArgs e)
{ {
((ListView)sender).SelectedItem = null; ((ExtendedCollectionView)sender).SelectedItem = null;
if (!DoOnce()) if (!DoOnce())
{ {
return; return;
} }
if (e.SelectedItem is SendView send) if (e.CurrentSelection is SendView send)
{ {
await _vm.SelectSendAsync(send); await _vm.SelectSendAsync(send);
} }

View File

@@ -32,27 +32,25 @@
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand"
HorizontalTextAlignment="Center"></Label> HorizontalTextAlignment="Center"></Label>
<ListView x:Name="_listView" <controls:ExtendedCollectionView
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}" IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
ItemsSource="{Binding Folders}" ItemsSource="{Binding Folders}"
VerticalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
CachingStrategy="RecycleElement" SelectionMode="Single"
ItemSelected="RowSelected" SelectionChanged="RowSelected"
StyleClass="list, list-platform"> StyleClass="list, list-platform">
<ListView.ItemTemplate> <CollectionView.ItemTemplate>
<DataTemplate x:DataType="views:FolderView"> <DataTemplate x:DataType="views:FolderView">
<ViewCell> <controls:ExtendedStackLayout
<StackLayout StyleClass="list-row, list-row-platform"
StyleClass="list-row, list-row-platform" Padding="10">
Padding="10"> <Label LineBreakMode="TailTruncation"
<Label LineBreakMode="TailTruncation" StyleClass="list-title, list-title-platform"
StyleClass="list-title, list-title-platform" Text="{Binding Name, Mode=OneWay}" />
Text="{Binding Name, Mode=OneWay}" /> </controls:ExtendedStackLayout>
</StackLayout>
</ViewCell>
</DataTemplate> </DataTemplate>
</ListView.ItemTemplate> </CollectionView.ItemTemplate>
</ListView> </controls:ExtendedCollectionView>
</StackLayout> </StackLayout>
</ResourceDictionary> </ResourceDictionary>
</ContentPage.Resources> </ContentPage.Resources>

View File

@@ -1,5 +1,7 @@
using Bit.Core.Models.View; using Bit.Core.Models.View;
using System; using System;
using System.Linq;
using Bit.App.Controls;
using Xamarin.Forms; using Xamarin.Forms;
namespace Bit.App.Pages namespace Bit.App.Pages
@@ -32,14 +34,14 @@ namespace Bit.App.Pages
}, _mainContent); }, _mainContent);
} }
private async void RowSelected(object sender, SelectedItemChangedEventArgs e) private async void RowSelected(object sender, SelectionChangedEventArgs e)
{ {
((ListView)sender).SelectedItem = null; ((ExtendedCollectionView)sender).SelectedItem = null;
if (!DoOnce()) if (!DoOnce())
{ {
return; return;
} }
if (!(e.SelectedItem is FolderView folder)) if (!(e.CurrentSelection?.FirstOrDefault() is FolderView folder))
{ {
return; return;
} }

View File

@@ -19,23 +19,21 @@
<DataTemplate <DataTemplate
x:Key="regularTemplate" x:Key="regularTemplate"
x:DataType="pages:SettingsPageListItem"> x:DataType="pages:SettingsPageListItem">
<ViewCell> <controls:ExtendedStackLayout Orientation="Horizontal"
<StackLayout Orientation="Horizontal" StyleClass="list-row, list-row-platform">
StyleClass="list-row, list-row-platform"> <Label Text="{Binding Name, Mode=OneWay}"
<Label Text="{Binding Name, Mode=OneWay}" LineBreakMode="TailTruncation"
LineBreakMode="TailTruncation" HorizontalOptions="StartAndExpand"
HorizontalOptions="StartAndExpand" VerticalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand" StyleClass="list-title"/>
StyleClass="list-title"/> <Label Text="{Binding SubLabel, Mode=OneWay}"
<Label Text="{Binding SubLabel, Mode=OneWay}" IsVisible="{Binding SubLabel, Converter={StaticResource stringHasValue}}"
IsVisible="{Binding SubLabel, Converter={StaticResource stringHasValue}}" HorizontalOptions="End"
HorizontalOptions="End" HorizontalTextAlignment="End"
HorizontalTextAlignment="End" VerticalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand" TextColor="{Binding SubLabelColor}"
TextColor="{Binding SubLabelColor}" StyleClass="list-sub" />
StyleClass="list-sub" /> </controls:ExtendedStackLayout>
</StackLayout>
</ViewCell>
</DataTemplate> </DataTemplate>
<pages:SettingsPageListItemSelector <pages:SettingsPageListItemSelector
@@ -44,35 +42,32 @@
</ResourceDictionary> </ResourceDictionary>
</ContentPage.Resources> </ContentPage.Resources>
<ListView <controls:ExtendedCollectionView
ItemsSource="{Binding GroupedItems}" ItemsSource="{Binding GroupedItems}"
VerticalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
HasUnevenRows="True"
RowHeight="-1"
ItemTemplate="{StaticResource listItemDataTemplateSelector}" ItemTemplate="{StaticResource listItemDataTemplateSelector}"
IsGroupingEnabled="True" IsGrouped="True"
ItemSelected="RowSelected" SelectionMode="Single"
SelectionChanged="RowSelected"
StyleClass="list, list-platform"> StyleClass="list, list-platform">
<ListView.GroupHeaderTemplate> <CollectionView.GroupHeaderTemplate>
<DataTemplate x:DataType="pages:SettingsPageListGroup"> <DataTemplate x:DataType="pages:SettingsPageListGroup">
<ViewCell> <StackLayout
<StackLayout Padding="0" Spacing="0" VerticalOptions="FillAndExpand"
Padding="0" Spacing="0" VerticalOptions="FillAndExpand" StyleClass="list-row-header-container, list-row-header-container-platform">
StyleClass="list-row-header-container, list-row-header-container-platform"> <BoxView
<BoxView StyleClass="list-section-separator-top, list-section-separator-top-platform"
StyleClass="list-section-separator-top, list-section-separator-top-platform" IsVisible="{Binding First, Converter={StaticResource inverseBool}}" />
IsVisible="{Binding First, Converter={StaticResource inverseBool}}" /> <StackLayout StyleClass="list-row-header, list-row-header-platform">
<StackLayout StyleClass="list-row-header, list-row-header-platform"> <Label
<Label Text="{Binding Name}"
Text="{Binding Name}" StyleClass="list-header, list-header-platform" />
StyleClass="list-header, list-header-platform" />
</StackLayout>
<BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
</StackLayout> </StackLayout>
</ViewCell> <BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
</StackLayout>
</DataTemplate> </DataTemplate>
</ListView.GroupHeaderTemplate> </CollectionView.GroupHeaderTemplate>
</ListView> </controls:ExtendedCollectionView>
</pages:BaseContentPage> </pages:BaseContentPage>

View File

@@ -1,7 +1,9 @@
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Resources; using Bit.App.Resources;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.App.Controls;
using Xamarin.Forms; using Xamarin.Forms;
namespace Bit.App.Pages namespace Bit.App.Pages
@@ -41,14 +43,14 @@ namespace Bit.App.Pages
return base.OnBackButtonPressed(); return base.OnBackButtonPressed();
} }
private async void RowSelected(object sender, SelectedItemChangedEventArgs e) private async void RowSelected(object sender, SelectionChangedEventArgs e)
{ {
((ListView)sender).SelectedItem = null; ((ExtendedCollectionView)sender).SelectedItem = null;
if (!DoOnce()) if (!DoOnce())
{ {
return; return;
} }
if (!(e.SelectedItem is SettingsPageListItem item)) if (!(e.CurrentSelection?.FirstOrDefault() is SettingsPageListItem item))
{ {
return; return;
} }

View File

@@ -47,40 +47,35 @@
Clicked="AddButton_Clicked"></Button> Clicked="AddButton_Clicked"></Button>
</StackLayout> </StackLayout>
<controls:ExtendedListView <controls:ExtendedCollectionView
IsVisible="{Binding ShowList}" IsVisible="{Binding ShowList}"
ItemsSource="{Binding GroupedItems}" ItemsSource="{Binding GroupedItems}"
VerticalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
HasUnevenRows="true"
ItemTemplate="{StaticResource listItemDataTemplateSelector}" ItemTemplate="{StaticResource listItemDataTemplateSelector}"
IsGroupingEnabled="True" IsGrouped="True"
ItemSelected="RowSelected" SelectionMode="Single"
SelectionChanged="RowSelected"
StyleClass="list, list-platform"> StyleClass="list, list-platform">
<x:Arguments>
<ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
</x:Arguments>
<ListView.GroupHeaderTemplate> <CollectionView.GroupHeaderTemplate>
<DataTemplate x:DataType="pages:GroupingsPageListGroup"> <DataTemplate x:DataType="pages:GroupingsPageListGroup">
<ViewCell> <StackLayout
<StackLayout Spacing="0" Padding="0" VerticalOptions="FillAndExpand"
Spacing="0" Padding="0" VerticalOptions="FillAndExpand" StyleClass="list-row-header-container, list-row-header-container-platform">
StyleClass="list-row-header-container, list-row-header-container-platform"> <BoxView
<BoxView StyleClass="list-section-separator-top, list-section-separator-top-platform" />
StyleClass="list-section-separator-top, list-section-separator-top-platform" /> <StackLayout StyleClass="list-row-header, list-row-header-platform">
<StackLayout StyleClass="list-row-header, list-row-header-platform"> <Label
<Label Text="{Binding Name}"
Text="{Binding Name}" StyleClass="list-header, list-header-platform" />
StyleClass="list-header, list-header-platform" /> <Label
<Label Text="{Binding ItemCount}"
Text="{Binding ItemCount}" StyleClass="list-header-sub" />
StyleClass="list-header-sub" />
</StackLayout>
</StackLayout> </StackLayout>
</ViewCell> </StackLayout>
</DataTemplate> </DataTemplate>
</ListView.GroupHeaderTemplate> </CollectionView.GroupHeaderTemplate>
</controls:ExtendedListView> </controls:ExtendedCollectionView>
</StackLayout> </StackLayout>
</ResourceDictionary> </ResourceDictionary>
</ContentPage.Resources> </ContentPage.Resources>

View File

@@ -4,7 +4,9 @@ using Bit.Core.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using System; using System;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.App.Controls;
using Xamarin.Forms; using Xamarin.Forms;
namespace Bit.App.Pages namespace Bit.App.Pages
@@ -44,14 +46,14 @@ namespace Bit.App.Pages
}, _mainContent); }, _mainContent);
} }
private async void RowSelected(object sender, SelectedItemChangedEventArgs e) private async void RowSelected(object sender, SelectionChangedEventArgs e)
{ {
((ListView)sender).SelectedItem = null; ((ExtendedCollectionView)sender).SelectedItem = null;
if (!DoOnce()) if (!DoOnce())
{ {
return; return;
} }
if (e.SelectedItem is GroupingsPageListItem item && item.Cipher != null) if (e.CurrentSelection?.FirstOrDefault() is GroupingsPageListItem item && item.Cipher != null)
{ {
await _vm.SelectCipherAsync(item.Cipher, item.FuzzyAutofill); await _vm.SelectCipherAsync(item.Cipher, item.FuzzyAutofill);
} }

View File

@@ -60,15 +60,14 @@
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand"
HorizontalTextAlignment="Center" /> HorizontalTextAlignment="Center" />
<ListView x:Name="_listView" <controls:ExtendedCollectionView
IsVisible="{Binding ShowList}" IsVisible="{Binding ShowList}"
ItemsSource="{Binding Ciphers}" ItemsSource="{Binding Ciphers}"
VerticalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
HasUnevenRows="true" SelectionMode="Single"
CachingStrategy="RecycleElement" SelectionChanged="RowSelected"
ItemSelected="RowSelected"
StyleClass="list, list-platform"> StyleClass="list, list-platform">
<ListView.ItemTemplate> <CollectionView.ItemTemplate>
<DataTemplate x:DataType="views:CipherView"> <DataTemplate x:DataType="views:CipherView">
<controls:CipherViewCell <controls:CipherViewCell
Cipher="{Binding .}" Cipher="{Binding .}"
@@ -76,8 +75,8 @@
WebsiteIconsEnabled="{Binding BindingContext.WebsiteIconsEnabled, Source={x:Reference _page}}" WebsiteIconsEnabled="{Binding BindingContext.WebsiteIconsEnabled, Source={x:Reference _page}}"
/> />
</DataTemplate> </DataTemplate>
</ListView.ItemTemplate> </CollectionView.ItemTemplate>
</ListView> </controls:ExtendedCollectionView>
</StackLayout> </StackLayout>
</pages:BaseContentPage> </pages:BaseContentPage>

View File

@@ -3,6 +3,7 @@ using Bit.App.Resources;
using Bit.Core.Models.View; using Bit.Core.Models.View;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using System; using System;
using Bit.App.Controls;
using Xamarin.Forms; using Xamarin.Forms;
namespace Bit.App.Pages namespace Bit.App.Pages
@@ -119,15 +120,15 @@ namespace Bit.App.Pages
} }
} }
private async void RowSelected(object sender, SelectedItemChangedEventArgs e) private async void RowSelected(object sender, SelectionChangedEventArgs e)
{ {
((ListView)sender).SelectedItem = null; ((ExtendedCollectionView)sender).SelectedItem = null;
if (!DoOnce()) if (!DoOnce())
{ {
return; return;
} }
if (e.SelectedItem is CipherView cipher) if (e.CurrentSelection is CipherView cipher)
{ {
await _vm.SelectCipherAsync(cipher); await _vm.SelectCipherAsync(cipher);
} }

View File

@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<pages:BaseContentPage xmlns="http://xamarin.com/schemas/2014/forms" <pages:BaseContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Bit.App.Pages.GroupingsPage" x:Class="Bit.App.Pages.GroupingsPage"
xmlns:pages="clr-namespace:Bit.App.Pages" xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:u="clr-namespace:Bit.App.Utilities" xmlns:u="clr-namespace:Bit.App.Utilities"
xmlns:effects="clr-namespace:Bit.App.Effects" xmlns:effects="clr-namespace:Bit.App.Effects"
xmlns:controls="clr-namespace:Bit.App.Controls" xmlns:controls="clr-namespace:Bit.App.Controls"
x:DataType="pages:GroupingsPageViewModel" x:DataType="pages:GroupingsPageViewModel"
Title="{Binding PageTitle}" Title="{Binding PageTitle}"
x:Name="_page"> x:Name="_page">
<ContentPage.BindingContext> <ContentPage.BindingContext>
<pages:GroupingsPageViewModel /> <pages:GroupingsPageViewModel />
@@ -45,29 +45,27 @@
<DataTemplate x:Key="groupTemplate" <DataTemplate x:Key="groupTemplate"
x:DataType="pages:GroupingsPageListItem"> x:DataType="pages:GroupingsPageListItem">
<ViewCell> <controls:ExtendedStackLayout Orientation="Horizontal"
<StackLayout Orientation="Horizontal" StyleClass="list-row, list-row-platform">
StyleClass="list-row, list-row-platform"> <controls:FaLabel Text="{Binding Icon, Mode=OneWay}"
<controls:FaLabel Text="{Binding Icon, Mode=OneWay}" HorizontalOptions="Start"
HorizontalOptions="Start" VerticalOptions="Center"
VerticalOptions="Center" StyleClass="list-icon, list-icon-platform">
StyleClass="list-icon, list-icon-platform"> <controls:FaLabel.Effects>
<controls:FaLabel.Effects> <effects:FixedSizeEffect />
<effects:FixedSizeEffect /> </controls:FaLabel.Effects>
</controls:FaLabel.Effects> </controls:FaLabel>
</controls:FaLabel> <Label Text="{Binding Name, Mode=OneWay}"
<Label Text="{Binding Name, Mode=OneWay}" LineBreakMode="TailTruncation"
LineBreakMode="TailTruncation" HorizontalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand" VerticalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand" StyleClass="list-title"/>
StyleClass="list-title"/> <Label Text="{Binding ItemCount, Mode=OneWay}"
<Label Text="{Binding ItemCount, Mode=OneWay}" HorizontalOptions="End"
HorizontalOptions="End" VerticalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand" HorizontalTextAlignment="End"
HorizontalTextAlignment="End" StyleClass="list-sub"/>
StyleClass="list-sub"/> </controls:ExtendedStackLayout>
</StackLayout>
</ViewCell>
</DataTemplate> </DataTemplate>
<pages:GroupingsPageListItemSelector x:Key="listItemDataTemplateSelector" <pages:GroupingsPageListItemSelector x:Key="listItemDataTemplateSelector"
@@ -89,27 +87,21 @@
IsVisible="{Binding ShowAddCipherButton}"></Button> IsVisible="{Binding ShowAddCipherButton}"></Button>
</StackLayout> </StackLayout>
<controls:ExtendedListView <RefreshView
x:Name="_listView"
IsVisible="{Binding ShowList}" IsVisible="{Binding ShowList}"
ItemsSource="{Binding GroupedItems}"
VerticalOptions="FillAndExpand"
HasUnevenRows="True"
RowHeight="-1"
RefreshCommand="{Binding RefreshCommand}"
IsPullToRefreshEnabled="True"
IsRefreshing="{Binding Refreshing}" IsRefreshing="{Binding Refreshing}"
ItemTemplate="{StaticResource listItemDataTemplateSelector}" Command="{Binding RefreshCommand}">
IsGroupingEnabled="True" <controls:ExtendedCollectionView
ItemSelected="RowSelected" ItemsSource="{Binding GroupedItems}"
StyleClass="list, list-platform"> VerticalOptions="FillAndExpand"
<x:Arguments> ItemTemplate="{StaticResource listItemDataTemplateSelector}"
<ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy> IsGrouped="True"
</x:Arguments> SelectionMode="Single"
SelectionChanged="RowSelected"
StyleClass="list, list-platform">
<ListView.GroupHeaderTemplate> <CollectionView.GroupHeaderTemplate>
<DataTemplate x:DataType="pages:GroupingsPageListGroup"> <DataTemplate x:DataType="pages:GroupingsPageListGroup">
<ViewCell>
<StackLayout <StackLayout
Spacing="0" Padding="0" VerticalOptions="FillAndExpand" Spacing="0" Padding="0" VerticalOptions="FillAndExpand"
StyleClass="list-row-header-container, list-row-header-container-platform"> StyleClass="list-row-header-container, list-row-header-container-platform">
@@ -126,10 +118,10 @@
</StackLayout> </StackLayout>
<BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" /> <BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
</StackLayout> </StackLayout>
</ViewCell> </DataTemplate>
</DataTemplate> </CollectionView.GroupHeaderTemplate>
</ListView.GroupHeaderTemplate> </controls:ExtendedCollectionView>
</controls:ExtendedListView> </RefreshView>
</StackLayout> </StackLayout>
</ResourceDictionary> </ResourceDictionary>
</ContentPage.Resources> </ContentPage.Resources>

View File

@@ -1,5 +1,4 @@
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 Bit.Core; using Bit.Core;
@@ -9,6 +8,7 @@ using Bit.Core.Utilities;
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.App.Controls;
using Xamarin.Forms; using Xamarin.Forms;
namespace Bit.App.Pages namespace Bit.App.Pages
@@ -33,7 +33,6 @@ namespace Bit.App.Pages
{ {
_pageName = string.Concat(nameof(GroupingsPage), "_", DateTime.UtcNow.Ticks); _pageName = string.Concat(nameof(GroupingsPage), "_", DateTime.UtcNow.Ticks);
InitializeComponent(); InitializeComponent();
ListView = _listView;
SetActivityIndicator(_mainContent); SetActivityIndicator(_mainContent);
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService"); _broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
_syncService = ServiceContainer.Resolve<ISyncService>("syncService"); _syncService = ServiceContainer.Resolve<ISyncService>("syncService");
@@ -73,8 +72,6 @@ namespace Bit.App.Pages
} }
} }
public ExtendedListView ListView { get; set; }
protected async override void OnAppearing() protected async override void OnAppearing()
{ {
base.OnAppearing(); base.OnAppearing();
@@ -197,14 +194,14 @@ namespace Bit.App.Pages
_vm.DisableRefreshing(); _vm.DisableRefreshing();
} }
private async void RowSelected(object sender, SelectedItemChangedEventArgs e) private async void RowSelected(object sender, SelectionChangedEventArgs e)
{ {
((ListView)sender).SelectedItem = null; ((ExtendedCollectionView)sender).SelectedItem = null;
if (!DoOnce()) if (!DoOnce())
{ {
return; return;
} }
if (!(e.SelectedItem is GroupingsPageListItem item)) if (!(e.CurrentSelection?.FirstOrDefault() is GroupingsPageListItem item))
{ {
return; return;
} }

View File

@@ -75,30 +75,30 @@ namespace Bit.App.Pages
} }
else if (Folder != null) else if (Folder != null)
{ {
_icon = Folder.Id == null ? "" : ""; _icon = Folder.Id == null ? "\uf115" : "\uf07c"; // fa-folder-open-o : fa-folder-open
} }
else if (Collection != null) else if (Collection != null)
{ {
_icon = ""; _icon = "\uf1b2"; // fa-cube
} }
else if (Type != null) else if (Type != null)
{ {
switch (Type.Value) switch (Type.Value)
{ {
case CipherType.Login: case CipherType.Login:
_icon = ""; _icon = "\uf0ac"; // fa-globe
break; break;
case CipherType.SecureNote: case CipherType.SecureNote:
_icon = ""; _icon = "\uf24a"; // fa-sticky-note-o
break; break;
case CipherType.Card: case CipherType.Card:
_icon = ""; _icon = "\uf09d"; // fa-credit-card
break; break;
case CipherType.Identity: case CipherType.Identity:
_icon = ""; _icon = "\uf2c3"; // fa-id-card-o
break; break;
default: default:
_icon = ""; _icon = "\uf0ac"; // fa-globe
break; break;
} }
} }

View File

@@ -34,57 +34,53 @@
VerticalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand"
HorizontalTextAlignment="Center"></Label> HorizontalTextAlignment="Center"></Label>
<ListView x:Name="_listView" <controls:ExtendedCollectionView
IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}" IsVisible="{Binding ShowNoData, Converter={StaticResource inverseBool}}"
ItemsSource="{Binding History}" ItemsSource="{Binding History}"
VerticalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
HasUnevenRows="true"
CachingStrategy="RecycleElement"
StyleClass="list, list-platform"> StyleClass="list, list-platform">
<ListView.ItemTemplate> <CollectionView.ItemTemplate>
<DataTemplate x:DataType="views:PasswordHistoryView"> <DataTemplate x:DataType="views:PasswordHistoryView">
<ViewCell> <Grid
<Grid StyleClass="list-row, list-row-platform"
StyleClass="list-row, list-row-platform" Padding="10"
Padding="10" RowSpacing="0"
RowSpacing="0" ColumnSpacing="10">
ColumnSpacing="10">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<controls:MonoLabel LineBreakMode="CharacterWrap" <controls:MonoLabel LineBreakMode="CharacterWrap"
Grid.Column="0" Grid.Column="0"
Grid.Row="0" Grid.Row="0"
StyleClass="list-title, list-title-platform" StyleClass="list-title, list-title-platform"
TextType="Html" TextType="Html"
Text="{Binding Password, Mode=OneWay, Converter={StaticResource coloredPassword}}" /> Text="{Binding Password, Mode=OneWay, Converter={StaticResource coloredPassword}}" />
<Label LineBreakMode="TailTruncation" <Label LineBreakMode="TailTruncation"
Grid.Column="0" Grid.Column="0"
Grid.Row="1" Grid.Row="1"
StyleClass="list-subtitle, list-subtitle-platform" StyleClass="list-subtitle, list-subtitle-platform"
Text="{Binding LastUsedDate, Mode=OneWay, Converter={StaticResource dateTime}}" /> Text="{Binding LastUsedDate, Mode=OneWay, Converter={StaticResource dateTime}}" />
<controls:FaButton <controls:FaButton
StyleClass="list-row-button, list-row-button-platform" StyleClass="list-row-button, list-row-button-platform"
Text="&#xf24d;" Text="&#xf24d;"
Command="{Binding BindingContext.CopyCommand, Source={x:Reference _page}}" Command="{Binding BindingContext.CopyCommand, Source={x:Reference _page}}"
CommandParameter="{Binding .}" CommandParameter="{Binding .}"
Grid.Row="0" Grid.Row="0"
Grid.Column="1" Grid.Column="1"
Grid.RowSpan="2" Grid.RowSpan="2"
AutomationProperties.IsInAccessibleTree="True" AutomationProperties.IsInAccessibleTree="True"
AutomationProperties.Name="{u:I18n CopyPassword}" /> AutomationProperties.Name="{u:I18n CopyPassword}" />
</Grid> </Grid>
</ViewCell>
</DataTemplate> </DataTemplate>
</ListView.ItemTemplate> </CollectionView.ItemTemplate>
</ListView> </controls:ExtendedCollectionView>
</StackLayout> </StackLayout>
</pages:BaseContentPage> </pages:BaseContentPage>

View File

@@ -1,7 +1,6 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// This code was generated by a tool. // This code was generated by a tool.
// Runtime Version:4.0.30319.42000
// //
// Changes to this file may cause incorrect behavior and will be lost if // Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated. // the code is regenerated.
@@ -10,7 +9,6 @@
namespace Bit.App.Resources { namespace Bit.App.Resources {
using System; using System;
using System.Reflection;
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
@@ -3371,6 +3369,12 @@ namespace Bit.App.Resources {
} }
} }
public static string AddASend {
get {
return ResourceManager.GetString("AddASend", resourceCulture);
}
}
public static string CopyLink { public static string CopyLink {
get { get {
return ResourceManager.GetString("CopyLink", resourceCulture); return ResourceManager.GetString("CopyLink", resourceCulture);

View File

@@ -1904,6 +1904,10 @@
<value>There are no Sends in your account.</value> <value>There are no Sends in your account.</value>
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment> <comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
</data> </data>
<data name="AddASend" xml:space="preserve">
<value>Add a Send</value>
<comment>'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.</comment>
</data>
<data name="CopyLink" xml:space="preserve"> <data name="CopyLink" xml:space="preserve">
<value>Copy Link</value> <value>Copy Link</value>
</data> </data>

View File

@@ -68,10 +68,10 @@
<!-- List --> <!-- List -->
<Style TargetType="ListView" <Style TargetType="BoxView"
Class="list-platform" Class="list-item-separator-bottom-platform"
ApplyToDerivedTypes="True"> ApplyToDerivedTypes="True">
<Setter Property="SeparatorColor" <Setter Property="BackgroundColor"
Value="Transparent" /> Value="Transparent" />
</Style> </Style>
<Style TargetType="StackLayout" <Style TargetType="StackLayout"
@@ -105,9 +105,15 @@
<Style TargetType="StackLayout" <Style TargetType="StackLayout"
Class="list-row-platform"> Class="list-row-platform">
</Style> </Style>
<Style TargetType="controls:ExtendedStackLayout"
Class="list-row-platform">
</Style>
<Style TargetType="Grid" <Style TargetType="Grid"
Class="list-row-platform"> Class="list-row-platform">
</Style> </Style>
<Style TargetType="controls:ExtendedGrid"
Class="list-row-platform">
</Style>
<Style TargetType="Label" <Style TargetType="Label"
Class="list-icon-platform" Class="list-icon-platform"
ApplyToDerivedTypes="True"> ApplyToDerivedTypes="True">

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms" <ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:Bit.App.Controls;assembly=BitwardenApp"
x:Class="Bit.App.Styles.Base"> x:Class="Bit.App.Styles.Base">
<!-- General --> <!-- General -->
@@ -124,7 +125,7 @@
</Style> </Style>
<!-- List --> <!-- List -->
<Style TargetType="ListView" <Style TargetType="CollectionView"
Class="list"> Class="list">
</Style> </Style>
<Style TargetType="StackLayout" <Style TargetType="StackLayout"
@@ -161,11 +162,21 @@
<Setter Property="Padding" <Setter Property="Padding"
Value="10, 12" /> Value="10, 12" />
</Style> </Style>
<Style TargetType="controls:ExtendedStackLayout"
Class="list-row">
<Setter Property="Padding"
Value="10, 12" />
</Style>
<Style TargetType="Grid" <Style TargetType="Grid"
Class="list-row"> Class="list-row">
<Setter Property="Padding" <Setter Property="Padding"
Value="2, 5, 0, 5" /> Value="2, 5, 0, 5" />
</Style> </Style>
<Style TargetType="controls:ExtendedGrid"
Class="list-row">
<Setter Property="Padding"
Value="2, 5, 0, 5" />
</Style>
<Style TargetType="BoxView" <Style TargetType="BoxView"
Class="list-section-separator-top"> Class="list-section-separator-top">
<Setter Property="HeightRequest" <Setter Property="HeightRequest"
@@ -176,6 +187,11 @@
<Setter Property="HeightRequest" <Setter Property="HeightRequest"
Value="1" /> Value="1" />
</Style> </Style>
<Style TargetType="BoxView"
Class="list-item-separator-bottom">
<Setter Property="HeightRequest"
Value="1" />
</Style>
<Style TargetType="Label" <Style TargetType="Label"
Class="list-title"> Class="list-title">
</Style> </Style>

View File

@@ -51,11 +51,14 @@
<Setter Property="BackgroundColor" <Setter Property="BackgroundColor"
Value="{StaticResource BackgroundColor}" /> Value="{StaticResource BackgroundColor}" />
</Style> </Style>
<Style TargetType="ListView" <Style TargetType="CollectionView"
ApplyToDerivedTypes="True"> ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor" <Setter Property="BackgroundColor"
Value="{StaticResource BackgroundColor}" /> Value="{StaticResource BackgroundColor}" />
<Setter Property="RefreshControlColor" </Style>
<Style TargetType="RefreshView"
ApplyToDerivedTypes="True">
<Setter Property="RefreshColor"
Value="{StaticResource PrimaryColor}" /> Value="{StaticResource PrimaryColor}" />
</Style> </Style>
<Style TargetType="ActivityIndicator" <Style TargetType="ActivityIndicator"
@@ -93,10 +96,10 @@
<!-- List --> <!-- List -->
<Style TargetType="ListView" <Style TargetType="BoxView"
Class="list-platform" Class="list-item-separator-bottom-platform"
ApplyToDerivedTypes="True"> ApplyToDerivedTypes="True">
<Setter Property="SeparatorColor" <Setter Property="BackgroundColor"
Value="{StaticResource ListItemBorderColor}" /> Value="{StaticResource ListItemBorderColor}" />
</Style> </Style>
<Style TargetType="StackLayout" <Style TargetType="StackLayout"
@@ -107,7 +110,7 @@
<Style TargetType="StackLayout" <Style TargetType="StackLayout"
Class="list-row-header-platform"> Class="list-row-header-platform">
<Setter Property="Padding" <Setter Property="Padding"
Value="10, 0, 10, 5" /> Value="10, 22, 10, 5" />
<Setter Property="VerticalOptions" <Setter Property="VerticalOptions"
Value="EndAndExpand" /> Value="EndAndExpand" />
</Style> </Style>
@@ -127,15 +130,19 @@
Class="list-section-separator-bottom-platform"> Class="list-section-separator-bottom-platform">
<Setter Property="Color" <Setter Property="Color"
Value="{StaticResource ListSectionBorderBottomColor}" /> Value="{StaticResource ListSectionBorderBottomColor}" />
<Setter Property="Margin"
Value="0, 0, 0, -1" />
</Style> </Style>
<Style TargetType="StackLayout" <Style TargetType="StackLayout"
Class="list-row-platform"> Class="list-row-platform">
</Style> </Style>
<Style TargetType="controls:ExtendedStackLayout"
Class="list-row-platform">
</Style>
<Style TargetType="Grid" <Style TargetType="Grid"
Class="list-row-platform"> Class="list-row-platform">
</Style> </Style>
<Style TargetType="controls:ExtendedGrid"
Class="list-row-platform">
</Style>
<Style TargetType="Label" <Style TargetType="Label"
Class="list-icon-platform" Class="list-icon-platform"
ApplyToDerivedTypes="True"> ApplyToDerivedTypes="True">

View File

@@ -0,0 +1,64 @@
using System;
using System.Globalization;
using Bit.Core;
using Bit.Core.Enums;
using Bit.Core.Models.View;
using Xamarin.Forms;
namespace Bit.App.Utilities
{
public class IconGlyphConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var cipher = value as CipherView;
return GetIcon(cipher);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
private string GetIcon(CipherView cipher)
{
string icon = null;
switch (cipher.Type)
{
case CipherType.Login:
icon = GetLoginIconGlyph(cipher);
break;
case CipherType.SecureNote:
icon = "\uf24a"; // fa-sticky-note-o
break;
case CipherType.Card:
icon = "\uf09d"; // fa-credit-card
break;
case CipherType.Identity:
icon = "\uf2c3"; // fa-id-card-o
break;
default:
break;
}
return icon;
}
string GetLoginIconGlyph(CipherView cipher)
{
var icon = "\uf0ac"; // fa-globe
if (cipher.Login.Uri != null)
{
var hostnameUri = cipher.Login.Uri;
if (hostnameUri.StartsWith(Constants.AndroidAppProtocol))
{
icon = "\uf17b"; // fa-android
}
else if (hostnameUri.StartsWith(Constants.iOSAppProtocol))
{
icon = "\uf179"; // fa-apple
}
}
return icon;
}
}
}

View File

@@ -0,0 +1,79 @@
using System;
using System.Globalization;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
using Xamarin.Forms;
namespace Bit.App.Utilities
{
public class IconImageConverter : IValueConverter
{
private readonly IEnvironmentService _environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var cipher = value as CipherView;
return GetIcon(cipher);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
private string GetIcon(CipherView cipher)
{
string icon = null;
switch (cipher.Type)
{
case CipherType.Login:
icon = GetLoginIconImage(cipher);
break;
default:
break;
}
return icon;
}
string GetLoginIconImage(CipherView cipher)
{
string image = null;
if (cipher.Login.Uri != null)
{
var hostnameUri = cipher.Login.Uri;
var isWebsite = false;
if (!hostnameUri.Contains("://") && hostnameUri.Contains("."))
{
hostnameUri = string.Concat("http://", hostnameUri);
isWebsite = true;
}
else
{
isWebsite = hostnameUri.StartsWith("http") && hostnameUri.Contains(".");
}
if (isWebsite)
{
var hostname = CoreHelpers.GetHostname(hostnameUri);
var iconsUrl = _environmentService.IconsUrl;
if (string.IsNullOrWhiteSpace(iconsUrl))
{
if (!string.IsNullOrWhiteSpace(_environmentService.BaseUrl))
{
iconsUrl = string.Format("{0}/icons", _environmentService.BaseUrl);
}
else
{
iconsUrl = "https://icons.bitwarden.net";
}
}
image = string.Format("{0}/{1}/icon.png", iconsUrl, hostname);
}
}
return image;
}
}
}

View File

@@ -0,0 +1,36 @@
using System;
using System.Globalization;
using Bit.Core.Enums;
using Bit.Core.Models.View;
using Xamarin.Forms;
namespace Bit.App.Utilities
{
public class SendIconGlyphConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var send = value as SendView;
if (send == null)
{
return null;
}
string icon = null;
switch (send.Type)
{
case SendType.Text:
icon = "\uf0f6"; // fa-file-text-o
break;
case SendType.File:
icon = "\uf016"; // fa-file-o
break;
}
return icon;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -66,10 +66,9 @@ namespace Bit.iOS.Core.Services
{ {
Duration = TimeSpan.FromSeconds(longDuration ? 5 : 3) Duration = TimeSpan.FromSeconds(longDuration ? 5 : 3)
}; };
if (TabBarVisible()) _toast.BottomMargin = 110;
{ _toast.LeftMargin = 20;
_toast.BottomMargin = 55; _toast.RightMargin = 20;
}
_toast.Show(); _toast.Show();
_toast.DismissCallback = () => _toast.DismissCallback = () =>
{ {

View File

@@ -27,7 +27,7 @@ namespace Bit.iOS.Core.Views
bgColor = Color.FromHex("#4c566a").ToUIColor(); bgColor = Color.FromHex("#4c566a").ToUIColor();
} }
BackgroundColor = bgColor.ColorWithAlpha(0.9f); BackgroundColor = bgColor.ColorWithAlpha(0.9f);
Layer.CornerRadius = 15; Layer.CornerRadius = 18;
Layer.MasksToBounds = true; Layer.MasksToBounds = true;
MessageLabel = new UILabel MessageLabel = new UILabel

View File

@@ -163,7 +163,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Xamarin.Essentials"> <PackageReference Include="Xamarin.Essentials">
<Version>1.5.3.2</Version> <Version>1.6.1</Version>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />