diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2019-12-12 02:08:07 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2019-12-12 02:08:07 +0000 |
commit | 9bb03ebce29e157ca3d69914281fae019f87d4a3 (patch) | |
tree | a5953b40cfc157bb6397db088247d44bce509f78 | |
parent | a8f419b5d60f89949997f3469dd97cc2ad380555 (diff) | |
parent | e1358b52a2df4d8606c3c3dd172c25169df5a15c (diff) | |
download | Messenger-9bb03ebce29e157ca3d69914281fae019f87d4a3.tar.gz |
Snap for 6066667 from e1358b52a2df4d8606c3c3dd172c25169df5a15c to qt-d4-release
Change-Id: I3233095a9dfb46850d463ed698773b39505c50cb
-rw-r--r-- | Android.mk | 4 | ||||
-rw-r--r-- | res/values/dimens.xml | 1 | ||||
-rw-r--r-- | src/com/android/car/messenger/MessengerDelegate.java | 160 | ||||
-rw-r--r-- | src/com/android/car/messenger/SmsDatabaseHandler.java | 25 |
4 files changed, 116 insertions, 74 deletions
@@ -40,10 +40,12 @@ LOCAL_USE_AAPT2 := true LOCAL_STATIC_ANDROID_LIBRARIES += \ car-apps-common \ + car-telephony-common \ # Including the resources for the static android libraries allows to pick up their static overlays. LOCAL_RESOURCE_DIR += \ - $(LOCAL_PATH)/../libs/car-apps-common/res + $(LOCAL_PATH)/../libs/car-apps-common/res \ + $(LOCAL_PATH)/../libs/car-telephony-common/res \ LOCAL_STATIC_JAVA_LIBRARIES += \ androidx.annotation_annotation \ diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 7308708..1f9b6af 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -16,4 +16,5 @@ --> <resources> <dimen name="notification_contact_photo_size">300dp</dimen> + <dimen name="contact_avatar_corner_radius_percent" format="float">0.5</dimen> </resources> diff --git a/src/com/android/car/messenger/MessengerDelegate.java b/src/com/android/car/messenger/MessengerDelegate.java index 33615fb..58193d4 100644 --- a/src/com/android/car/messenger/MessengerDelegate.java +++ b/src/com/android/car/messenger/MessengerDelegate.java @@ -7,20 +7,15 @@ import android.app.PendingIntent; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothMapClient; -import android.bluetooth.BluetoothProfile; -import android.content.ContentResolver; -import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.content.res.Resources.NotFoundException; -import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.net.Uri; -import android.provider.ContactsContract; -import android.text.TextUtils; +import android.util.Log; import android.widget.Toast; - import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.core.app.NotificationCompat; @@ -28,23 +23,24 @@ import androidx.core.app.NotificationCompat.Action; import androidx.core.app.NotificationCompat.MessagingStyle; import androidx.core.app.Person; import androidx.core.app.RemoteInput; - +import androidx.core.graphics.drawable.RoundedBitmapDrawable; +import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; import com.android.car.apps.common.LetterTileDrawable; import com.android.car.messenger.bluetooth.BluetoothHelper; import com.android.car.messenger.bluetooth.BluetoothMonitor; import com.android.car.messenger.log.L; +import com.android.car.telephony.common.TelecomUtils; import com.android.internal.annotations.GuardedBy; - import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.target.SimpleTarget; import com.bumptech.glide.request.transition.Transition; - import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.function.Predicate; /** Delegate class responsible for handling messaging service actions */ @@ -57,7 +53,10 @@ public class MessengerDelegate implements BluetoothMonitor.OnBluetoothEventListe private BluetoothMapClient mBluetoothMapClient; private final NotificationManager mNotificationManager; private final SmsDatabaseHandler mSmsDatabaseHandler; + private final int mBitmapSize; + private final float mCornerRadiusPercent; private boolean mShouldLoadExistingMessages; + private CompletableFuture<Void> mPhoneNumberInfoFuture; @VisibleForTesting final Map<MessageKey, MapMessage> mMessages = new HashMap<>(); @@ -67,6 +66,7 @@ public class MessengerDelegate implements BluetoothMonitor.OnBluetoothEventListe // Notifications for messages received before this time. @VisibleForTesting final Map<String, Long> mBTDeviceAddressToConnectionTimestamp = new HashMap<>(); + final Map<SenderKey, Bitmap> mSenderToLargeIconBitmap = new HashMap<>(); public MessengerDelegate(Context context) { mContext = context; @@ -75,6 +75,11 @@ public class MessengerDelegate implements BluetoothMonitor.OnBluetoothEventListe (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); mSmsDatabaseHandler = new SmsDatabaseHandler(mContext); + mBitmapSize = + mContext.getResources() + .getDimensionPixelSize(R.dimen.notification_contact_photo_size); + mCornerRadiusPercent = mContext.getResources() + .getFloat(R.dimen.contact_avatar_corner_radius_percent); try { mShouldLoadExistingMessages = mContext.getResources().getBoolean(R.bool.config_loadExistingMessages); @@ -216,6 +221,14 @@ public class MessengerDelegate implements BluetoothMonitor.OnBluetoothEventListe } } + protected void onDestroy() { + cleanupMessagesAndNotifications(key -> true); + + if (mPhoneNumberInfoFuture != null) { + mPhoneNumberInfoFuture.cancel(true); + } + } + /** * Clears all notifications matching the {@param predicate}. Example method calls are when user * wants to clear (a) message notification(s), or when the Bluetooth device that received the @@ -240,6 +253,7 @@ public class MessengerDelegate implements BluetoothMonitor.OnBluetoothEventListe messageKeyMapMessageEntry -> predicate.test(messageKeyMapMessageEntry.getKey())); clearNotifications(predicate); mNotificationInfos.entrySet().removeIf(entry -> predicate.test(entry.getKey())); + mSenderToLargeIconBitmap.entrySet().removeIf(entry -> predicate.test(entry.getKey())); } private void updateNotification(MessageKey messageKey, MapMessage mapMessage) { @@ -258,72 +272,75 @@ public class MessengerDelegate implements BluetoothMonitor.OnBluetoothEventListe NotificationInfo notificationInfo = mNotificationInfos.get(senderKey); notificationInfo.mMessageKeys.add(messageKey); - updateNotification(senderKey, notificationInfo); - } - - private void updateNotification(SenderKey senderKey, NotificationInfo notificationInfo) { - final Uri photoUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, - getContactId(mContext.getContentResolver(), notificationInfo.mSenderContactUri)); - - Glide.with(mContext) - .asBitmap() - .load(photoUri) - .apply(RequestOptions.circleCropTransform()) - .into(new SimpleTarget<Bitmap>() { - @Override - public void onResourceReady(Bitmap bitmap, - Transition<? super Bitmap> transition) { - sendNotification(bitmap); - } - - @Override - public void onLoadFailed(@Nullable Drawable fallback) { - sendNotification(null); - } - - private void sendNotification(Bitmap bitmap) { - mNotificationManager.notify( - notificationInfo.mNotificationId, - createNotification(senderKey, notificationInfo, bitmap)); - } - }); + updateNotificationWithIcon(senderKey, notificationInfo); } - // TODO: move out to a shared library. - protected static int getContactId(ContentResolver cr, String contactUri) { - if (TextUtils.isEmpty(contactUri)) { - return 0; + private void updateNotificationWithIcon(SenderKey senderKey, + NotificationInfo notificationInfo) { + String phoneNumber = getPhoneNumber(notificationInfo.mSenderContactUri); + if (mSenderToLargeIconBitmap.get(senderKey) != null || phoneNumber == null) { + postNotification(senderKey, notificationInfo); } - Uri lookupUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, - Uri.encode(contactUri)); - String[] projection = new String[]{ContactsContract.PhoneLookup._ID}; - - try (Cursor cursor = cr.query(lookupUri, projection, null, null, null)) { - if (cursor != null && cursor.moveToFirst() && cursor.isLast()) { - return cursor.getInt(cursor.getColumnIndex(ContactsContract.PhoneLookup._ID)); - } else { - L.w(TAG, "Unable to find contact id from phone number."); - } + if (mPhoneNumberInfoFuture != null) { + mPhoneNumberInfoFuture.cancel(/* mayInterruptRunning= */ true); } - return 0; + LetterTileDrawable errorDrawable = TelecomUtils.createLetterTile(mContext, + notificationInfo.mSenderName, notificationInfo.mSenderName); + + mPhoneNumberInfoFuture = TelecomUtils.getPhoneNumberInfo(mContext, phoneNumber) + .thenAcceptAsync(phoneNumberInfo -> { + if (phoneNumberInfo == null) { + postNotification(senderKey, notificationInfo); + } + Glide.with(mContext) + .asBitmap() + .load(phoneNumberInfo.getAvatarUri()) + .apply(new RequestOptions().override(mBitmapSize).error(errorDrawable)) + .into(new SimpleTarget<Bitmap>() { + @Override + public void onResourceReady(Bitmap bitmap, + Transition<? super Bitmap> transition) { + RoundedBitmapDrawable roundedBitmapDrawable = + RoundedBitmapDrawableFactory + .create(mContext.getResources(), bitmap); + Icon avatarIcon = TelecomUtils + .createFromRoundedBitmapDrawable(roundedBitmapDrawable, mBitmapSize, + mCornerRadiusPercent); + mSenderToLargeIconBitmap.put(senderKey, avatarIcon.getBitmap()); + postNotification(senderKey, notificationInfo); + } + + @Override + public void onLoadFailed(@Nullable Drawable fallback) { + postNotification(senderKey, notificationInfo); + } + + }); + }, mContext.getMainExecutor()); } - protected void onDestroy() { - cleanupMessagesAndNotifications(key -> true); + private void postNotification(SenderKey senderKey, NotificationInfo notificationInfo) { + mNotificationManager.notify( + notificationInfo.mNotificationId, + createNotification(senderKey, notificationInfo)); } private Notification createNotification( - SenderKey senderKey, NotificationInfo notificationInfo, Bitmap bitmap) { + SenderKey senderKey, NotificationInfo notificationInfo) { String contentText = mContext.getResources().getQuantityString( R.plurals.notification_new_message, notificationInfo.mMessageKeys.size(), notificationInfo.mMessageKeys.size()); long lastReceiveTime = mMessages.get(notificationInfo.mMessageKeys.getLast()) .getReceiveTime(); - if (bitmap == null) { - bitmap = letterTileBitmap(notificationInfo.mSenderName); + Bitmap largeIcon = mSenderToLargeIconBitmap.get(senderKey); + if (largeIcon == null) { + largeIcon = + TelecomUtils.createLetterTile(mContext, + TelecomUtils.getInitials(notificationInfo.mSenderName, ""), + notificationInfo.mSenderName, mBitmapSize, mCornerRadiusPercent).getBitmap(); } final String senderName = notificationInfo.mSenderName; @@ -358,7 +375,7 @@ public class MessengerDelegate implements BluetoothMonitor.OnBluetoothEventListe .setContentText(contentText) .setStyle(messagingStyle) .setCategory(Notification.CATEGORY_MESSAGE) - .setLargeIcon(bitmap) + .setLargeIcon(largeIcon) .setSmallIcon(R.drawable.ic_message) .setWhen(lastReceiveTime) .setShowWhen(true) @@ -371,17 +388,6 @@ public class MessengerDelegate implements BluetoothMonitor.OnBluetoothEventListe return builder.build(); } - private Bitmap letterTileBitmap(String senderName) { - LetterTileDrawable letterTileDrawable = new LetterTileDrawable(mContext.getResources()); - letterTileDrawable.setContactDetails(senderName, senderName); - letterTileDrawable.setIsCircular(true); - - int bitmapSize = mContext.getResources() - .getDimensionPixelSize(R.dimen.notification_contact_photo_size); - - return letterTileDrawable.toBitmap(bitmapSize); - } - private PendingIntent createServiceIntent(SenderKey senderKey, int notificationId, String action) { Intent intent = new Intent(mContext, MessengerService.class) @@ -449,6 +455,18 @@ public class MessengerDelegate implements BluetoothMonitor.OnBluetoothEventListe } /** + * Extracts the phone number from the {@link BluetoothMapClient} formatted URI. + **/ + @Nullable + private String getPhoneNumber(String senderContactUri) { + if (senderContactUri == null || !senderContactUri.matches("tel:(.+)")) { + return null; + } + + return senderContactUri.substring(4); + } + + /** * Contains information about a single notification that is displayed, with grouped messages. */ @VisibleForTesting diff --git a/src/com/android/car/messenger/SmsDatabaseHandler.java b/src/com/android/car/messenger/SmsDatabaseHandler.java index 333d8d7..77d8e29 100644 --- a/src/com/android/car/messenger/SmsDatabaseHandler.java +++ b/src/com/android/car/messenger/SmsDatabaseHandler.java @@ -1,8 +1,6 @@ package com.android.car.messenger; -import static com.android.car.messenger.MessengerDelegate.getContactId; - import android.Manifest; import android.app.AppOpsManager; import android.content.ContentResolver; @@ -13,7 +11,9 @@ import android.database.Cursor; import android.database.DatabaseUtils; import android.net.Uri; import android.provider.BaseColumns; +import android.provider.ContactsContract; import android.provider.Telephony; +import android.text.TextUtils; import android.util.Log; import androidx.core.content.ContextCompat; @@ -194,4 +194,25 @@ class SmsDatabaseHandler { return granted; } + + // TODO: move out to a shared library. + private static int getContactId(ContentResolver cr, String contactUri) { + if (TextUtils.isEmpty(contactUri)) { + return 0; + } + + Uri lookupUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, + Uri.encode(contactUri)); + String[] projection = new String[]{ContactsContract.PhoneLookup._ID}; + + try (Cursor cursor = cr.query(lookupUri, projection, null, null, null)) { + if (cursor != null && cursor.moveToFirst() && cursor.isLast()) { + return cursor.getInt(cursor.getColumnIndex(ContactsContract.PhoneLookup._ID)); + } else { + L.w(TAG, "Unable to find contact id from phone number."); + } + } + + return 0; + } } |