Refactor - extracted common method to load avatars for notifications

Signed-off-by: Dariusz Olszewski <starypatyk@users.noreply.github.com>
This commit is contained in:
Dariusz Olszewski 2022-05-09 21:51:07 +02:00 committed by Andy Scherzinger
parent 3b7ca4a31a
commit d4bdd88588
No known key found for this signature in database
GPG key ID: 6CADC7E3523C308B
3 changed files with 43 additions and 72 deletions

View file

@ -37,14 +37,6 @@ import android.util.Base64;
import android.util.Log;
import com.bluelinelabs.logansquare.LoganSquare;
import com.facebook.common.executors.UiThreadImmediateExecutorService;
import com.facebook.common.references.CloseableReference;
import com.facebook.datasource.DataSource;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
import com.facebook.imagepipeline.image.CloseableImage;
import com.facebook.imagepipeline.postprocessors.RoundAsCirclePostprocessor;
import com.facebook.imagepipeline.request.ImageRequest;
import com.nextcloud.talk.R;
import com.nextcloud.talk.activities.CallActivity;
import com.nextcloud.talk.activities.MainActivity;
@ -61,7 +53,6 @@ import com.nextcloud.talk.models.json.push.DecryptedPushMessage;
import com.nextcloud.talk.models.json.push.NotificationUser;
import com.nextcloud.talk.receivers.DirectReplyReceiver;
import com.nextcloud.talk.utils.ApiUtils;
import com.nextcloud.talk.utils.DisplayUtils;
import com.nextcloud.talk.utils.DoNotDisturbUtils;
import com.nextcloud.talk.utils.NotificationUtils;
import com.nextcloud.talk.utils.PushUtils;
@ -93,7 +84,6 @@ import androidx.core.app.NotificationCompat.MessagingStyle;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.app.Person;
import androidx.core.app.RemoteInput;
import androidx.core.graphics.drawable.IconCompat;
import androidx.emoji.text.EmojiCompat;
import androidx.work.Data;
import androidx.work.Worker;
@ -395,9 +385,10 @@ public class NotificationWorker extends Worker {
final NotificationUser notificationUser = decryptedPushMessage.getNotificationUser();
final String userType = notificationUser.getType();
MessagingStyle style = activeStatusBarNotification != null ?
MessagingStyle.extractMessagingStyleFromNotification(activeStatusBarNotification.getNotification()) :
null;
MessagingStyle style = null;
if (activeStatusBarNotification != null) {
style = MessagingStyle.extractMessagingStyleFromNotification(activeStatusBarNotification.getNotification());
}
Person.Builder person =
new Person.Builder()
@ -413,31 +404,11 @@ public class NotificationWorker extends Worker {
String avatarUrl = "user".equals(userType) ?
ApiUtils.getUrlForAvatar(baseUrl, notificationUser.getId(), false) :
ApiUtils.getUrlForGuestAvatar(baseUrl, notificationUser.getName(), false);
ImageRequest imageRequest = DisplayUtils.getImageRequestForUrl(avatarUrl, null);
Fresco.getImagePipeline().fetchDecodedImage(imageRequest, context).subscribe(
new BaseBitmapDataSubscriber() {
@Override
protected void onNewResultImpl(Bitmap bitmap) {
if (bitmap != null) {
new RoundAsCirclePostprocessor(true).process(bitmap);
person.setIcon(IconCompat.createWithBitmap(bitmap));
notificationBuilder.setStyle(getStyle(person.build(), style));
sendNotificationWithId(notificationId, notificationBuilder.build());
}
}
@Override
protected void onFailureImpl(DataSource<CloseableReference<CloseableImage>> dataSource) {
notificationBuilder.setStyle(getStyle(person.build(), style));
sendNotificationWithId(notificationId, notificationBuilder.build());
}
},
UiThreadImmediateExecutorService.getInstance());
} else {
notificationBuilder.setStyle(getStyle(person.build(), style));
sendNotificationWithId(notificationId, notificationBuilder.build());
person.setIcon(NotificationUtils.INSTANCE.loadAvatarSync(avatarUrl));
}
notificationBuilder.setStyle(getStyle(person.build(), style));
sendNotificationWithId(notificationId, notificationBuilder.build());
}
private void addReplyAction(NotificationCompat.Builder notificationBuilder, int notificationId) {

View file

@ -25,28 +25,18 @@ import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.Person
import androidx.core.app.RemoteInput
import androidx.core.graphics.drawable.IconCompat
import autodagger.AutoInjector
import com.facebook.common.executors.UiThreadImmediateExecutorService
import com.facebook.common.references.CloseableReference
import com.facebook.datasource.DataSource
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber
import com.facebook.imagepipeline.image.CloseableImage
import com.facebook.imagepipeline.postprocessors.RoundAsCirclePostprocessor
import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.models.database.UserEntity
import com.nextcloud.talk.models.json.generic.GenericOverall
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.NotificationUtils
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_ID
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
@ -110,7 +100,7 @@ class DirectReplyReceiver : BroadcastReceiver() {
@RequiresApi(Build.VERSION_CODES.N)
override fun onNext(genericOverall: GenericOverall) {
loadAvatar(::confirmReplySent)
confirmReplySent()
}
override fun onError(e: Throwable) {
@ -124,27 +114,6 @@ class DirectReplyReceiver : BroadcastReceiver() {
})
}
private fun loadAvatar(callback: (avatarIcon: IconCompat) -> Unit) {
val avatarUrl = ApiUtils.getUrlForAvatar(currentUser.baseUrl, currentUser.userId, false)
val imageRequest = DisplayUtils.getImageRequestForUrl(avatarUrl, currentUser)
val dataSource = Fresco.getImagePipeline().fetchDecodedImage(imageRequest, null)
dataSource.subscribe(
object : BaseBitmapDataSubscriber() {
override fun onNewResultImpl(bitmap: Bitmap?) {
if (bitmap != null) {
RoundAsCirclePostprocessor(true).process(bitmap)
callback(IconCompat.createWithBitmap(bitmap))
}
}
override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage?>>) {
// unused atm
}
},
UiThreadImmediateExecutorService.getInstance()
)
}
@RequiresApi(Build.VERSION_CODES.N)
private fun findActiveNotification(notificationId: Int): Notification? {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@ -152,7 +121,7 @@ class DirectReplyReceiver : BroadcastReceiver() {
}
@RequiresApi(Build.VERSION_CODES.N)
private fun confirmReplySent(avatarIcon: IconCompat) {
private fun confirmReplySent() {
// Implementation inspired by the SO question and article below:
// https://stackoverflow.com/questions/51549456/android-o-notification-for-direct-reply-message
// https://medium.com/@sidorovroman3/android-how-to-use-messagingstyle-for-notifications-without-caching-messages-c414ef2b816c
@ -171,10 +140,10 @@ class DirectReplyReceiver : BroadcastReceiver() {
.extractMessagingStyleFromNotification(previousNotification)
// Add reply
val avatarUrl = ApiUtils.getUrlForAvatar(currentUser.baseUrl, currentUser.userId, false)
val me = Person.Builder()
.setName(currentUser.displayName)
// .setIcon(IconCompat.createWithResource(context, R.drawable.ic_user))
.setIcon(avatarIcon)
.setIcon(NotificationUtils.loadAvatarSync(avatarUrl))
.build()
val message = NotificationCompat.MessagingStyle.Message(replyMessage, System.currentTimeMillis(), me)
previousStyle?.addMessage(message)

View file

@ -32,7 +32,13 @@ import android.net.Uri
import android.os.Build
import android.service.notification.StatusBarNotification
import android.text.TextUtils
import androidx.core.graphics.drawable.IconCompat
import com.bluelinelabs.logansquare.LoganSquare
import com.facebook.common.references.CloseableReference
import com.facebook.datasource.DataSources
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.imagepipeline.image.CloseableBitmap
import com.facebook.imagepipeline.postprocessors.RoundAsCirclePostprocessor
import com.nextcloud.talk.BuildConfig
import com.nextcloud.talk.R
import com.nextcloud.talk.models.RingtoneSettings
@ -297,6 +303,31 @@ object NotificationUtils {
)
}
/*
* Load user avatar synchronously.
* Inspired by:
* https://frescolib.org/docs/using-image-pipeline.html
* https://github.com/facebook/fresco/issues/830
* https://localcoder.org/using-facebooks-fresco-to-load-a-bitmap
*/
fun loadAvatarSync(avatarUrl: String): IconCompat? {
// TODO - how to handle errors here?
var avatarIcon: IconCompat? = null
val imageRequest = DisplayUtils.getImageRequestForUrl(avatarUrl, null)
val dataSource = Fresco.getImagePipeline().fetchDecodedImage(imageRequest, null)
val closeableImageRef = DataSources.waitForFinalResult(dataSource) as CloseableReference<CloseableBitmap>?
val bitmap = closeableImageRef?.get()?.underlyingBitmap
if (bitmap != null) {
// According to Fresco documentation a copy of the bitmap should be made before closing the references.
// However, it seems to work without making a copy... ;-)
RoundAsCirclePostprocessor(true).process(bitmap)
avatarIcon = IconCompat.createWithBitmap(bitmap)
}
CloseableReference.closeSafely(closeableImageRef)
dataSource.close()
return avatarIcon
}
private data class Channel(
val id: String,
val name: String,