diff --git a/app/src/main/java/protect/card_locker/LetterBitmap.java b/app/src/main/java/protect/card_locker/LetterBitmap.java deleted file mode 100644 index 73ae8d79e..000000000 --- a/app/src/main/java/protect/card_locker/LetterBitmap.java +++ /dev/null @@ -1,140 +0,0 @@ -package protect.card_locker; - -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.Typeface; -import android.text.TextPaint; -import android.util.Log; - -import androidx.core.graphics.PaintCompat; - -/** - * Original from https://github.com/andOTP/andOTP/blob/master/app/src/main/java/org/shadowice/flocke/andotp/Utilities/LetterBitmap.java - * which was originally from http://stackoverflow.com/questions/23122088/colored-boxed-with-letters-a-la-gmail - * Used to create a {@link Bitmap} that contains a letter used in the English - * alphabet or digit, if there is no letter or digit available, a default image - * is shown instead. - */ -class LetterBitmap { - /** - * The letter bitmap - */ - private final Bitmap mBitmap; - /** - * The background color of the letter bitmap - */ - private final Integer mColor; - - /** - * Constructor for LetterTileProvider - * - * @param context The {@link Context} to use - * @param displayName The name used to create the letter for the tile - * @param key The key used to generate the background color for the tile - * @param tileLetterFontSize The font size used to display the letter - * @param width The desired width of the tile - * @param height The desired height of the tile - * @param backgroundColor (optional) color to use for background. - * @param textColor (optional) color to use for text. - */ - public LetterBitmap(Context context, String displayName, String key, int tileLetterFontSize, - int width, int height, Integer backgroundColor, Integer textColor) { - TextPaint paint = new TextPaint(); - - if (textColor != null) { - paint.setColor(textColor); - } else { - paint.setColor(Color.WHITE); - } - - paint.setTextAlign(Paint.Align.CENTER); - paint.setAntiAlias(true); - paint.setTextSize(tileLetterFontSize); - paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); - - if (backgroundColor == null) { - mColor = getDefaultColor(context, key); - } else { - mColor = backgroundColor; - } - - mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - String firstChar = displayName.substring(0, 1).toUpperCase(); - int firstCharEnd = 2; - while (firstCharEnd <= displayName.length()) { - // Test for the longest render-able string - // But ignore containing only a-Z0-9 to not render things like ffi as a single character - String test = displayName.substring(0, firstCharEnd); - if (!isAlphabetical(test) && PaintCompat.hasGlyph(paint, test)) { - firstChar = test; - } - firstCharEnd++; - } - - Log.d("LetterBitmap", "using sequence " + firstChar + " to render first char which has length " + firstChar.length()); - - final Canvas c = new Canvas(); - c.setBitmap(mBitmap); - c.drawColor(mColor); - - Rect bounds = new Rect(); - paint.getTextBounds(firstChar, 0, firstChar.length(), bounds); - c.drawText(firstChar, - 0, firstChar.length(), - width / 2.0f, (height - (bounds.bottom + bounds.top)) / 2.0f - , paint); - - } - - /** - * @return A {@link Bitmap} that contains a letter used in the English - * alphabet or digit, if there is no letter or digit available, a - * default image is shown instead - */ - public Bitmap getLetterTile() { - return mBitmap; - } - - /** - * @return background color used for letter title. - */ - public int getBackgroundColor() { - return mColor; - } - - /** - * @param key The key used to generate the tile color - * @return A new or previously chosen color for key used as the - * tile background color - */ - private static int pickColor(String key, TypedArray colors) { - // String.hashCode() is not supposed to change across java versions, so - // this should guarantee the same key always maps to the same color - final int color = Math.abs(key.hashCode()) % colors.length(); - return colors.getColor(color, Color.BLACK); - } - - private static boolean isAlphabetical(String string) { - return string.matches("[a-zA-Z0-9]*"); - } - - /** - * Determine the color which the letter tile will use if no default - * color is provided. - */ - public static int getDefaultColor(Context context, String key) { - final Resources res = context.getResources(); - - TypedArray colors = res.obtainTypedArray(R.array.letter_tile_colors); - int color = pickColor(key, colors); - colors.recycle(); - - return color; - } -} \ No newline at end of file diff --git a/app/src/main/java/protect/card_locker/LetterBitmap.kt b/app/src/main/java/protect/card_locker/LetterBitmap.kt new file mode 100644 index 000000000..6ac73931c --- /dev/null +++ b/app/src/main/java/protect/card_locker/LetterBitmap.kt @@ -0,0 +1,136 @@ +package protect.card_locker + +import android.content.Context +import android.content.res.TypedArray +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Rect +import android.graphics.Typeface +import android.text.TextPaint +import android.util.Log +import androidx.core.graphics.PaintCompat +import java.util.Locale +import kotlin.math.abs + +/** + * Original from https://github.com/andOTP/andOTP/blob/master/app/src/main/java/org/shadowice/flocke/andotp/Utilities/LetterBitmap.java + * which was originally from http://stackoverflow.com/questions/23122088/colored-boxed-with-letters-a-la-gmail + * Used to create a {@link Bitmap} that contains a letter used in the English + * alphabet or digit, if there is no letter or digit available, a default image + * is shown instead. + * + * @constructor Constructor for LetterTileProvider + * @param context The {@link Context} to use + * @param displayName The name used to create the letter for the tile + * @param key The key used to generate the background color for the tile + * @param tileLetterFontSize The font size used to display the letter + * @param width The desired width of the tile + * @param height The desired height of the tile + * @param backgroundColor (optional) color to use for background. + * @param textColor (optional) color to use for text. + */ +class LetterBitmap( + context: Context, displayName: String, key: String, tileLetterFontSize: Int, + width: Int, height: Int, backgroundColor: Int?, textColor: Int? +) { + /** + * A {@link Bitmap} that contains a letter used in the English + * alphabet or digit, if there is no letter or digit available, a + * default image is shown instead + */ + private val letterTile: Bitmap + + /** + * The background color of the letter bitmap + */ + private val mColor: Int + + init { + val paint = TextPaint().apply { + color = textColor ?: Color.WHITE + textAlign = Paint.Align.CENTER + isAntiAlias = true + textSize = tileLetterFontSize.toFloat() + typeface = Typeface.defaultFromStyle(Typeface.BOLD) + } + + mColor = backgroundColor ?: getDefaultColor(context, key) + + this.letterTile = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) + + var firstChar = displayName.substring(0, 1).uppercase(Locale.getDefault()) + var firstCharEnd = 2 + while (firstCharEnd <= displayName.length) { + // Test for the longest render-able string + // But ignore containing only a-Z0-9 to not render things like ffi as a single character + val test = displayName.substring(0, firstCharEnd) + if (!isAlphabetical(test) && PaintCompat.hasGlyph(paint, test)) { + firstChar = test + } + firstCharEnd++ + } + + Log.d( + "LetterBitmap", + "using sequence $firstChar to render first char which has length ${firstChar.length}" + ) + + Canvas().apply { + setBitmap(this@LetterBitmap.letterTile) + drawColor(mColor) + + val bounds = Rect() + paint.getTextBounds(firstChar, 0, firstChar.length, bounds) + drawText( + firstChar, + 0, firstChar.length, + width / 2.0f, (height - (bounds.bottom + bounds.top)) / 2.0f, + paint + ) + } + } + + val backgroundColor: Int + /** + * @return background color used for letter title. + */ + get() = mColor + + fun getLetterTile(): Bitmap { + return letterTile + } + + companion object { + /** + * @param key The key used to generate the tile color + * @return A new or previously chosen color for `key` used as the + * tile background color + */ + private fun pickColor(key: String, colors: TypedArray): Int { + // String.hashCode() is not supposed to change across java versions, so + // this should guarantee the same key always maps to the same color + val color = abs(key.hashCode()) % colors.length() + return colors.getColor(color, Color.BLACK) + } + + private fun isAlphabetical(string: String): Boolean { + return string.matches("[a-zA-Z0-9]*".toRegex()) + } + + /** + * Determine the color which the letter tile will use if no default + * color is provided. + */ + fun getDefaultColor(context: Context, key: String): Int { + val res = context.resources + + val colors = res.obtainTypedArray(R.array.letter_tile_colors) + val color: Int = pickColor(key, colors) + colors.recycle() + + return color + } + } +} \ No newline at end of file diff --git a/app/src/main/java/protect/card_locker/Utils.java b/app/src/main/java/protect/card_locker/Utils.java index 2703dca10..1d46955f9 100644 --- a/app/src/main/java/protect/card_locker/Utils.java +++ b/app/src/main/java/protect/card_locker/Utils.java @@ -143,7 +143,7 @@ public class Utils { int pixelSize = context.getResources().getDimensionPixelSize(R.dimen.tileLetterImageSize); if (backgroundColor == null) { - backgroundColor = LetterBitmap.getDefaultColor(context, store); + backgroundColor = LetterBitmap.Companion.getDefaultColor(context, store); } return new LetterBitmap(context, store, store, @@ -1129,7 +1129,7 @@ public class Utils { } public static int getHeaderColor(Context context, LoyaltyCard loyaltyCard) { - return loyaltyCard.headerColor != null ? loyaltyCard.headerColor : LetterBitmap.getDefaultColor(context, loyaltyCard.store); + return loyaltyCard.headerColor != null ? loyaltyCard.headerColor : LetterBitmap.Companion.getDefaultColor(context, loyaltyCard.store); } public static String checksum(InputStream input) throws IOException {