avatar: refactor drawing code
Change-Id: I85258ec690aa0d7328d07300e12d9ea1a316ab39
diff --git a/ring-android/app/src/main/AndroidManifest.xml b/ring-android/app/src/main/AndroidManifest.xml
index 2f2d10a..8b69d7c 100644
--- a/ring-android/app/src/main/AndroidManifest.xml
+++ b/ring-android/app/src/main/AndroidManifest.xml
@@ -260,7 +260,7 @@
</activity>
<activity
android:name=".client.ConversationActivity"
- android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize"
+ android:configChanges="screenSize|screenLayout|smallestScreenSize"
android:label="@string/app_name"
android:parentActivityName=".client.HomeActivity"
android:screenOrientation="fullUser"
diff --git a/ring-android/app/src/main/java/cx/ring/adapters/ConversationAdapter.java b/ring-android/app/src/main/java/cx/ring/adapters/ConversationAdapter.java
index 3325a82..fd14121 100644
--- a/ring-android/app/src/main/java/cx/ring/adapters/ConversationAdapter.java
+++ b/ring-android/app/src/main/java/cx/ring/adapters/ConversationAdapter.java
@@ -48,6 +48,7 @@
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.CenterInside;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
+import com.bumptech.glide.request.target.DrawableImageViewTarget;
import java.io.File;
import java.text.DateFormat;
@@ -86,7 +87,6 @@
private final ArrayList<ConversationElement> mConversationElements = new ArrayList<>();
private final ConversationPresenter presenter;
private final ConversationFragment conversationFragment;
- private byte[] mPhoto;
private final ColorStateList mErrorColor;
private final int hPadding;
private final int vPadding;
@@ -161,11 +161,8 @@
/**
* Updates the contact photo to use for this conversation
- *
- * @param photo contact photo to display.
*/
- public void setPhoto(byte[] photo) {
- mPhoto = photo;
+ public void setPhoto() {
notifyDataSetChanged();
}
@@ -352,7 +349,7 @@
GlideApp.with(context)
.load(path)
.apply(PICTURE_OPTIONS)
- .into(conversationViewHolder.mPhoto);
+ .into(new DrawableImageViewTarget(conversationViewHolder.mPhoto).waitForLayout());
((LinearLayout)conversationViewHolder.mAnswerLayout).setGravity(file.isOutgoing() ? Gravity.END : Gravity.START);
conversationViewHolder.mPhoto.setOnClickListener(v -> {
@@ -444,17 +441,7 @@
final Context context = convViewHolder.itemView.getContext();
if (textMessage.isIncoming() && !sameAsPreviousMsg) {
- Drawable contactPicture = AvatarFactory.getAvatar(
- context,
- mPhoto,
- contact.getUsername(),
- textMessage.getNumberUri().getHost());
-
- Glide.with(context)
- .load(contactPicture)
- .apply(AvatarFactory.getGlideOptions(true, true))
- //.transition(DrawableTransitionOptions.withCrossFade())
- .into(convViewHolder.mPhoto);
+ AvatarFactory.loadGlideAvatar(convViewHolder.mPhoto, contact);
}
switch (textMessage.getStatus()) {
diff --git a/ring-android/app/src/main/java/cx/ring/adapters/SmartListAdapter.java b/ring-android/app/src/main/java/cx/ring/adapters/SmartListAdapter.java
index b494423..5d61724 100644
--- a/ring-android/app/src/main/java/cx/ring/adapters/SmartListAdapter.java
+++ b/ring-android/app/src/main/java/cx/ring/adapters/SmartListAdapter.java
@@ -47,6 +47,7 @@
import cx.ring.model.TextMessage;
import cx.ring.smartlist.SmartListViewModel;
import cx.ring.viewholders.SmartListViewHolder;
+import cx.ring.views.AvatarDrawable;
public class SmartListAdapter extends RecyclerView.Adapter<SmartListViewHolder> {
@@ -98,22 +99,9 @@
holder.convStatus.setTypeface(null, Typeface.NORMAL);
}
- final Drawable contactPicture = AvatarFactory.getAvatar(
- holder.itemView.getContext(),
- contact.getPhoto(),
- smartListViewModel.getContactName(),
- smartListViewModel.getUuid());
- if (contactPicture != holder.picture) {
- holder.picture = contactPicture;
- holder.photo.setImageDrawable(null);
- Glide.with(holder.itemView.getContext())
- .load(contactPicture)
- .apply(AvatarFactory.getGlideOptions(true, true))
- .into(holder.photo);
- }
-
+ holder.photo.setImageDrawable(new AvatarDrawable(holder.photo.getContext(), contact));
+ //AvatarFactory.loadGlideAvatar(holder.photo, contact);
holder.online.setVisibility(smartListViewModel.isOnline() ? View.VISIBLE : View.GONE);
-
holder.bind(listener, smartListViewModel);
}
diff --git a/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.java b/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.java
index 480f9c1..821a178 100644
--- a/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.java
+++ b/ring-android/app/src/main/java/cx/ring/client/ConversationActivity.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.os.Bundle;
+import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.KeyEvent;
@@ -54,8 +55,9 @@
ButterKnife.bind(this);
setSupportActionBar(mToolbar);
-
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ ActionBar ab = getSupportActionBar();
+ if (ab != null)
+ ab.setDisplayHomeAsUpEnabled(true);
Intent intent = getIntent();
String action = intent == null ? null : intent.getAction();
diff --git a/ring-android/app/src/main/java/cx/ring/contactrequests/BlackListViewHolder.java b/ring-android/app/src/main/java/cx/ring/contactrequests/BlackListViewHolder.java
index d59a66d..a55bb75 100644
--- a/ring-android/app/src/main/java/cx/ring/contactrequests/BlackListViewHolder.java
+++ b/ring-android/app/src/main/java/cx/ring/contactrequests/BlackListViewHolder.java
@@ -52,7 +52,9 @@
}
public void bind(final BlackListListeners clickListener, final CallContact contact) {
- byte[] photo = contact.getPhoto();
+ AvatarFactory.loadGlideAvatar(mPhoto, contact);
+
+ /*byte[] photo = contact.getPhoto();
Drawable contactPicture = AvatarFactory.getAvatar(
itemView.getContext(),
@@ -64,7 +66,7 @@
.load(contactPicture)
.apply(AvatarFactory.getGlideOptions(true, true))
.transition(DrawableTransitionOptions.withCrossFade())
- .into(mPhoto);
+ .into(mPhoto);*/
mDisplayname.setText(contact.getRingUsername());
diff --git a/ring-android/app/src/main/java/cx/ring/contacts/AvatarFactory.java b/ring-android/app/src/main/java/cx/ring/contacts/AvatarFactory.java
index e1b3ca4..197f12e 100644
--- a/ring-android/app/src/main/java/cx/ring/contacts/AvatarFactory.java
+++ b/ring-android/app/src/main/java/cx/ring/contacts/AvatarFactory.java
@@ -2,6 +2,7 @@
* Copyright (C) 2004-2018 Savoir-faire Linux Inc.
*
* Author: Pierre Duchemin <pierre.duchemin@savoirfairelinux.com>
+ * Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,43 +21,36 @@
package cx.ring.contacts;
+import android.app.Fragment;
import android.content.Context;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
-import android.support.v7.content.res.AppCompatResources;
-import android.util.Log;
-import android.util.LruCache;
+import android.text.TextUtils;
+import android.widget.ImageView;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.RequestBuilder;
+import com.bumptech.glide.RequestManager;
+import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import com.bumptech.glide.request.RequestOptions;
import cx.ring.R;
-import cx.ring.model.Uri;
+import cx.ring.model.CallContact;
import cx.ring.utils.CircleTransform;
-import cx.ring.utils.HashUtils;
+import cx.ring.views.AvatarDrawable;
import ezvcard.VCard;
+import ezvcard.property.FormattedName;
+import io.reactivex.Single;
+import io.reactivex.schedulers.Schedulers;
public class AvatarFactory {
- private static final String TAG = AvatarFactory.class.getSimpleName();
-
- // ordered to have the same colors on all clients
- private static final int[] contactColors = {
- R.color.red_500, R.color.pink_500,
- R.color.purple_500, R.color.deep_purple_500,
- R.color.indigo_500, R.color.blue_500,
- R.color.cyan_500, R.color.teal_500,
- R.color.green_500, R.color.light_green_500,
- R.color.grey_500, R.color.lime_500,
- R.color.amber_500, R.color.deep_orange_500,
- R.color.brown_500, R.color.blue_grey_500
- };
- private static final int DEFAULT_AVATAR_SIZE = 128;
-
+ public static final int SIZE_AB = 36;
+ public static final int SIZE_NOTIF = 48;
+
private static final RequestOptions GLIDE_OPTIONS = new RequestOptions()
.centerCrop()
.error(R.drawable.ic_contact_picture_fallback);
@@ -66,149 +60,87 @@
.error(R.drawable.ic_contact_picture_fallback)
.transform(new CircleTransform());
- private static final Paint AVATAR_TEXT_PAINT = new Paint();
- static {
- AVATAR_TEXT_PAINT.setTextAlign(Paint.Align.CENTER);
- AVATAR_TEXT_PAINT.setColor(Color.WHITE);
- AVATAR_TEXT_PAINT.setAntiAlias(true);
+ private AvatarFactory() {}
+
+ private static Drawable getDrawable(Context context, byte[] photo, String profileName, String username, String id) {
+ return new AvatarDrawable(context, photo, TextUtils.isEmpty(profileName) ? username : profileName, id, true);
}
- private static final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
-
- // Use 1/8th of the available memory for this memory cache.
- private static final LruCache<String, BitmapDrawable> mMemoryCache = new LruCache<String, BitmapDrawable>(maxMemory / 8) {
- @Override
- protected int sizeOf(String key, BitmapDrawable bitmap) {
- return bitmap.getBitmap() == null ? 0 : bitmap.getBitmap().getByteCount() / 1024;
- }
- };
-
- private AvatarFactory() {
+ private static <T> RequestBuilder<T> getGlideRequest(Context context, RequestBuilder<T> request, byte[] photo, String profileName, String username, String id) {
+ return request.load(getDrawable(context, photo, profileName, username, id));
}
- public static Drawable getAvatar(Context context, VCard vcard, String username, String ringId) {
- return getAvatar(context, vcard, username, ringId, Float.valueOf(context.getResources().getDisplayMetrics().density * 128).intValue());
+ public static RequestBuilder<Drawable> getGlideAvatar(Context context, RequestManager manager, CallContact contact) {
+ return getGlideRequest(context, manager.asDrawable(), contact.getPhoto(), contact.getProfileName(), contact.getUsername(), contact.getPrimaryNumber());
}
- public static Drawable getAvatar(Context context, VCard vcard, String username, String ringId, int pictureSize) {
- if (vcard == null || pictureSize <= 0) {
- throw new IllegalArgumentException();
- }
-
- byte[] contactPhoto = null;
- if (vcard.getPhotos() != null && !vcard.getPhotos().isEmpty()) {
- contactPhoto = vcard.getPhotos().get(0).getData();
- }
-
- return getAvatar(context, contactPhoto, username, ringId, pictureSize, false);
+ public static Single<Drawable> getAvatar(Context context, CallContact contact) {
+ return Single.fromCallable(() -> new AvatarDrawable(context, contact));
}
- public static BitmapDrawable getAvatar(Context context, byte[] photo, String username, String ringId, boolean noCache) {
- return getAvatar(context, photo, username, ringId, Float.valueOf(context.getResources().getDisplayMetrics().density * DEFAULT_AVATAR_SIZE).intValue(), noCache);
+ private static Bitmap drawableToBitmap(Drawable drawable, int size) {
+ if (drawable instanceof BitmapDrawable) {
+ return ((BitmapDrawable)drawable).getBitmap();
+ }
+
+ int width = drawable.getIntrinsicWidth();
+ width = width > 0 ? width : size;
+ int height = drawable.getIntrinsicHeight();
+ height = height > 0 ? height : size;
+
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+
+ return bitmap;
}
- public static BitmapDrawable getAvatar(Context context, byte[] photo, String username, String ringId) {
- return getAvatar(context, photo, username, ringId, Float.valueOf(context.getResources().getDisplayMetrics().density * DEFAULT_AVATAR_SIZE).intValue(), false);
+ public static Single<Bitmap> getBitmapAvatar(Context context, CallContact contact, int size) {
+ return getAvatar(context, contact)
+ .map(d -> drawableToBitmap(d, size))
+ .subscribeOn(Schedulers.computation());
}
- public static BitmapDrawable getAvatar(Context context, byte[] photo, String username, String ringId, int pictureSize, boolean noCache) {
- if (context == null || pictureSize <= 0) {
- throw new IllegalArgumentException();
- }
- String key = ringId + pictureSize + username;
- BitmapDrawable bmp = noCache ? null : mMemoryCache.get(key);
- if (bmp != null)
- return bmp;
-
- Log.d(TAG, "getAvatar: username=" + username + ", ringid=" + ringId + ", pictureSize=" + pictureSize);
-
- if (photo != null && photo.length > 0) {
- bmp = new BitmapDrawable(context.getResources(), BitmapFactory.decodeByteArray(photo, 0, photo.length));
- mMemoryCache.put(key, bmp);
- return bmp;
+ public static RequestBuilder<Drawable> getGlideAvatar(Context context, RequestManager manager, VCard vcard, String username, String ringId) {
+ byte[] photo = null;
+ String profile = null;
+ if (vcard != null) {
+ if (vcard.getPhotos() != null && !vcard.getPhotos().isEmpty()) {
+ photo = vcard.getPhotos().get(0).getData();
+ }
+ FormattedName name = vcard.getFormattedName();
+ if (name != null) {
+ String n = name.getValue();
+ if (!TextUtils.isEmpty(n))
+ profile = n;
+ }
}
- Uri uriUsername = new Uri(username);
- Uri uri = new Uri(ringId);
- Character firstCharacter = getFirstCharacter(uriUsername.getRawRingId());
- if (uri.isEmpty() || uriUsername.isRingId() || firstCharacter == null) {
- bmp = createDefaultAvatar(context, generateAvatarColor(uri.getRawUriString()), pictureSize);
- mMemoryCache.put(key, bmp);
- return bmp;
- }
-
- bmp = createLetterAvatar(context, firstCharacter, generateAvatarColor(uri.getRawUriString()), pictureSize);
- mMemoryCache.put(key, bmp);
- return bmp;
+ return getGlideRequest(context, manager.asDrawable(), photo, profile, username, ringId)
+ .transition(DrawableTransitionOptions.withCrossFade(100));
}
- private static BitmapDrawable createDefaultAvatar(Context context, int backgroundColor, int pictureSize) {
- if (context == null || pictureSize <= 0) {
- throw new IllegalArgumentException();
- }
-
- Bitmap canvasBitmap = Bitmap.createBitmap(pictureSize, pictureSize, Bitmap.Config.ARGB_8888);
-
- Canvas canvas = new Canvas(canvasBitmap);
- canvas.drawColor(context.getResources().getColor(backgroundColor));
-
- Drawable drawable = AppCompatResources.getDrawable(context, R.drawable.ic_contact_picture_box_default);
- if (drawable == null) {
- Log.e(TAG, "Not able to get default drawable");
- } else {
- drawable.setBounds(0, 0, pictureSize, pictureSize);
- drawable.draw(canvas);
- }
-
- return new BitmapDrawable(context.getResources(), canvasBitmap);
+ public static RequestBuilder<Drawable> getGlideAvatar(Fragment fragment, CallContact contact) {
+ return getGlideAvatar(fragment.getActivity(), Glide.with(fragment), contact);
}
- private static BitmapDrawable createLetterAvatar(Context context, char firstCharacter, int backgroundColor, int pictureSize) {
- if (context == null || pictureSize <= 0) {
- throw new IllegalArgumentException();
- }
-
- Bitmap canvasBitmap = Bitmap.createBitmap(pictureSize, pictureSize, Bitmap.Config.ARGB_8888);
-
- Canvas canvas = new Canvas(canvasBitmap);
- canvas.drawColor(context.getResources().getColor(backgroundColor));
-
- AVATAR_TEXT_PAINT.setTextSize(pictureSize / 2);
- canvas.drawText(Character.toString(firstCharacter), pictureSize * 0.51f, pictureSize * 0.7f, AVATAR_TEXT_PAINT);
-
- return new BitmapDrawable(context.getResources(), canvasBitmap);
+ public static RequestBuilder<Drawable> getGlideAvatar(Context context, CallContact contact) {
+ return getGlideAvatar(context, Glide.with(context), contact);
}
- private static int generateAvatarColor(String ringId) {
- if (ringId == null) {
- return R.color.grey_500;
- }
-
- String md5 = HashUtils.md5(ringId);
- if (md5 == null) {
- return R.color.grey_500;
- }
- int colorIndex = Integer.parseInt(md5.charAt(0) + "", 16);
- Log.d(TAG, "generateAvatarColor: ringid=" + ringId + ", index=" + colorIndex + ", md5=" + md5);
- return contactColors[colorIndex % contactColors.length];
+ public static void loadGlideAvatar(ImageView view, CallContact contact) {
+ getGlideAvatar(view.getContext(), contact).into(view);
}
- private static Character getFirstCharacter(String name) {
- if (name == null) {
- return null;
- }
- String filteredName = name.replaceAll("\\W+", "");
- if (filteredName.isEmpty()) {
- return null;
- }
- return Character.toUpperCase(name.charAt(0));
+ public static RequestBuilder<Bitmap> getBitmapGlideAvatar(Context context, CallContact contact) {
+ return getGlideRequest(context, Glide.with(context).asBitmap(), contact.getPhoto(), contact.getProfileName(), contact.getUsername(), contact.getPrimaryNumber());
}
- public static RequestOptions getGlideOptions(boolean circle, boolean withPlaceholder) {
+ public static RequestOptions getGlideOptions(boolean circle) {
return circle ? GLIDE_OPTIONS_CIRCLE : GLIDE_OPTIONS;
}
public static void clearCache() {
- mMemoryCache.evictAll();
}
}
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java
index 52412bd..9ec6f9f 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java
@@ -53,7 +53,6 @@
import android.widget.RelativeLayout;
import android.widget.TextView;
-import com.bumptech.glide.Glide;
import com.rodolfonavalon.shaperipplelibrary.ShapeRipple;
import com.rodolfonavalon.shaperipplelibrary.model.Circle;
@@ -83,10 +82,7 @@
import cx.ring.utils.Log;
import cx.ring.utils.MediaButtonsHelper;
import cx.ring.views.CheckableImageButton;
-import io.reactivex.Single;
-import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
-import io.reactivex.schedulers.Schedulers;
public class CallFragment extends BaseFragment<CallPresenter> implements CallView, MediaButtonsHelper.MediaButtonsHelperCallback {
@@ -509,7 +505,9 @@
contactBubbleTxt.setText(username);
}
- mCompositeDisposable.add(Single.fromCallable(() -> Glide.with(getActivity())
+ AvatarFactory.loadGlideAvatar(contactBubbleView, contact);
+
+ /*mCompositeDisposable.add(Single.fromCallable(() -> Glide.with(getActivity())
.load(AvatarFactory.getAvatar(
getActivity(),
contact.getPhoto(),
@@ -520,7 +518,7 @@
.get())
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe(d -> contactBubbleView.setImageDrawable(d)));
+ .subscribe(d -> contactBubbleView.setImageDrawable(d)));*/
}
@Override
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java
index 34b3cd2..3fcd9d9 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/ConversationFragment.java
@@ -25,9 +25,12 @@
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.provider.MediaStore;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.FileProvider;
@@ -68,6 +71,7 @@
import cx.ring.client.CallActivity;
import cx.ring.client.ConversationActivity;
import cx.ring.client.HomeActivity;
+import cx.ring.contacts.AvatarFactory;
import cx.ring.conversation.ConversationPresenter;
import cx.ring.conversation.ConversationView;
import cx.ring.dependencyinjection.RingInjectionComponent;
@@ -86,6 +90,8 @@
import cx.ring.utils.ContentUriHandler;
import cx.ring.utils.MediaButtonsHelper;
import cx.ring.views.MessageEditText;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
import static android.app.Activity.RESULT_OK;
@@ -160,6 +166,7 @@
private SharedPreferences mPreferences;
private File mCurrentPhoto = null;
+ private Disposable actionbarTarget = null;
private Handler mHandler = new Handler();
private static final int REFRESH_INTERVAL = 10000;
@@ -455,11 +462,19 @@
}
@Override
+ public void onDetach() {
+ if (actionbarTarget != null) {
+ actionbarTarget.dispose();
+ actionbarTarget = null;
+ }
+ super.onDetach();
+ }
+
+ @Override
public void onDestroy() {
if (mDeleteConversation) {
mDeleteDialog.dismiss();
}
-
super.onDestroy();
}
@@ -536,17 +551,30 @@
}
@Override
- public void displayContactName(final CallContact contact) {
+ public void displayContact(final CallContact contact) {
ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
if (actionBar == null) {
return;
}
+ Context context = actionBar.getThemedContext();
String displayName = contact.getDisplayName();
actionBar.setTitle(displayName);
+
+ if (actionbarTarget != null) {
+ actionbarTarget.dispose();
+ actionbarTarget = null;
+ }
+ int targetSize = (int) (AvatarFactory.SIZE_AB * context.getResources().getDisplayMetrics().density);
+ actionbarTarget = AvatarFactory.getBitmapAvatar(context, contact, targetSize)
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(b -> actionBar.setLogo(new BitmapDrawable(context.getResources(), b)));
+
String identity = contact.getRingUsername();
if (identity != null && !identity.equals(displayName)) {
actionBar.setSubtitle(identity);
}
+
+ mAdapter.setPhoto();
}
@Override
@@ -555,11 +583,6 @@
}
@Override
- public void displayContactPhoto(final byte[] photo) {
- mAdapter.setPhoto(photo);
- }
-
- @Override
public void displayNumberSpinner(final Conversation conversation, final Uri number) {
mNumberSpinner.setVisibility(View.VISIBLE);
mNumberAdapter = new NumberAdapter(getActivity(),
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/SmartListFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/SmartListFragment.java
index 69f5a09..22140ad 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/SmartListFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/SmartListFragment.java
@@ -362,7 +362,8 @@
ImageView photo = mNewContact.findViewById(R.id.photo);
- Drawable contactPicture = AvatarFactory.getAvatar(
+ AvatarFactory.loadGlideAvatar(photo, contact);
+ /*Drawable contactPicture = AvatarFactory.getAvatar(
getActivity(),
contact.getPhoto(),
contact.getRingUsername(),
@@ -372,7 +373,7 @@
.load(contactPicture)
.apply(AvatarFactory.getGlideOptions(true, false))
//.transition(DrawableTransitionOptions.withCrossFade())
- .into(photo);
+ .into(photo);*/
mNewContact.setVisibility(View.VISIBLE);
}
diff --git a/ring-android/app/src/main/java/cx/ring/navigation/AccountAdapter.java b/ring-android/app/src/main/java/cx/ring/navigation/AccountAdapter.java
index de1d9ec..a52063d 100644
--- a/ring-android/app/src/main/java/cx/ring/navigation/AccountAdapter.java
+++ b/ring-android/app/src/main/java/cx/ring/navigation/AccountAdapter.java
@@ -42,6 +42,7 @@
import cx.ring.contacts.AvatarFactory;
import cx.ring.model.Account;
import cx.ring.utils.VCardUtils;
+import cx.ring.views.AvatarDrawable;
import ezvcard.VCard;
class AccountAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
@@ -183,17 +184,7 @@
public void update(final Account account) {
final Context context = itemView.getContext();
- VCard vcard = account.getProfile();
-
- Drawable accountPicture = AvatarFactory.getAvatar(context, vcard,
- account.getRegisteredName(),
- account.getUri());
-
- Glide.with(context)
- .load(accountPicture)
- .apply(AvatarFactory.getGlideOptions(true, true))
- //.transition(DrawableTransitionOptions.withCrossFade())
- .into(photo);
+ photo.setImageDrawable(new AvatarDrawable(context, account));
alias.setText(mRingNavigationPresenter.getAccountAlias(account));
host.setText(mRingNavigationPresenter.getUri(account, context.getText(R.string.account_type_ip2ip)));
diff --git a/ring-android/app/src/main/java/cx/ring/navigation/RingNavigationFragment.java b/ring-android/app/src/main/java/cx/ring/navigation/RingNavigationFragment.java
index 55879e8..c7c5284 100644
--- a/ring-android/app/src/main/java/cx/ring/navigation/RingNavigationFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/navigation/RingNavigationFragment.java
@@ -60,6 +60,8 @@
import cx.ring.model.Account;
import cx.ring.mvp.BaseFragment;
import cx.ring.utils.BitmapUtils;
+import cx.ring.utils.VCardUtils;
+import cx.ring.views.AvatarDrawable;
import ezvcard.VCard;
import ezvcard.parameter.ImageType;
import ezvcard.property.Photo;
@@ -236,12 +238,11 @@
return;
}
- String username = account.getRegisteredName();
+ /*String username = account.getRegisteredName();
String ringId = account.getUri();
- Glide.with(getActivity())
- .load(AvatarFactory.getAvatar(getActivity(), vcard, username, ringId))
- .apply(AvatarFactory.getGlideOptions(true, true))
- .into(mUserImage);
+ AvatarFactory.getGlideAvatar(getActivity(), Glide.with(this), vcard, username, ringId)
+ .into(mUserImage);*/
+ mUserImage.setImageDrawable(new AvatarDrawable(getActivity(), VCardUtils.readData(vcard), account.getRegisteredName(), account.getUri()));
}
public void updatePhoto(Uri uriImage) {
@@ -293,7 +294,7 @@
final EditText editText = view.findViewById(R.id.user_name);
editText.setText(presenter.getAlias(mSelectedAccount));
mProfilePhoto = view.findViewById(R.id.profile_photo);
- mProfilePhoto.setImageDrawable(mUserImage.getDrawable());
+ mProfilePhoto.setImageDrawable(new AvatarDrawable(inflater.getContext(), mSelectedAccount));
ImageButton cameraView = view.findViewById(R.id.camera);
cameraView.setOnClickListener(v -> presenter.cameraClicked());
diff --git a/ring-android/app/src/main/java/cx/ring/services/NotificationServiceImpl.java b/ring-android/app/src/main/java/cx/ring/services/NotificationServiceImpl.java
index 6bef8c9..c48023c 100644
--- a/ring-android/app/src/main/java/cx/ring/services/NotificationServiceImpl.java
+++ b/ring-android/app/src/main/java/cx/ring/services/NotificationServiceImpl.java
@@ -53,6 +53,7 @@
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
+import java.util.concurrent.ExecutionException;
import javax.inject.Inject;
@@ -371,12 +372,12 @@
PendingIntent.FLAG_ONE_SHOT));
- List<Photo> photos = contact.vcard == null ? null : contact.vcard.getPhotos();
+ /*List<Photo> photos = contact.vcard == null ? null : contact.vcard.getPhotos();
byte[] data = null;
if (photos != null && !photos.isEmpty()) {
data = photos.get(0).getData();
- }
- setContactPicture(data, contact.getRingUsername(), contact.getPrimaryNumber(), messageNotificationBuilder);
+ }*/
+ setContactPicture(contact, messageNotificationBuilder);
} else {
messageNotificationBuilder = new NotificationCompat.Builder(mContext, NOTIF_CHANNEL_REQUEST);
boolean newRequest = false;
@@ -612,11 +613,16 @@
}
private void setContactPicture(CallContact contact, NotificationCompat.Builder messageNotificationBuilder) {
- setContactPicture(contact.getPhoto(), contact.getUsername(),
- contact.getPhones().get(0).getNumber().getHost(), messageNotificationBuilder);
+ int size = (int) (mContext.getResources().getDisplayMetrics().density * AvatarFactory.SIZE_NOTIF);
+ try {
+ messageNotificationBuilder.setLargeIcon(AvatarFactory.getBitmapGlideAvatar(mContext, contact)
+ .submit(size, size)
+ .get());
+ } catch (Exception e) {
+ }
}
- private void setContactPicture(byte[] photo, String username, String ringId, NotificationCompat.Builder messageNotificationBuilder) {
+ /*private void setContactPicture(byte[] photo, String username, String ringId, NotificationCompat.Builder messageNotificationBuilder) {
Drawable contactPicture = AvatarFactory.getAvatar(mContext, photo, username, ringId);
Bitmap contactBitmap = BitmapUtils.drawableToBitmap(contactPicture);
@@ -630,5 +636,5 @@
return;
}
messageNotificationBuilder.setLargeIcon(circleBitmap);
- }
+ }*/
}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/services/RingChooserTargetService.java b/ring-android/app/src/main/java/cx/ring/services/RingChooserTargetService.java
index 4025198..9c73b42 100644
--- a/ring-android/app/src/main/java/cx/ring/services/RingChooserTargetService.java
+++ b/ring-android/app/src/main/java/cx/ring/services/RingChooserTargetService.java
@@ -20,6 +20,7 @@
import android.content.ComponentName;
import android.content.IntentFilter;
+import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -28,8 +29,7 @@
import android.service.chooser.ChooserTarget;
import android.service.chooser.ChooserTargetService;
import android.support.annotation.RequiresApi;
-
-import com.bumptech.glide.Glide;
+import android.util.Log;
import java.util.ArrayList;
import java.util.List;
@@ -47,15 +47,19 @@
@RequiresApi(api = Build.VERSION_CODES.M)
public class RingChooserTargetService extends ChooserTargetService {
+
@Inject
@Singleton
ConversationFacade conversationFacade;
+ private int targetSize;
+
@Override
public void onCreate() {
super.onCreate();
RingApplication.getInstance().startDaemon();
RingApplication.getInstance().getRingInjectionComponent().inject(this);
+ targetSize = (int) (AvatarFactory.SIZE_NOTIF * getResources().getDisplayMetrics().density);
}
@Override
@@ -67,18 +71,10 @@
.getConversationsSubject()
.firstOrError()
.map(conversations -> {
- List<Future<Drawable>> futureIcons = new ArrayList<>(conversations.size());
+ List<Future<Bitmap>> futureIcons = new ArrayList<>(conversations.size());
for (Conversation conversation : conversations) {
CallContact contact = conversation.getContact();
- final BitmapDrawable contactPicture = AvatarFactory.getAvatar(
- this,
- contact.getPhoto(),
- contact.getDisplayName(),
- contact.getPrimaryNumber());
- futureIcons.add(Glide.with(this)
- .load(contactPicture)
- .apply(AvatarFactory.getGlideOptions(true, true))
- .submit());
+ futureIcons.add(AvatarFactory.getBitmapAvatar(this, contact, targetSize).toFuture());
}
int i=0;
List<ChooserTarget> choosers = new ArrayList<>(conversations.size());
@@ -87,8 +83,12 @@
Bundle bundle = new Bundle();
bundle.putString(ConversationFragment.KEY_ACCOUNT_ID, a.getAccountID());
bundle.putString(ConversationFragment.KEY_CONTACT_RING_ID, contact.getPrimaryNumber());
- BitmapDrawable d = (BitmapDrawable) futureIcons.get(i).get();
- Icon icon = Icon.createWithBitmap(d.getBitmap());
+ Icon icon = null;
+ try {
+ icon = Icon.createWithBitmap(futureIcons.get(i).get());
+ } catch (Exception e) {
+ Log.w("RingChooserService", "Failed to load icon", e);
+ }
ChooserTarget target = new ChooserTarget(contact.getDisplayName(), icon, 1.f-(i/(float)conversations.size()), componentName, bundle);
choosers.add(target);
i++;
diff --git a/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileCreationFragment.java b/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileCreationFragment.java
index 68254b1..03902a5 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileCreationFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileCreationFragment.java
@@ -214,7 +214,7 @@
Glide.with(getActivity())
.load(model.getPhoto())
- .apply(AvatarFactory.getGlideOptions(true, false))
+ .apply(AvatarFactory.getGlideOptions(true))
.transition(DrawableTransitionOptions.withCrossFade())
.into(getGuidanceStylist().getIconView());
}
diff --git a/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileEditingFragment.java b/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileEditingFragment.java
index 241a3ea..080a635 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileEditingFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/account/TVProfileEditingFragment.java
@@ -52,6 +52,8 @@
import cx.ring.navigation.RingNavigationView;
import cx.ring.navigation.RingNavigationViewModel;
import cx.ring.tv.camera.CustomCameraActivity;
+import cx.ring.utils.VCardUtils;
+import cx.ring.views.AvatarDrawable;
import ezvcard.VCard;
import ezvcard.parameter.ImageType;
import ezvcard.property.Photo;
@@ -65,6 +67,7 @@
private static final int CAMERA = 3;
private List<GuidedAction> actions;
+ private int iconSize = -1;
public static GuidedStepSupportFragment newInstance() {
return new TVProfileEditingFragment();
@@ -113,6 +116,7 @@
public void onViewCreated(View view, Bundle savedInstanceState) {
((RingApplication) getActivity().getApplication()).getRingInjectionComponent().inject(this);
super.onViewCreated(view, savedInstanceState);
+ iconSize = (int) getResources().getDimension(R.dimen.tv_avatar_size);
}
@Override
@@ -188,22 +192,9 @@
else
getGuidanceStylist().getTitleView().setText(alias);
- if (vcard == null || vcard.getPhotos().isEmpty()) {
- getGuidanceStylist().getIconView().setImageDrawable(getActivity().getResources().getDrawable(R.drawable.ic_contact_picture_fallback));
- return;
- }
-
- Drawable contactPicture = AvatarFactory.getAvatar(
- getActivity(),
- vcard.getPhotos().get(0).getData(),
- account.getDisplayUsername(),
- account.getUri());
-
- Glide.with(getActivity())
- .load(contactPicture)
- .apply(AvatarFactory.getGlideOptions(true, false))
- .transition(DrawableTransitionOptions.withCrossFade())
- .into(getGuidanceStylist().getIconView());
+ AvatarDrawable avatar = new AvatarDrawable(getContext(), VCardUtils.readData(vcard), account.getUsername(), account == null ? null : account.getUsername(), true);
+ avatar.setInSize(iconSize);
+ getGuidanceStylist().getIconView().setImageDrawable(avatar);
}
@Override
diff --git a/ring-android/app/src/main/java/cx/ring/tv/account/TVRingAccountCreationFragment.java b/ring-android/app/src/main/java/cx/ring/tv/account/TVRingAccountCreationFragment.java
index d5f9f61..ec5d59f 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/account/TVRingAccountCreationFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/account/TVRingAccountCreationFragment.java
@@ -102,7 +102,7 @@
Glide.with(getActivity())
.load(ringAccountViewModel.getPhoto())
- .apply(AvatarFactory.getGlideOptions(true, false))
+ .apply(AvatarFactory.getGlideOptions(true))
.transition(DrawableTransitionOptions.withCrossFade())
.into(getGuidanceStylist().getIconView());
}
diff --git a/ring-android/app/src/main/java/cx/ring/tv/call/TVCallFragment.java b/ring-android/app/src/main/java/cx/ring/tv/call/TVCallFragment.java
index 604cc15..2a12969 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/call/TVCallFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/call/TVCallFragment.java
@@ -321,7 +321,9 @@
contactBubbleTxt.setText(username);
}
- mCompositeDisposable.add(Single.fromCallable(() -> Glide.with(getActivity())
+ AvatarFactory.loadGlideAvatar(contactBubbleView, contact);
+
+ /*mCompositeDisposable.add(Single.fromCallable(() -> Glide.with(getActivity())
.load(AvatarFactory.getAvatar(
getActivity(),
contact.getPhoto(),
@@ -332,7 +334,7 @@
.get())
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe(d -> contactBubbleView.setImageDrawable(d)));
+ .subscribe(d -> contactBubbleView.setImageDrawable(d)));*/
}
@Override
diff --git a/ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCardPresenter.java b/ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCardPresenter.java
index d5723b2..fffcd08 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCardPresenter.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCardPresenter.java
@@ -21,8 +21,15 @@
import android.content.Context;
import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.v17.leanback.widget.ImageCardView;
import android.view.ContextThemeWrapper;
+import android.widget.ImageView;
+
+import com.bumptech.glide.request.target.ImageViewTarget;
+import com.bumptech.glide.request.target.ViewTarget;
+import com.bumptech.glide.request.transition.Transition;
import cx.ring.R;
import cx.ring.contacts.AvatarFactory;
@@ -63,18 +70,11 @@
}
cardView.setBackgroundColor(cardView.getResources().getColor(R.color.color_primary_dark));
- cardView.setMainImage(getCardImage(contact));
- }
-
- private Drawable getCardImage(ContactCard contact) {
- String username = contact.getModel().getContact().getDisplayName();
- if (username == null || username.isEmpty()) {
- username = contact.getModel().getContact().getUsername();
- }
-
- return AvatarFactory.getAvatar(getContext(),
- contact.getPhoto(),
- username,
- contact.getModel().getContact().getIds().get(0));
+ AvatarFactory.getGlideAvatar(getContext(), model).into(new ViewTarget<ImageCardView, Drawable>(cardView){
+ @Override
+ public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
+ cardView.setMainImage(resource);
+ }
+ }.waitForLayout());
}
}
diff --git a/ring-android/app/src/main/java/cx/ring/tv/contactrequest/TVContactRequestFragment.java b/ring-android/app/src/main/java/cx/ring/tv/contactrequest/TVContactRequestFragment.java
index 81f4553..f32b493 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/contactrequest/TVContactRequestFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/contactrequest/TVContactRequestFragment.java
@@ -133,15 +133,7 @@
public void showRequest(TVListViewModel model) {
final DetailsOverviewRow row = new DetailsOverviewRow(model);
- Drawable contactPicture = AvatarFactory.getAvatar(getActivity(),
- model.getContact().getPhoto(),
- model.getContact().getDisplayName(),
- model.getContact().getPrimaryNumber());
-
- Glide.with(this)
- .load(contactPicture)
- .apply(AvatarFactory.getGlideOptions(false, true))
- .into(new DetailsOverviewRowTarget(row, contactPicture));
+ AvatarFactory.getGlideAvatar(this, model.getContact()).into(new DetailsOverviewRowTarget(row));
SparseArrayObjectAdapter adapter = new SparseArrayObjectAdapter();
adapter.set(ACTION_ACCEPT, new Action(ACTION_ACCEPT, getResources()
diff --git a/ring-android/app/src/main/java/cx/ring/tv/main/MainFragment.java b/ring-android/app/src/main/java/cx/ring/tv/main/MainFragment.java
index 6ecda1d..88f65df 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/main/MainFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/main/MainFragment.java
@@ -37,6 +37,8 @@
import android.view.View;
import android.view.ViewGroup;
+import com.bumptech.glide.Glide;
+
import java.util.ArrayList;
import java.util.List;
@@ -244,42 +246,17 @@
}
@Override
- public void displayAccountInfos(final String address, final RingNavigationViewModel viewModel) {
+ public void displayAccountInfos(final RingNavigationViewModel viewModel) {
if (getActivity() == null) {
Log.e(TAG, "displayAccountInfos: Not able to get activity");
return;
}
VCard vcard = viewModel.getVcard();
- if (vcard == null) {
- Log.e(TAG, "displayAccountInfos: Not able to get vcard");
- return;
- }
-
- FormattedName fn = vcard.getFormattedName();
- String formattedName = fn == null ? null : fn.getValue();
- if (formattedName != null && !formattedName.isEmpty()) {
- titleView.setAlias(formattedName);
- if (address != null) {
- setTitle(address);
- } else {
- setTitle("");
- }
- } else {
- titleView.setAlias(address);
- }
-
- byte[] data = null;
- List<Photo> photos = vcard.getPhotos();
- if (!photos.isEmpty() && photos.get(0) != null) {
- data = photos.get(0).getData();
- }
- Drawable contactPicture = AvatarFactory.getAvatar(getActivity(),
- data,
- viewModel.getAccount().getDisplayUsername(),
- address);
-
- titleView.setCurrentAccountPhoto(contactPicture);
+ String registeredName = viewModel.getAccount().getRegisteredName();
+ String uri = viewModel.getAccount().getUri();
+ titleView.getLogoView().setVisibility(View.VISIBLE);
+ AvatarFactory.getGlideAvatar(getActivity(), Glide.with(this), vcard, registeredName, uri).into(titleView.getLogoView());
}
@Override
diff --git a/ring-android/app/src/main/java/cx/ring/tv/main/MainPresenter.java b/ring-android/app/src/main/java/cx/ring/tv/main/MainPresenter.java
index a269fb3..ecf2646 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/main/MainPresenter.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/main/MainPresenter.java
@@ -189,7 +189,7 @@
.subscribe(accounts -> {
Account account = accounts.isEmpty() ? null : accounts.get(0);
RingNavigationViewModel viewModel = new RingNavigationViewModel(account, accounts);
- getView().displayAccountInfos(account == null ? null : account.getDisplayUri(), viewModel);
+ getView().displayAccountInfos(viewModel);
}));
}
diff --git a/ring-android/app/src/main/java/cx/ring/tv/main/MainView.java b/ring-android/app/src/main/java/cx/ring/tv/main/MainView.java
index fcad417..81fcf39 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/main/MainView.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/main/MainView.java
@@ -38,7 +38,7 @@
void displayErrorToast(int error);
- void displayAccountInfos(String address, RingNavigationViewModel viewModel);
+ void displayAccountInfos(RingNavigationViewModel viewModel);
void showExportDialog(String pAccountID);
diff --git a/ring-android/app/src/main/java/cx/ring/tv/views/CustomTitleView.java b/ring-android/app/src/main/java/cx/ring/tv/views/CustomTitleView.java
index 0826ac5..66f898b 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/views/CustomTitleView.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/views/CustomTitleView.java
@@ -110,13 +110,8 @@
mLogoView.setVisibility(View.VISIBLE);
}
- public void setCurrentAccountPhoto(Drawable photo) {
- Glide.with(getContext())
- .load(photo)
- .apply(AvatarFactory.getGlideOptions(true, true))
- .into(mLogoView);
-
- mLogoView.setVisibility(View.VISIBLE);
+ public ImageView getLogoView() {
+ return mLogoView;
}
@Override
diff --git a/ring-android/app/src/main/java/cx/ring/tv/views/DetailsOverviewRowTarget.java b/ring-android/app/src/main/java/cx/ring/tv/views/DetailsOverviewRowTarget.java
index b3a9456..63a4758 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/views/DetailsOverviewRowTarget.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/views/DetailsOverviewRowTarget.java
@@ -27,36 +27,25 @@
import android.util.Log;
import com.bumptech.glide.request.target.BaseTarget;
+import com.bumptech.glide.request.target.SimpleTarget;
import com.bumptech.glide.request.target.SizeReadyCallback;
import com.bumptech.glide.request.transition.Transition;
-public class DetailsOverviewRowTarget extends BaseTarget<Drawable> {
+public class DetailsOverviewRowTarget extends SimpleTarget<Drawable> {
private static final String TAG = DetailsOverviewRowTarget.class.getSimpleName();
private DetailsOverviewRow detailsOverviewRow;
- private Drawable drawable;
- public DetailsOverviewRowTarget(DetailsOverviewRow detailsOverviewRow, Drawable drawable) {
- if (detailsOverviewRow == null || drawable == null) {
+ public DetailsOverviewRowTarget(DetailsOverviewRow detailsOverviewRow) {
+ if (detailsOverviewRow == null) {
Log.d(TAG, "DetailsOverviewRowTarget: invalid parameter");
return;
}
this.detailsOverviewRow = detailsOverviewRow;
- this.drawable = drawable;
}
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
this.detailsOverviewRow.setImageDrawable(resource);
}
-
- @Override
- public void getSize(@NonNull SizeReadyCallback cb) {
- cb.onSizeReady(drawable.getMinimumWidth(), drawable.getMinimumHeight());
- }
-
- @Override
- public void removeCallback(@NonNull SizeReadyCallback cb) {
- // Do nothing, we never retain a reference to the callback
- }
}
diff --git a/ring-android/app/src/main/java/cx/ring/views/AvatarDrawable.java b/ring-android/app/src/main/java/cx/ring/views/AvatarDrawable.java
new file mode 100644
index 0000000..09b4c9f
--- /dev/null
+++ b/ring-android/app/src/main/java/cx/ring/views/AvatarDrawable.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2004-2018 Savoir-faire Linux Inc.
+ *
+ * Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package cx.ring.views;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import cx.ring.R;
+import cx.ring.model.Account;
+import cx.ring.model.CallContact;
+import cx.ring.utils.HashUtils;
+import cx.ring.utils.Tuple;
+import cx.ring.utils.VCardUtils;
+
+import android.media.ThumbnailUtils;
+import android.support.annotation.NonNull;
+import android.support.graphics.drawable.VectorDrawableCompat;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.TypedValue;
+
+public class AvatarDrawable extends Drawable {
+ private static final int SIZE_AB = 36;
+ private static final float DEFAULT_TEXT_SIZE_PERCENTAGE = 0.5f;
+ private static final int PLACEHOLDER_ICON = R.drawable.baseline_account_circle_24;
+
+ private static final int[] contactColors = {
+ R.color.red_500, R.color.pink_500,
+ R.color.purple_500, R.color.deep_purple_500,
+ R.color.indigo_500, R.color.blue_500,
+ R.color.cyan_500, R.color.teal_500,
+ R.color.green_500, R.color.light_green_500,
+ R.color.grey_500, R.color.lime_500,
+ R.color.amber_500, R.color.deep_orange_500,
+ R.color.brown_500, R.color.blue_grey_500
+ };
+
+ private final boolean cropCircle;
+ private boolean update = true;
+ private int inSize = -1;
+
+ private final int minSize;
+ private Bitmap workspace;
+ private final Bitmap bitmap;
+ private VectorDrawableCompat placeholder;
+ private final RectF backgroundBounds = new RectF();
+ private final String avatarText;
+ private float textStartXPoint;
+ private float textStartYPoint;
+ private int color;
+
+ private final Paint clipPaint = new Paint();
+ private final Paint textPaint = new Paint();
+ private static final Paint drawPaint = new Paint();
+ static {
+ drawPaint.setAntiAlias(true);
+ drawPaint.setFilterBitmap(true);
+ }
+
+ public AvatarDrawable(Context context, CallContact contact) {
+ this(context, contact.getPhoto(), contact.getProfileName(), contact.getUsername(), contact.getPrimaryNumber(), true);
+ }
+ public AvatarDrawable(Context context, CallContact contact, boolean crop) {
+ this(context, contact.getPhoto(), contact.getProfileName(), contact.getUsername(), contact.getPrimaryNumber(), crop);
+ }
+ public AvatarDrawable(Context context, Account account, boolean crop) {
+ this(context, VCardUtils.readData(account.getProfile()), account.getRegisteredName(), account.getUri(), crop);
+ }
+ public AvatarDrawable(Context context, Account account) {
+ this(context, VCardUtils.readData(account.getProfile()), account.getRegisteredName(), account.getUri(), true);
+ }
+ public AvatarDrawable(Context context, byte[] photo, String profileName, String username, String id, boolean crop) {
+ this(context, photo, TextUtils.isEmpty(profileName) ? username : profileName, id, crop);
+ }
+ public AvatarDrawable(Context context, Bitmap photo, String profileName, String username, String id, boolean crop) {
+ this(context, photo, TextUtils.isEmpty(profileName) ? username : profileName, id, crop);
+ }
+ public AvatarDrawable(Context context, Tuple<String, byte[]> data, String registeredName, String uri, boolean crop) {
+ this(context, data.second, data.first, registeredName, uri, crop);
+ }
+ public AvatarDrawable(Context context, Tuple<String, byte[]> data, String registeredName, String uri) {
+ this(context, data.second, data.first, registeredName, uri, true);
+ }
+ public AvatarDrawable(Context context, byte[] photo, String name, String id, boolean crop) {
+ this(context, photo == null ? null : getBitmap(photo), name, id, crop);
+ }
+
+ private static Bitmap getBitmap(byte[] photo) {
+ Bitmap source = BitmapFactory.decodeByteArray(photo, 0, photo.length);
+ int d = Math.min(source.getWidth(), source.getHeight());
+ return ThumbnailUtils.extractThumbnail(source, d, d);
+ }
+
+ public AvatarDrawable(Context context, Bitmap photo, String name, String id, boolean crop) {
+ Log.w("AvatarDrawable", photo + " " + name + " " + id);
+ cropCircle = crop;
+ Resources res = context.getResources();
+ minSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, SIZE_AB, res.getDisplayMetrics());
+ clipPaint.setAntiAlias(true);
+ if (photo != null) {
+ avatarText = null;
+ bitmap = photo;
+ } else {
+ bitmap = null;
+ avatarText = convertNameToAvatarText(name);
+ color = res.getColor(generateAvatarColor(id));
+ if (avatarText == null) {
+ placeholder = VectorDrawableCompat.create(context.getResources(), PLACEHOLDER_ICON, context.getTheme());
+ } else {
+ textPaint.setColor(Color.WHITE);
+ textPaint.setTypeface(Typeface.SANS_SERIF);
+ }
+ }
+ textPaint.setAntiAlias(true);
+ }
+
+ @Override
+ public void draw(@NonNull Canvas finalCanvas) {
+ if (workspace == null)
+ return;
+ if (update) {
+ drawActual(new Canvas(workspace));
+ update = false;
+ }
+ if (cropCircle) {
+ int d = Math.min(getBounds().width(), getBounds().height());
+ int r = d / 2;
+ finalCanvas.drawCircle(getBounds().centerX(), getBounds().centerY(), r, clipPaint);
+ } else {
+ finalCanvas.drawBitmap(workspace, null, getBounds(), drawPaint);
+ }
+ }
+
+ private void drawActual(@NonNull Canvas canvas) {
+ if (bitmap != null) {
+ canvas.drawBitmap(bitmap, null, backgroundBounds, drawPaint);
+ } else if (placeholder == null) {
+ canvas.drawColor(color);
+ canvas.drawText(avatarText, textStartXPoint, textStartYPoint, textPaint);
+ } else {
+ canvas.drawColor(0xffffffff);
+ canvas.save();
+ canvas.scale(1.2f, 1.2f, getBounds().centerX(), getBounds().centerY());
+ placeholder.setTint(color);
+ placeholder.draw(canvas);
+ canvas.restore();
+ }
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ //Log.w("AvatarDrawable", this + "onBoundsChange " + bounds.width() + " " + bounds.height());
+ setAvatarTextValues();
+ int d = Math.min(bounds.width(), bounds.height());
+ if (placeholder != null) {
+ int cx = (bounds.width()-d)/2;
+ int cy = (bounds.height()-d)/2;
+ placeholder.setBounds(cx, cy, cx + d, cy + d);
+ }
+ if (cropCircle) {
+ if (bitmap != null) {
+ int r = d / 2;
+ int cx = bounds.centerX();
+ int cy = bounds.centerY();
+ backgroundBounds.set(cx - r, cy - r, cx + r, cy + r);
+ }
+ if (d > 0) {
+ workspace = Bitmap.createBitmap(d, d, Bitmap.Config.ARGB_8888);
+ clipPaint.setShader(new BitmapShader(workspace, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
+ } else {
+ clipPaint.setShader(null);
+ workspace.recycle();
+ workspace = null;
+ }
+ } else {
+ if (bitmap != null) {
+ int a = bitmap.getWidth() * getBounds().height();
+ int b = bitmap.getHeight() * getBounds().width();
+ int w;
+ int h;
+ if (a < b) {
+ w = Math.max(bitmap.getWidth(), getBounds().width());
+ h = (w * bitmap.getHeight())/bitmap.getWidth();
+ } else {
+ h = Math.max(bitmap.getHeight(), getBounds().height());
+ w = (h * bitmap.getWidth())/bitmap.getHeight();
+ }
+ backgroundBounds.set(0, 0, w, h);
+ } else {
+ backgroundBounds.set(getBounds());
+ }
+ workspace = Bitmap.createBitmap(getBounds().width(), getBounds().height(), Bitmap.Config.ARGB_8888);
+ }
+ update = true;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ if (placeholder != null) {
+ placeholder.setAlpha(alpha);
+ } else {
+ textPaint.setAlpha(alpha);
+ }
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ if (placeholder != null) {
+ placeholder.setColorFilter(colorFilter);
+ } else {
+ textPaint.setColorFilter(colorFilter);
+ }
+ }
+
+ @Override
+ public int getMinimumWidth() {
+ return minSize;
+ }
+
+ @Override
+ public int getMinimumHeight() {
+ return minSize;
+ }
+
+ public void setInSize(int s) {
+ inSize = s;
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return inSize;
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return inSize;
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ private void setAvatarTextValues() {
+ if (avatarText != null) {
+ textPaint.setTextSize(getBounds().height() * DEFAULT_TEXT_SIZE_PERCENTAGE);
+ textStartXPoint = calculateTextStartXPoint();
+ textStartYPoint = calculateTextStartYPoint();
+ }
+ }
+
+ private float calculateTextStartXPoint() {
+ float stringWidth = textPaint.measureText(avatarText);
+ return (getBounds().width() / 2f) - (stringWidth / 2f);
+ }
+
+ private float calculateTextStartYPoint() {
+ return (getBounds().height() / 2f) - ((textPaint.ascent() + textPaint.descent()) / 2f);
+ }
+
+ private String convertNameToAvatarText(String name) {
+ return TextUtils.isEmpty(name) ? null : name.substring(0, 1).toUpperCase();
+ }
+
+ private static int generateAvatarColor(String id) {
+ if (id == null) {
+ return R.color.grey_500;
+ }
+
+ String md5 = HashUtils.md5(id);
+ if (md5 == null) {
+ return R.color.grey_500;
+ }
+ int colorIndex = Integer.parseInt(md5.charAt(0) + "", 16);
+ return contactColors[colorIndex % contactColors.length];
+ }
+}
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/drawable/baseline_account_circle_24.xml b/ring-android/app/src/main/res/drawable/baseline_account_circle_24.xml
new file mode 100755
index 0000000..4a91951
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable/baseline_account_circle_24.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,5c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM12,19.2c-2.5,0 -4.71,-1.28 -6,-3.22 0.03,-1.99 4,-3.08 6,-3.08 1.99,0 5.97,1.09 6,3.08 -1.29,1.94 -3.5,3.22 -6,3.22z"/>
+</vector>
diff --git a/ring-android/app/src/main/res/layout/activity_conversation.xml b/ring-android/app/src/main/res/layout/activity_conversation.xml
index 8840c64..be163ed 100644
--- a/ring-android/app/src/main/res/layout/activity_conversation.xml
+++ b/ring-android/app/src/main/res/layout/activity_conversation.xml
@@ -18,15 +18,16 @@
android:minHeight="?attr/actionBarSize"
android:popupTheme="@style/Theme.AppCompat.Light.NoActionBar"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
- android:titleTextAppearance="@style/ToolbarTitle"
app:contentInsetStart="72dp"
app:elevation="4dp"
- app:titleTextAppearance="@style/ToolbarTitle" />
+ app:subtitleTextAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Subtitle"
+ app:titleMarginStart="24dp"
+ app:titleTextAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title" />
<FrameLayout
android:id="@+id/main_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_alignParentStart="true"
- android:layout_below="@+id/main_toolbar" />
+ android:layout_below="@id/main_toolbar"
+ android:layout_alignParentStart="true" />
</RelativeLayout>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/values/dimens.xml b/ring-android/app/src/main/res/values/dimens.xml
index 341f823..8b62b48 100644
--- a/ring-android/app/src/main/res/values/dimens.xml
+++ b/ring-android/app/src/main/res/values/dimens.xml
@@ -49,4 +49,5 @@
<dimen name="search_image_card_height">90dp</dimen>
<dimen name="share_preview_height">120dp</dimen>
+ <dimen name="tv_avatar_size">320dp</dimen>
</resources>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/values/styles.xml b/ring-android/app/src/main/res/values/styles.xml
index ce4af49..9a869a9 100644
--- a/ring-android/app/src/main/res/values/styles.xml
+++ b/ring-android/app/src/main/res/values/styles.xml
@@ -28,7 +28,6 @@
</style>
<style name="ToolbarTitle" parent="@style/TextAppearance.Widget.AppCompat.Toolbar.Title">
- <item name="android:textSize">20sp</item>
</style>
<style name="MenuHeader" parent="Theme.AppCompat.Light.NoActionBar">