Improve performance of notifications
This commit is contained in:
parent
4c18a6563c
commit
d10df30080
|
@ -23,6 +23,8 @@ import android.app.Service;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
|
@ -30,13 +32,17 @@ import android.support.v4.app.TaskStackBuilder;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.RemoteViews;
|
import android.widget.RemoteViews;
|
||||||
|
|
||||||
|
import com.squareup.picasso.Picasso;
|
||||||
|
import com.squareup.picasso.Target;
|
||||||
import com.syncedsynapse.kore2.R;
|
import com.syncedsynapse.kore2.R;
|
||||||
import com.syncedsynapse.kore2.host.HostConnectionObserver;
|
import com.syncedsynapse.kore2.host.HostConnectionObserver;
|
||||||
import com.syncedsynapse.kore2.host.HostManager;
|
import com.syncedsynapse.kore2.host.HostManager;
|
||||||
import com.syncedsynapse.kore2.jsonrpc.type.ListType;
|
import com.syncedsynapse.kore2.jsonrpc.type.ListType;
|
||||||
import com.syncedsynapse.kore2.jsonrpc.type.PlayerType;
|
import com.syncedsynapse.kore2.jsonrpc.type.PlayerType;
|
||||||
import com.syncedsynapse.kore2.ui.RemoteActivity;
|
import com.syncedsynapse.kore2.ui.RemoteActivity;
|
||||||
|
import com.syncedsynapse.kore2.utils.CharacterDrawable;
|
||||||
import com.syncedsynapse.kore2.utils.LogUtils;
|
import com.syncedsynapse.kore2.utils.LogUtils;
|
||||||
|
import com.syncedsynapse.kore2.utils.UIUtils;
|
||||||
import com.syncedsynapse.kore2.utils.Utils;
|
import com.syncedsynapse.kore2.utils.Utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -162,11 +168,14 @@ public class NotificationService extends Service
|
||||||
stopSelf();
|
stopSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Picasso target that will be used to load images
|
||||||
|
private static Target picassoTarget = null;
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||||
private void buildNotification(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
private void buildNotification(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||||
PlayerType.PropertyValue getPropertiesResult,
|
PlayerType.PropertyValue getPropertiesResult,
|
||||||
ListType.ItemsAll getItemResult) {
|
ListType.ItemsAll getItemResult) {
|
||||||
String title, underTitle, poster;
|
final String title, underTitle, poster;
|
||||||
int smallIcon, playPauseIcon, rewindIcon, ffIcon;
|
int smallIcon, playPauseIcon, rewindIcon, ffIcon;
|
||||||
|
|
||||||
boolean isVideo = ((getItemResult.type.equals(ListType.ItemsAll.TYPE_MOVIE)) ||
|
boolean isVideo = ((getItemResult.type.equals(ListType.ItemsAll.TYPE_MOVIE)) ||
|
||||||
|
@ -229,7 +238,7 @@ public class NotificationService extends Service
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the collpased and expanded notifications
|
// Setup the collpased and expanded notifications
|
||||||
RemoteViews collapsedRV = new RemoteViews(this.getPackageName(), R.layout.notification_colapsed);
|
final RemoteViews collapsedRV = new RemoteViews(this.getPackageName(), R.layout.notification_colapsed);
|
||||||
collapsedRV.setImageViewResource(R.id.rewind, rewindIcon);
|
collapsedRV.setImageViewResource(R.id.rewind, rewindIcon);
|
||||||
collapsedRV.setOnClickPendingIntent(R.id.rewind, rewindPendingItent);
|
collapsedRV.setOnClickPendingIntent(R.id.rewind, rewindPendingItent);
|
||||||
collapsedRV.setImageViewResource(R.id.play, playPauseIcon);
|
collapsedRV.setImageViewResource(R.id.play, playPauseIcon);
|
||||||
|
@ -239,7 +248,7 @@ public class NotificationService extends Service
|
||||||
collapsedRV.setTextViewText(R.id.title, title);
|
collapsedRV.setTextViewText(R.id.title, title);
|
||||||
collapsedRV.setTextViewText(R.id.text2, underTitle);
|
collapsedRV.setTextViewText(R.id.text2, underTitle);
|
||||||
|
|
||||||
RemoteViews expandedRV = new RemoteViews(this.getPackageName(), R.layout.notification_expanded);
|
final RemoteViews expandedRV = new RemoteViews(this.getPackageName(), R.layout.notification_expanded);
|
||||||
expandedRV.setImageViewResource(R.id.rewind, rewindIcon);
|
expandedRV.setImageViewResource(R.id.rewind, rewindIcon);
|
||||||
expandedRV.setOnClickPendingIntent(R.id.rewind, rewindPendingItent);
|
expandedRV.setOnClickPendingIntent(R.id.rewind, rewindPendingItent);
|
||||||
expandedRV.setImageViewResource(R.id.play, playPauseIcon);
|
expandedRV.setImageViewResource(R.id.play, playPauseIcon);
|
||||||
|
@ -248,7 +257,7 @@ public class NotificationService extends Service
|
||||||
expandedRV.setOnClickPendingIntent(R.id.fast_forward, ffPendingItent);
|
expandedRV.setOnClickPendingIntent(R.id.fast_forward, ffPendingItent);
|
||||||
expandedRV.setTextViewText(R.id.title, title);
|
expandedRV.setTextViewText(R.id.title, title);
|
||||||
expandedRV.setTextViewText(R.id.text2, underTitle);
|
expandedRV.setTextViewText(R.id.text2, underTitle);
|
||||||
int expandedIconResId;
|
final int expandedIconResId;
|
||||||
if (isVideo) {
|
if (isVideo) {
|
||||||
expandedIconResId = R.id.icon_slim;
|
expandedIconResId = R.id.icon_slim;
|
||||||
expandedRV.setViewVisibility(R.id.icon_slim, View.VISIBLE);
|
expandedRV.setViewVisibility(R.id.icon_slim, View.VISIBLE);
|
||||||
|
@ -261,7 +270,7 @@ public class NotificationService extends Service
|
||||||
|
|
||||||
// Build the notification
|
// Build the notification
|
||||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
|
||||||
Notification notification = builder
|
final Notification notification = builder
|
||||||
.setSmallIcon(smallIcon)
|
.setSmallIcon(smallIcon)
|
||||||
.setShowWhen(false)
|
.setShowWhen(false)
|
||||||
.setOngoing(true)
|
.setOngoing(true)
|
||||||
|
@ -271,30 +280,66 @@ public class NotificationService extends Service
|
||||||
.setContent(collapsedRV)
|
.setContent(collapsedRV)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Load images. Use the same dimensions as the remote to hit the cache both times
|
// This is a convoluted way of loading the image and showing the
|
||||||
|
// notification, but it's what works with Picasso and is efficient.
|
||||||
|
// Here's what's going on:
|
||||||
|
//
|
||||||
|
// 1. The image is loaded asynchronously into a Target, and only after
|
||||||
|
// it is loaded is the notification shown. Using targets is a lot more
|
||||||
|
// efficient than letting Picasso load it directly into the
|
||||||
|
// notification imageview, which causes a lot of flickering
|
||||||
|
//
|
||||||
|
// 2. The target needs to be static, because Picasso only keeps a weak
|
||||||
|
// reference to it, so we need to keed a strong reference and reset it
|
||||||
|
// to null when we're done. We also need to check if it is not null in
|
||||||
|
// case a previous request hasn't finished yet.
|
||||||
|
//
|
||||||
|
// 3. We can only show the notification after the bitmap is loaded into
|
||||||
|
// the target, so it is done in the callback
|
||||||
|
//
|
||||||
|
// 4. We specifically resize the image to the same dimensions used in
|
||||||
|
// the remote, so that Picasso reuses it in the remote and here from the cache
|
||||||
Resources resources = this.getResources();
|
Resources resources = this.getResources();
|
||||||
int posterWidth = resources.getDimensionPixelOffset(R.dimen.now_playing_poster_width);
|
final int posterWidth = resources.getDimensionPixelOffset(R.dimen.now_playing_poster_width);
|
||||||
int posterHeight = isVideo?
|
final int posterHeight = isVideo?
|
||||||
resources.getDimensionPixelOffset(R.dimen.now_playing_poster_height):
|
resources.getDimensionPixelOffset(R.dimen.now_playing_poster_height):
|
||||||
posterWidth;
|
posterWidth;
|
||||||
|
if (picassoTarget == null ) {
|
||||||
|
picassoTarget = new Target() {
|
||||||
|
@Override
|
||||||
|
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
|
||||||
|
showNotification(bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
HostManager hostManager = HostManager.getInstance(this);
|
@Override
|
||||||
hostManager.getPicasso()
|
public void onBitmapFailed(Drawable errorDrawable) {
|
||||||
.load(hostManager.getHostInfo().getImageUrl(poster))
|
CharacterDrawable avatarDrawable = UIUtils.getCharacterAvatar(NotificationService.this, title);
|
||||||
.resize(posterWidth, posterHeight)
|
showNotification(Utils.drawableToBitmap(avatarDrawable, posterWidth, posterHeight));
|
||||||
.into(collapsedRV, R.id.icon, NOTIFICATION_ID, notification);
|
}
|
||||||
|
|
||||||
if (Utils.isJellybeanOrLater()) {
|
@Override
|
||||||
notification.bigContentView = expandedRV;
|
public void onPrepareLoad(Drawable placeHolderDrawable) { }
|
||||||
|
|
||||||
|
private void showNotification(Bitmap bitmap) {
|
||||||
|
collapsedRV.setImageViewBitmap(R.id.icon, bitmap);
|
||||||
|
if (Utils.isJellybeanOrLater()) {
|
||||||
|
notification.bigContentView = expandedRV;
|
||||||
|
expandedRV.setImageViewBitmap(expandedIconResId, bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
notificationManager.notify(NOTIFICATION_ID, notification);
|
||||||
|
picassoTarget = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load the image
|
||||||
|
HostManager hostManager = HostManager.getInstance(this);
|
||||||
hostManager.getPicasso()
|
hostManager.getPicasso()
|
||||||
.load(hostManager.getHostInfo().getImageUrl(poster))
|
.load(hostManager.getHostInfo().getImageUrl(poster))
|
||||||
.resize(posterWidth, posterHeight)
|
.resize(posterWidth, posterHeight)
|
||||||
.into(expandedRV, expandedIconResId, NOTIFICATION_ID, notification);
|
.into(picassoTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
|
|
||||||
notificationManager.notify(NOTIFICATION_ID, notification);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private PendingIntent buildActionPendingIntent(int playerId, String action) {
|
private PendingIntent buildActionPendingIntent(int playerId, String action) {
|
||||||
|
|
|
@ -129,22 +129,8 @@ public class UIUtils {
|
||||||
String imageUrl, String stringAvatar,
|
String imageUrl, String stringAvatar,
|
||||||
ImageView imageView,
|
ImageView imageView,
|
||||||
int imageWidth, int imageHeight) {
|
int imageWidth, int imageHeight) {
|
||||||
// Load character avatar
|
|
||||||
if (characterAvatarColors == null) {
|
|
||||||
characterAvatarColors = context.getResources()
|
|
||||||
.obtainTypedArray(R.array.character_avatar_colors);
|
|
||||||
}
|
|
||||||
|
|
||||||
char charAvatar = TextUtils.isEmpty(stringAvatar) ?
|
CharacterDrawable avatarDrawable = getCharacterAvatar(context, stringAvatar);
|
||||||
' ' : stringAvatar.charAt(0);
|
|
||||||
avatarColorsIdx = TextUtils.isEmpty(stringAvatar) ? 0 :
|
|
||||||
Math.max(Character.getNumericValue(stringAvatar.charAt(0)) +
|
|
||||||
Character.getNumericValue(stringAvatar.charAt(stringAvatar.length() - 1)) +
|
|
||||||
stringAvatar.length(), 0) % characterAvatarColors.length();
|
|
||||||
int color = characterAvatarColors.getColor(avatarColorsIdx, 0xff000000);
|
|
||||||
CharacterDrawable avatarDrawable = new CharacterDrawable(charAvatar, color);
|
|
||||||
|
|
||||||
// avatarColorsIdx = randomGenerator.nextInt(characterAvatarColors.length());
|
|
||||||
if (TextUtils.isEmpty(imageUrl)) {
|
if (TextUtils.isEmpty(imageUrl)) {
|
||||||
imageView.setImageDrawable(avatarDrawable);
|
imageView.setImageDrawable(avatarDrawable);
|
||||||
return;
|
return;
|
||||||
|
@ -166,6 +152,31 @@ public class UIUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a CharacterDrawable that is suitable to use as an avatar
|
||||||
|
* @param context Context
|
||||||
|
* @param str String to use to create the avatar
|
||||||
|
* @return Character avatar to use in a image view
|
||||||
|
*/
|
||||||
|
public static CharacterDrawable getCharacterAvatar(Context context, String str) {
|
||||||
|
// Load character avatar
|
||||||
|
if (characterAvatarColors == null) {
|
||||||
|
characterAvatarColors = context
|
||||||
|
.getResources()
|
||||||
|
.obtainTypedArray(R.array.character_avatar_colors);
|
||||||
|
}
|
||||||
|
|
||||||
|
char charAvatar = TextUtils.isEmpty(str) ?
|
||||||
|
' ' : str.charAt(0);
|
||||||
|
avatarColorsIdx = TextUtils.isEmpty(str) ? 0 :
|
||||||
|
Math.max(Character.getNumericValue(str.charAt(0)) +
|
||||||
|
Character.getNumericValue(str.charAt(str.length() - 1)) +
|
||||||
|
str.length(), 0) % characterAvatarColors.length();
|
||||||
|
int color = characterAvatarColors.getColor(avatarColorsIdx, 0xff000000);
|
||||||
|
// avatarColorsIdx = randomGenerator.nextInt(characterAvatarColors.length());
|
||||||
|
return new CharacterDrawable(charAvatar, color);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets play/pause button icon on a ImageView, based on speed
|
* Sets play/pause button icon on a ImageView, based on speed
|
||||||
* @param context Activity
|
* @param context Activity
|
||||||
|
|
|
@ -17,6 +17,10 @@ package com.syncedsynapse.kore2.utils;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
@ -131,4 +135,23 @@ public class Utils {
|
||||||
context.startActivity(intent);
|
context.startActivity(intent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a drawable to a bitmap
|
||||||
|
* @param drawable Drawable to convert
|
||||||
|
* @return Bitmap
|
||||||
|
*/
|
||||||
|
public static Bitmap drawableToBitmap (Drawable drawable, int width, int height) {
|
||||||
|
if (drawable instanceof BitmapDrawable) {
|
||||||
|
return ((BitmapDrawable)drawable).getBitmap();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue