diff --git a/assets/fonts/Roboto-Light.ttf b/assets/fonts/Roboto-Light.ttf new file mode 100644 index 0000000000..13bf13af00 Binary files /dev/null and b/assets/fonts/Roboto-Light.ttf differ diff --git a/res/values/arrays.xml b/res/values/arrays.xml index b8b25120a3..c9de19381a 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -39,6 +39,20 @@ Ukrainian Український + + #6dcaec + #cf9fe7 + #b6db49 + #ffd060 + #ff7979 + + #2cb1e1 + #c182e0 + #92c500 + #ffb61c + #f83a3a + + zz en diff --git a/src/org/thoughtcrime/securesms/ConversationItem.java b/src/org/thoughtcrime/securesms/ConversationItem.java index fbb007f9cc..6db7dd0f54 100644 --- a/src/org/thoughtcrime/securesms/ConversationItem.java +++ b/src/org/thoughtcrime/securesms/ConversationItem.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.res.TypedArray; +import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.net.Uri; @@ -40,6 +41,7 @@ import android.widget.Toast; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.DatabaseFactory; +import org.thoughtcrime.securesms.contacts.ContactPhotoFactory; import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord; @@ -405,7 +407,15 @@ public class ConversationItem extends LinearLayout { private void setContactPhotoForRecipient(final Recipient recipient) { if (contactPhoto == null) return; - contactPhoto.setImageBitmap(recipient.getCircleCroppedContactPhoto()); + Bitmap contactPhotoBitmap; + + if ((recipient.getContactPhoto() == ContactPhotoFactory.getDefaultContactPhoto(context)) && (groupThread)) { + contactPhotoBitmap = recipient.getGeneratedAvatar(context); + } else { + contactPhotoBitmap = recipient.getCircleCroppedContactPhoto(); + } + + contactPhoto.setImageBitmap(contactPhotoBitmap); contactPhoto.setOnClickListener(new View.OnClickListener() { @Override diff --git a/src/org/thoughtcrime/securesms/recipients/AvatarGenerator.java b/src/org/thoughtcrime/securesms/recipients/AvatarGenerator.java new file mode 100644 index 0000000000..188514435c --- /dev/null +++ b/src/org/thoughtcrime/securesms/recipients/AvatarGenerator.java @@ -0,0 +1,120 @@ +package org.thoughtcrime.securesms.recipients; + +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 org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.contacts.ContactPhotoFactory; +import org.thoughtcrime.securesms.util.BitmapUtil; + +/** + * Utility class to generate avatars for contacts who don't have a contact + * picture set. + * + * @author Lukas Barth + */ +public class AvatarGenerator { + + public static Bitmap generateFor(Context context, Recipient recipient) { + if ((recipient == null) || (recipient.getName() == null)) { + return BitmapUtil.getCircleCroppedBitmap(ContactPhotoFactory.getDefaultContactPhoto(context)); + } + + final int size = ContactPhotoFactory.getDefaultContactPhoto(context).getHeight(); + final Bitmap output = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + final Canvas canvas = new Canvas(output); + final int color = getColorForRecipient(recipient, context); + final Paint paint = new Paint(); + final int innerRectOffset = (int) Math.ceil((size - Math.sqrt(2) * (size / 2)) / 2); + final Rect innerRect = new Rect(innerRectOffset, innerRectOffset, + size - innerRectOffset, size - innerRectOffset); + + paint.setAntiAlias(true); + paint.setColor(color); + canvas.drawCircle(size / 2, size / 2, size / 2, paint); + + paint.setColor(Color.WHITE); + Typeface robotoLightTypeface = Typeface.createFromAsset(context.getAssets(), "fonts/Roboto-Light.ttf"); + paint.setTypeface(robotoLightTypeface); + setFontSize(innerRect, paint); + + paint.setTextAlign(Paint.Align.CENTER); + + int initialIndex = 0; + char[] contactName = recipient.getName().toCharArray(); + + if (contactName.length == 0) { + contactName = new char[]{'?'}; + initialIndex = 0; + } else { + while ((! Character.isLetter(contactName[initialIndex]))) { + initialIndex ++; + + if (initialIndex >= contactName.length) { + contactName[0] = '?'; + initialIndex = 0; + break; + } + } + } + + Rect textBounds = new Rect(); + paint.getTextBounds(contactName, initialIndex, 1, textBounds); + + int bottomOffset = (innerRect.height() - textBounds.height()) / 2; + + canvas.drawText(Character.toString(contactName[initialIndex]), + innerRect.centerX(), innerRect.bottom - bottomOffset, paint); + + return output; + } + + + private static int getColorForRecipient(Recipient recipient, Context context) { + if ((recipient == null) || (recipient.getName() == null)) { + return Color.WHITE; + } + + long nameHash = recipient.getName().hashCode(); + Resources res = context.getResources(); + TypedArray colorArray = res.obtainTypedArray(R.array.avatar_colors); + int index = Math.abs((int) (nameHash % colorArray.length())); + int color = colorArray.getColor(index, Color.BLACK); + + colorArray.recycle(); + + return color; + } + + private static int setFontSize(Rect textRect, Paint paint) { + boolean overflow = false; + int currentSize = 0; + + while (!overflow) { + currentSize++; + paint.setTextSize(currentSize); + + Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt(); + int textHeight = fontMetrics.descent - fontMetrics.ascent; + + if (textHeight > textRect.height()) { + overflow = true; + } + } + + currentSize--; + + currentSize *= 1.2; + + paint.setTextSize(currentSize); + + return currentSize; + } +} diff --git a/src/org/thoughtcrime/securesms/recipients/Recipient.java b/src/org/thoughtcrime/securesms/recipients/Recipient.java index 9efb4b70ef..b4a1160fc8 100644 --- a/src/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/src/org/thoughtcrime/securesms/recipients/Recipient.java @@ -54,6 +54,7 @@ public class Recipient implements Parcelable { private Bitmap contactPhoto; private Bitmap circleCroppedContactPhoto; + private Bitmap generatedAvatar; private Uri contactUri; @@ -64,6 +65,7 @@ public class Recipient implements Parcelable { this.circleCroppedContactPhoto = circleCroppedContactPhoto; this.contactPhoto = contactPhoto; this.recipientId = recipientId; + this.generatedAvatar = null; future.addListener(new FutureTaskListener() { @Override @@ -187,6 +189,13 @@ public class Recipient implements Parcelable { return this.circleCroppedContactPhoto; } + public synchronized Bitmap getGeneratedAvatar(Context context) { + if (this.generatedAvatar == null) + this.generatedAvatar = AvatarGenerator.generateFor(context, this); + + return this.generatedAvatar; + } + public static Recipient getUnknownRecipient(Context context) { return new Recipient("Unknown", "Unknown", -1, null, ContactPhotoFactory.getDefaultContactPhoto(context),