mirror of
https://github.com/bitwarden/android.git
synced 2024-12-26 19:08:32 +03: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
6 changed files with 63 additions and 26 deletions
|
@ -14,7 +14,7 @@ namespace Bit.App.Controls
|
||||||
{
|
{
|
||||||
AccountView = accountView;
|
AccountView = accountView;
|
||||||
AvatarImageSource = ServiceContainer.Resolve<IAvatarImageSourcePool>("avatarImageSourcePool")
|
AvatarImageSource = ServiceContainer.Resolve<IAvatarImageSourcePool>("avatarImageSourcePool")
|
||||||
?.GetOrCreateAvatar(AccountView.Name, AccountView.Email);
|
?.GetOrCreateAvatar(AccountView.UserId, AccountView.Name, AccountView.Email);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccountView AccountView
|
public AccountView AccountView
|
||||||
|
|
|
@ -3,6 +3,7 @@ using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
@ -10,7 +11,8 @@ namespace Bit.App.Controls
|
||||||
{
|
{
|
||||||
public class AvatarImageSource : StreamImageSource
|
public class AvatarImageSource : StreamImageSource
|
||||||
{
|
{
|
||||||
private string _data;
|
private readonly string _text;
|
||||||
|
private readonly string _id;
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
{
|
{
|
||||||
|
@ -21,20 +23,21 @@ namespace Bit.App.Controls
|
||||||
|
|
||||||
if (obj is AvatarImageSource avatar)
|
if (obj is AvatarImageSource avatar)
|
||||||
{
|
{
|
||||||
return avatar._data == _data;
|
return avatar._text == _text;
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.Equals(obj);
|
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;
|
_id = userId;
|
||||||
if (string.IsNullOrWhiteSpace(_data))
|
_text = name;
|
||||||
|
if (string.IsNullOrWhiteSpace(_text))
|
||||||
{
|
{
|
||||||
_data = email;
|
_text = email;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,24 +55,24 @@ namespace Bit.App.Controls
|
||||||
private Stream Draw()
|
private Stream Draw()
|
||||||
{
|
{
|
||||||
string chars;
|
string chars;
|
||||||
string upperData = null;
|
string upperCaseText = null;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(_data))
|
if (string.IsNullOrEmpty(_text))
|
||||||
{
|
{
|
||||||
chars = "..";
|
chars = "..";
|
||||||
}
|
}
|
||||||
else if (_data?.Length > 1)
|
else if (_text?.Length > 1)
|
||||||
{
|
{
|
||||||
upperData = _data.ToUpper();
|
upperCaseText = _text.ToUpper();
|
||||||
chars = GetFirstLetters(upperData, 2);
|
chars = GetFirstLetters(upperCaseText, 2);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
chars = upperData = _data.ToUpper();
|
chars = upperCaseText = _text.ToUpper();
|
||||||
}
|
}
|
||||||
|
|
||||||
var bgColor = StringToColor(upperData);
|
var bgColor = CoreHelpers.StringToColor(_id ?? upperCaseText, "#33ffffff");
|
||||||
var textColor = Color.White;
|
var textColor = CoreHelpers.TextColorFromBgColor(bgColor);
|
||||||
var size = 50;
|
var size = 50;
|
||||||
|
|
||||||
using (var bitmap = new SKBitmap(size * 2,
|
using (var bitmap = new SKBitmap(size * 2,
|
||||||
|
@ -85,7 +88,7 @@ namespace Bit.App.Controls
|
||||||
IsAntialias = true,
|
IsAntialias = true,
|
||||||
Style = SKPaintStyle.Fill,
|
Style = SKPaintStyle.Fill,
|
||||||
StrokeJoin = SKStrokeJoin.Miter,
|
StrokeJoin = SKStrokeJoin.Miter,
|
||||||
Color = SKColor.Parse(bgColor.ToHex())
|
Color = SKColor.Parse(bgColor)
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
var midX = canvas.LocalClipBounds.Size.ToSizeI().Width / 2;
|
var midX = canvas.LocalClipBounds.Size.ToSizeI().Width / 2;
|
||||||
|
@ -97,7 +100,7 @@ namespace Bit.App.Controls
|
||||||
IsAntialias = true,
|
IsAntialias = true,
|
||||||
Style = SKPaintStyle.Fill,
|
Style = SKPaintStyle.Fill,
|
||||||
StrokeJoin = SKStrokeJoin.Miter,
|
StrokeJoin = SKStrokeJoin.Miter,
|
||||||
Color = SKColor.Parse(bgColor.ToHex())
|
Color = SKColor.Parse(bgColor)
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
canvas.DrawCircle(midX, midY, radius, circlePaint);
|
canvas.DrawCircle(midX, midY, radius, circlePaint);
|
||||||
|
@ -108,7 +111,7 @@ namespace Bit.App.Controls
|
||||||
{
|
{
|
||||||
IsAntialias = true,
|
IsAntialias = true,
|
||||||
Style = SKPaintStyle.Fill,
|
Style = SKPaintStyle.Fill,
|
||||||
Color = SKColor.Parse(textColor.ToHex()),
|
Color = SKColor.Parse(textColor),
|
||||||
TextSize = textSize,
|
TextSize = textSize,
|
||||||
TextAlign = SKTextAlign.Center,
|
TextAlign = SKTextAlign.Center,
|
||||||
Typeface = typeface
|
Typeface = typeface
|
||||||
|
|
|
@ -5,19 +5,19 @@ namespace Bit.App.Controls
|
||||||
{
|
{
|
||||||
public interface IAvatarImageSourcePool
|
public interface IAvatarImageSourcePool
|
||||||
{
|
{
|
||||||
AvatarImageSource GetOrCreateAvatar(string name, string email);
|
AvatarImageSource GetOrCreateAvatar(string userId, string name, string email);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AvatarImageSourcePool : IAvatarImageSourcePool
|
public class AvatarImageSourcePool : IAvatarImageSourcePool
|
||||||
{
|
{
|
||||||
private readonly ConcurrentDictionary<string, AvatarImageSource> _cache = new ConcurrentDictionary<string, AvatarImageSource>();
|
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))
|
if (!_cache.TryGetValue(key, out var avatar))
|
||||||
{
|
{
|
||||||
avatar = new AvatarImageSource(name, email);
|
avatar = new AvatarImageSource(userId, name, email);
|
||||||
if (!_cache.TryAdd(key, avatar)
|
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.
|
!_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)
|
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();
|
return new AvatarImageSource();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
|
@ -264,5 +265,36 @@ namespace Bit.Core.Utilities
|
||||||
{
|
{
|
||||||
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj));
|
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));
|
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())
|
using (var avatarUIImage = await avatarImageSource.GetNativeImageAsync())
|
||||||
{
|
{
|
||||||
return avatarUIImage?.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal) ?? UIImage.GetSystemImage(DEFAULT_SYSTEM_AVATAR_IMAGE);
|
return avatarUIImage?.ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal) ?? UIImage.GetSystemImage(DEFAULT_SYSTEM_AVATAR_IMAGE);
|
||||||
|
|
Loading…
Reference in a new issue