mirror of
https://github.com/bitwarden/mobile.git
synced 2024-11-27 12:26:31 +01:00
Updated avatar color selection logic (#2151)
* updated avatar color selection logic * tweaks * more tweaks * formatting
This commit is contained in:
parent
505426cd6a
commit
5deba15373
@ -14,7 +14,7 @@ namespace Bit.App.Controls
|
||||
{
|
||||
AccountView = accountView;
|
||||
AvatarImageSource = ServiceContainer.Resolve<IAvatarImageSourcePool>("avatarImageSourcePool")
|
||||
?.GetOrCreateAvatar(AccountView.Name, AccountView.Email);
|
||||
?.GetOrCreateAvatar(AccountView.UserId, AccountView.Name, AccountView.Email);
|
||||
}
|
||||
|
||||
public AccountView AccountView
|
||||
|
@ -3,6 +3,7 @@ using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Utilities;
|
||||
using SkiaSharp;
|
||||
using Xamarin.Forms;
|
||||
|
||||
@ -10,7 +11,8 @@ namespace Bit.App.Controls
|
||||
{
|
||||
public class AvatarImageSource : StreamImageSource
|
||||
{
|
||||
private string _data;
|
||||
private readonly string _text;
|
||||
private readonly string _id;
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
@ -21,20 +23,21 @@ namespace Bit.App.Controls
|
||||
|
||||
if (obj is AvatarImageSource avatar)
|
||||
{
|
||||
return avatar._data == _data;
|
||||
return avatar._text == _text;
|
||||
}
|
||||
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode() => _data?.GetHashCode() ?? -1;
|
||||
public override int GetHashCode() => _text?.GetHashCode() ?? -1;
|
||||
|
||||
public AvatarImageSource(string name = null, string email = null)
|
||||
public AvatarImageSource(string userId = null, string name = null, string email = null)
|
||||
{
|
||||
_data = name;
|
||||
if (string.IsNullOrWhiteSpace(_data))
|
||||
_id = userId;
|
||||
_text = name;
|
||||
if (string.IsNullOrWhiteSpace(_text))
|
||||
{
|
||||
_data = email;
|
||||
_text = email;
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,24 +55,24 @@ namespace Bit.App.Controls
|
||||
private Stream Draw()
|
||||
{
|
||||
string chars;
|
||||
string upperData = null;
|
||||
string upperCaseText = null;
|
||||
|
||||
if (string.IsNullOrEmpty(_data))
|
||||
if (string.IsNullOrEmpty(_text))
|
||||
{
|
||||
chars = "..";
|
||||
}
|
||||
else if (_data?.Length > 1)
|
||||
else if (_text?.Length > 1)
|
||||
{
|
||||
upperData = _data.ToUpper();
|
||||
chars = GetFirstLetters(upperData, 2);
|
||||
upperCaseText = _text.ToUpper();
|
||||
chars = GetFirstLetters(upperCaseText, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
chars = upperData = _data.ToUpper();
|
||||
chars = upperCaseText = _text.ToUpper();
|
||||
}
|
||||
|
||||
var bgColor = StringToColor(upperData);
|
||||
var textColor = Color.White;
|
||||
var bgColor = CoreHelpers.StringToColor(_id ?? upperCaseText, "#33ffffff");
|
||||
var textColor = CoreHelpers.TextColorFromBgColor(bgColor);
|
||||
var size = 50;
|
||||
|
||||
using (var bitmap = new SKBitmap(size * 2,
|
||||
@ -85,7 +88,7 @@ namespace Bit.App.Controls
|
||||
IsAntialias = true,
|
||||
Style = SKPaintStyle.Fill,
|
||||
StrokeJoin = SKStrokeJoin.Miter,
|
||||
Color = SKColor.Parse(bgColor.ToHex())
|
||||
Color = SKColor.Parse(bgColor)
|
||||
})
|
||||
{
|
||||
var midX = canvas.LocalClipBounds.Size.ToSizeI().Width / 2;
|
||||
@ -97,7 +100,7 @@ namespace Bit.App.Controls
|
||||
IsAntialias = true,
|
||||
Style = SKPaintStyle.Fill,
|
||||
StrokeJoin = SKStrokeJoin.Miter,
|
||||
Color = SKColor.Parse(bgColor.ToHex())
|
||||
Color = SKColor.Parse(bgColor)
|
||||
})
|
||||
{
|
||||
canvas.DrawCircle(midX, midY, radius, circlePaint);
|
||||
@ -108,7 +111,7 @@ namespace Bit.App.Controls
|
||||
{
|
||||
IsAntialias = true,
|
||||
Style = SKPaintStyle.Fill,
|
||||
Color = SKColor.Parse(textColor.ToHex()),
|
||||
Color = SKColor.Parse(textColor),
|
||||
TextSize = textSize,
|
||||
TextAlign = SKTextAlign.Center,
|
||||
Typeface = typeface
|
||||
|
@ -5,19 +5,19 @@ namespace Bit.App.Controls
|
||||
{
|
||||
public interface IAvatarImageSourcePool
|
||||
{
|
||||
AvatarImageSource GetOrCreateAvatar(string name, string email);
|
||||
AvatarImageSource GetOrCreateAvatar(string userId, string name, string email);
|
||||
}
|
||||
|
||||
public class AvatarImageSourcePool : IAvatarImageSourcePool
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, AvatarImageSource> _cache = new ConcurrentDictionary<string, AvatarImageSource>();
|
||||
|
||||
public AvatarImageSource GetOrCreateAvatar(string name, string email)
|
||||
public AvatarImageSource GetOrCreateAvatar(string userId, string name, string email)
|
||||
{
|
||||
var key = $"{name}{email}";
|
||||
var key = $"{userId}{name}{email}";
|
||||
if (!_cache.TryGetValue(key, out var avatar))
|
||||
{
|
||||
avatar = new AvatarImageSource(name, email);
|
||||
avatar = new AvatarImageSource(userId, name, email);
|
||||
if (!_cache.TryAdd(key, avatar)
|
||||
&&
|
||||
!_cache.TryGetValue(key, out avatar)) // If add fails another thread created the avatar in between the first try get and the try add.
|
||||
|
@ -129,7 +129,8 @@ namespace Bit.App.Pages
|
||||
{
|
||||
if (useCurrentActiveAccount)
|
||||
{
|
||||
return new AvatarImageSource(await _stateService.GetNameAsync(), await _stateService.GetEmailAsync());
|
||||
return new AvatarImageSource(await _stateService.GetActiveUserIdAsync(),
|
||||
await _stateService.GetNameAsync(), await _stateService.GetEmailAsync());
|
||||
}
|
||||
return new AvatarImageSource();
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
@ -264,5 +265,36 @@ namespace Bit.Core.Utilities
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj));
|
||||
}
|
||||
|
||||
public static string TextColorFromBgColor(string hexColor, int threshold = 166)
|
||||
{
|
||||
if (new ColorConverter().ConvertFromString(hexColor) is Color bgColor)
|
||||
{
|
||||
var luminance = bgColor.R * 0.299 + bgColor.G * 0.587 + bgColor.B * 0.114;
|
||||
return luminance > threshold ? "#ff000000" : "#ffffffff";
|
||||
}
|
||||
|
||||
return "#ff000000";
|
||||
}
|
||||
|
||||
public static string StringToColor(string str, string fallback)
|
||||
{
|
||||
if (str == null)
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
var hash = 0;
|
||||
for (var i = 0; i < str.Length; i++)
|
||||
{
|
||||
hash = str[i] + ((hash << 5) - hash);
|
||||
}
|
||||
var color = "#FF";
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
var value = (hash >> (i * 8)) & 0xff;
|
||||
color += Convert.ToString(value, 16).PadLeft(2, '0');
|
||||
}
|
||||
return color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,8 @@ namespace Bit.iOS.Core.Utilities
|
||||
throw new NullReferenceException(nameof(_stateService));
|
||||
}
|
||||
|
||||
var avatarImageSource = new AvatarImageSource(await _stateService.GetNameAsync(), await _stateService.GetEmailAsync());
|
||||
var avatarImageSource = new AvatarImageSource(await _stateService.GetActiveUserIdAsync(),
|
||||
await _stateService.GetNameAsync(), await _stateService.GetEmailAsync());
|
||||
using (var avatarUIImage = await avatarImageSource.GetNativeImageAsync())
|
||||
{
|
||||
return avatarUIImage?.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal) ?? UIImage.GetSystemImage(DEFAULT_SYSTEM_AVATAR_IMAGE);
|
||||
|
Loading…
Reference in New Issue
Block a user