From c7ee4f1a60a5494abc4f6d91e087a664263910a9 Mon Sep 17 00:00:00 2001 From: tobiaskaminsky Date: Fri, 13 Oct 2017 08:23:30 +0200 Subject: [PATCH] use same algo for avatar placeholder --- .../com/owncloud/android/ui/TextDrawable.java | 8 +- .../owncloud/android/utils/BitmapUtils.java | 106 ++++++++++++++++-- 2 files changed, 98 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/TextDrawable.java b/src/main/java/com/owncloud/android/ui/TextDrawable.java index aab89f0617..035df79435 100644 --- a/src/main/java/com/owncloud/android/ui/TextDrawable.java +++ b/src/main/java/com/owncloud/android/ui/TextDrawable.java @@ -113,10 +113,10 @@ public class TextDrawable extends Drawable { @NonNull public static TextDrawable createNamedAvatar(String name, float radiusInDp) throws UnsupportedEncodingException, NoSuchAlgorithmException { - int[] rgb = BitmapUtils.calculateRGB(name); - TextDrawable avatar = new TextDrawable( - name.substring(0, 1).toUpperCase(), rgb[0], rgb[1], rgb[2], radiusInDp); - return avatar; + int[] hsl = BitmapUtils.calculateHSL(name); + int[] rgb = BitmapUtils.HSLtoRGB(hsl[0], hsl[1], hsl[2], 1); + + return new TextDrawable(name.substring(0, 1).toUpperCase(), rgb[0], rgb[1], rgb[2], radiusInDp); } /** diff --git a/src/main/java/com/owncloud/android/utils/BitmapUtils.java b/src/main/java/com/owncloud/android/utils/BitmapUtils.java index 0f97f9e097..4d8db95d02 100644 --- a/src/main/java/com/owncloud/android/utils/BitmapUtils.java +++ b/src/main/java/com/owncloud/android/utils/BitmapUtils.java @@ -29,16 +29,17 @@ import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; import com.owncloud.android.lib.common.utils.Log_OC; +import org.apache.commons.codec.binary.Hex; + import java.io.UnsupportedEncodingException; -import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.Locale; /** * Utility class with methods for decoding Bitmaps. */ public class BitmapUtils { + public static final String TAG = BitmapUtils.class.getSimpleName(); /** @@ -188,7 +189,7 @@ public class BitmapUtils { * * @param h Hue is specified as degrees in the range 0 - 360. * @param s Saturation is specified as a percentage in the range 1 - 100. - * @param l Lumanance is specified as a percentage in the range 1 - 100. + * @param l Luminance is specified as a percentage in the range 1 - 100. * @param alpha the alpha value between 0 - 1 * adapted from https://svn.codehaus.org/griffon/builders/gfxbuilder/tags/GFXBUILDER_0.2/ * gfxbuilder-core/src/main/com/camick/awt/HSLColor.java @@ -267,17 +268,98 @@ public class BitmapUtils { * @throws UnsupportedEncodingException if the charset is not supported * @throws NoSuchAlgorithmException if the specified algorithm is not available */ - public static int[] calculateRGB(String name) throws UnsupportedEncodingException, NoSuchAlgorithmException { - // using adapted algorithm from /core/js/placeholder.js:50 - byte[] seed = name.getBytes("UTF-8"); - MessageDigest md = MessageDigest.getInstance("MD5"); - Integer seedMd5Int = String.format(Locale.ROOT, "%032x", - new BigInteger(1, md.digest(seed))).hashCode(); + public static int[] calculateHSL(String name) throws UnsupportedEncodingException, NoSuchAlgorithmException { + // using adapted algorithm from https://github.com/nextcloud/server/blob/master/core/js/placeholder.js#L126 - double maxRange = Integer.MAX_VALUE; - float hue = (float) (seedMd5Int / maxRange * 360); + String[] result = new String[]{"0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"}; + double[] rgb = new double[]{0, 0, 0}; + int sat = 70; + int lum = 68; + int modulo = 16; - return BitmapUtils.HSLtoRGB(hue, 90.0f, 65.0f, 1.0f); + String hash = name.toLowerCase().replaceAll("[^0-9a-f]", ""); + + if (!hash.matches("^[0-9a-f]{32}")) { + hash = md5(hash); + } + + // Splitting evenly the string + for (int i = 0; i < hash.length(); i++) { + result[i % modulo] = result[i % modulo] + String.valueOf(Integer.parseInt(hash.substring(i, i + 1), 16)); + } + + // Converting our data into a usable rgb format + // Start at 1 because 16%3=1 but 15%3=0 and makes the repartition even + for (int count = 1; count < modulo; count++) { + rgb[count % 3] += (Integer.parseInt(result[count])); + } + + // Reduce values bigger than rgb requirements + rgb[0] = rgb[0] % 255; + rgb[1] = rgb[1] % 255; + rgb[2] = rgb[2] % 255; + + double[] hsl = rgbToHsl(rgb[0], rgb[1], rgb[2]); + + // Classic formula to check the brightness for our eye + // If too bright, lower the sat + double bright = Math.sqrt(0.299 * Math.pow(rgb[0], 2) + 0.587 * Math.pow(rgb[1], 2) + 0.114 + * Math.pow(rgb[2], 2)); + + if (bright >= 200) { + sat = 60; + } + + return new int[]{(int) (hsl[0] * 360), sat, lum}; + } + + private static double[] rgbToHsl(double r, double g, double b) { + r /= 255; + g /= 255; + b /= 255; + + double max = Math.max(r, Math.max(g, b)); + double min = Math.min(r, Math.min(g, b)); + double h = (max + min) / 2; + double s; + double l = (max + min) / 2; + + if (max == min) { + h = s = 0; // achromatic + } else { + double d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + + if (max == r) { + h = (g - b) / d + (g < b ? 6 : 0); + } else if (max == g) { + h = (b - r) / d + 2; + } else if (max == b) { + h = (r - g) / d + 4; + } + h /= 6; + } + + double[] hsl = new double[]{0.0, 0.0, 0.0}; + hsl[0] = h; + hsl[1] = s; + hsl[2] = l; + + return hsl; + } + + public static String md5(String string) { + try { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + md5.update(string.getBytes()); + + return new String(Hex.encodeHex(md5.digest())); + + } catch (Exception e) { + Log_OC.e(TAG, e.getMessage()); + } + + return ""; } /**