1
0
mirror of https://github.com/bitwarden/mobile.git synced 2024-11-23 11:45:38 +01:00

Updated avatar color selection logic (#2151)

* updated avatar color selection logic

* tweaks

* more tweaks

* formatting
This commit is contained in:
mp-bw 2022-10-26 12:34:54 -04:00 committed by GitHub
parent 505426cd6a
commit 5deba15373
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 63 additions and 26 deletions

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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();
}

View File

@ -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;
}
}
}

View File

@ -32,8 +32,9 @@ 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);