Improve image loading in the notification, the widget and the lockscreen album art by moving stuff to worker thread and adjusting the bitmap size which should also prevent the to large bitmap exception currently occurring in the widget for some devices.

This commit is contained in:
Karim Abou Zeid 2015-07-11 00:34:25 +02:00
commit 9d4ea9911f
7 changed files with 98 additions and 79 deletions

View file

@ -18,12 +18,14 @@ import com.kabouzeid.gramophone.model.Song;
import com.kabouzeid.gramophone.service.MusicService; import com.kabouzeid.gramophone.service.MusicService;
import com.kabouzeid.gramophone.ui.activities.MainActivity; import com.kabouzeid.gramophone.ui.activities.MainActivity;
import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.MusicUtil;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.assist.ImageSize; import com.nostra13.universalimageloader.core.assist.ImageSize;
import com.nostra13.universalimageloader.core.assist.ViewScaleType; import com.nostra13.universalimageloader.core.assist.ViewScaleType;
import com.nostra13.universalimageloader.core.imageaware.NonViewAware; import com.nostra13.universalimageloader.core.imageaware.NonViewAware;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
import com.nostra13.universalimageloader.core.process.BitmapProcessor;
public class WidgetMedium extends AppWidgetProvider { public class WidgetMedium extends AppWidgetProvider {
private static RemoteViews widgetLayout; private static RemoteViews widgetLayout;
@ -66,33 +68,40 @@ public class WidgetMedium extends AppWidgetProvider {
private static void loadAlbumArt(@NonNull final Context context, @Nullable final Song song) { private static void loadAlbumArt(@NonNull final Context context, @Nullable final Song song) {
if (song != null) { if (song != null) {
int widgetImageSize = context.getResources().getDimensionPixelSize(R.dimen.widget_medium_image_size);
currentAlbumArtUri = MusicUtil.getSongImageLoaderString(song); currentAlbumArtUri = MusicUtil.getSongImageLoaderString(song);
ImageLoader.getInstance().displayImage(currentAlbumArtUri, new NonViewAware(new ImageSize(-1, -1), ViewScaleType.CROP), new SimpleImageLoadingListener() { ImageLoader.getInstance().displayImage(
@Override currentAlbumArtUri,
public void onLoadingComplete(String imageUri, View view, @Nullable Bitmap loadedImage) { new NonViewAware(new ImageSize(widgetImageSize, widgetImageSize), ViewScaleType.CROP),
if (currentAlbumArtUri.equals(imageUri)) { new DisplayImageOptions.Builder()
if (loadedImage != null) { .postProcessor(new BitmapProcessor() {
// The RemoteViews might wants to recycle the bitmaps thrown at it, so we need @Override
// to make sure not to hand out our cache copy public Bitmap process(Bitmap bitmap) {
Bitmap.Config config = loadedImage.getConfig(); // The RemoteViews might wants to recycle the bitmaps thrown at it, so we need
if (config == null) { // to make sure not to hand out our cache copy
config = Bitmap.Config.ARGB_8888; Bitmap.Config config = bitmap.getConfig();
if (config == null) {
config = Bitmap.Config.ARGB_8888;
}
bitmap = bitmap.copy(config, false);
return bitmap.copy(bitmap.getConfig(), true);
}
}).build(),
new SimpleImageLoadingListener() {
@Override
public void onLoadingComplete(String imageUri, View view, @Nullable Bitmap loadedImage) {
if (currentAlbumArtUri.equals(imageUri)) {
setAlbumArt(context, loadedImage);
} }
loadedImage = loadedImage.copy(config, false);
setAlbumArt(context, loadedImage.copy(loadedImage.getConfig(), true));
} else {
setAlbumArt(context, null);
} }
}
}
@Override @Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) { public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
if (currentAlbumArtUri.equals(imageUri)) { if (currentAlbumArtUri.equals(imageUri)) {
setAlbumArt(context, null); setAlbumArt(context, null);
} }
} }
}); });
} }
} }

View file

@ -46,7 +46,7 @@ public class PlayingNotificationHelper {
@NonNull @NonNull
private final NotificationManager notificationManager; private final NotificationManager notificationManager;
@Nullable @Nullable
private Notification notification = null; private Notification notification;
private RemoteViews notificationLayout; private RemoteViews notificationLayout;
private RemoteViews notificationLayoutExpanded; private RemoteViews notificationLayoutExpanded;
@ -59,6 +59,8 @@ public class PlayingNotificationHelper {
private boolean isReceiverRegistered; private boolean isReceiverRegistered;
private boolean isNotificationShown; private boolean isNotificationShown;
private int bigNotificationImageSize;
@NonNull @NonNull
final IntentFilter intentFilter; final IntentFilter intentFilter;
@ -69,6 +71,8 @@ public class PlayingNotificationHelper {
intentFilter = new IntentFilter(); intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_NOTIFICATION_COLOR_PREFERENCE_CHANGED); intentFilter.addAction(ACTION_NOTIFICATION_COLOR_PREFERENCE_CHANGED);
bigNotificationImageSize = service.getResources().getDimensionPixelSize(R.dimen.notification_big_image_size);
} }
@NonNull @NonNull
@ -211,19 +215,22 @@ public class PlayingNotificationHelper {
private void loadAlbumArt() { private void loadAlbumArt() {
currentAlbumArtUri = MusicUtil.getSongImageLoaderString(currentSong); currentAlbumArtUri = MusicUtil.getSongImageLoaderString(currentSong);
ImageLoader.getInstance().displayImage(currentAlbumArtUri, new NonViewAware(new ImageSize(-1, -1), ViewScaleType.CROP), new SimpleImageLoadingListener() { ImageLoader.getInstance().displayImage(
@Override currentAlbumArtUri,
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { new NonViewAware(new ImageSize(bigNotificationImageSize, bigNotificationImageSize), ViewScaleType.CROP),
if (currentAlbumArtUri.equals(imageUri)) new SimpleImageLoadingListener() {
setAlbumArt(loadedImage); @Override
} public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
if (currentAlbumArtUri.equals(imageUri))
setAlbumArt(loadedImage);
}
@Override @Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) { public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
if (currentAlbumArtUri.equals(imageUri)) if (currentAlbumArtUri.equals(imageUri))
setAlbumArt(null); setAlbumArt(null);
} }
}); });
} }
private void setAlbumArt(@Nullable Bitmap albumArt) { private void setAlbumArt(@Nullable Bitmap albumArt) {
@ -244,7 +251,9 @@ public class PlayingNotificationHelper {
} }
} }
notificationManager.notify(NOTIFICATION_ID, notification); if (notification != null) {
notificationManager.notify(NOTIFICATION_ID, notification);
}
} }
private void setBackgroundColor(int color) { private void setBackgroundColor(int color) {

View file

@ -38,12 +38,14 @@ import com.kabouzeid.gramophone.provider.RecentlyPlayedStore;
import com.kabouzeid.gramophone.provider.SongPlayCountStore; import com.kabouzeid.gramophone.provider.SongPlayCountStore;
import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.MusicUtil;
import com.kabouzeid.gramophone.util.PreferenceUtils; import com.kabouzeid.gramophone.util.PreferenceUtils;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.assist.ImageSize; import com.nostra13.universalimageloader.core.assist.ImageSize;
import com.nostra13.universalimageloader.core.assist.ViewScaleType; import com.nostra13.universalimageloader.core.assist.ViewScaleType;
import com.nostra13.universalimageloader.core.imageaware.NonViewAware; import com.nostra13.universalimageloader.core.imageaware.NonViewAware;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
import com.nostra13.universalimageloader.core.process.BitmapProcessor;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
@ -438,33 +440,38 @@ public class MusicService extends Service {
.apply(); .apply();
if (showAlbumArt) { if (showAlbumArt) {
final String currentAlbumArtUri = MusicUtil.getSongImageLoaderString(song); final String currentAlbumArtUri = MusicUtil.getSongImageLoaderString(song);
ImageLoader.getInstance().displayImage(currentAlbumArtUri, new NonViewAware(new ImageSize(-1, -1), ViewScaleType.CROP), new SimpleImageLoadingListener() { ImageLoader.getInstance().displayImage(
@Override currentAlbumArtUri,
public void onLoadingComplete(String imageUri, View view, @Nullable Bitmap loadedImage) { new NonViewAware(new ImageSize(-1, -1), ViewScaleType.CROP),
if (!currentAlbumArtUri.equals(imageUri)) { new DisplayImageOptions.Builder()
return; .postProcessor(new BitmapProcessor() {
} @Override
if (loadedImage == null) { public Bitmap process(Bitmap bitmap) {
onLoadingFailed(imageUri, view, null); // RemoteControlClient wants to recycle the bitmaps thrown at it, so we need
return; // to make sure not to hand out our cache copy
} Bitmap.Config config = bitmap.getConfig();
// RemoteControlClient wants to recycle the bitmaps thrown at it, so we need if (config == null) {
// to make sure not to hand out our cache copy config = Bitmap.Config.ARGB_8888;
Bitmap.Config config = loadedImage.getConfig(); }
if (config == null) { bitmap = bitmap.copy(config, false);
config = Bitmap.Config.ARGB_8888; return bitmap.copy(bitmap.getConfig(), true);
} }
loadedImage = loadedImage.copy(config, false); }).build(),
updateRemoteControlClientBitmap(loadedImage.copy(loadedImage.getConfig(), true)); new SimpleImageLoadingListener() {
} @Override
public void onLoadingComplete(String imageUri, View view, @Nullable Bitmap loadedImage) {
if (currentAlbumArtUri.equals(imageUri)) {
updateRemoteControlClientBitmap(loadedImage);
}
}
@Override @Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) { public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
if (currentAlbumArtUri.equals(imageUri)) { if (currentAlbumArtUri.equals(imageUri)) {
updateRemoteControlClientBitmap(null); updateRemoteControlClientBitmap(null);
} }
} }
}); });
} else { } else {
updateRemoteControlClientBitmap(null); updateRemoteControlClientBitmap(null);
} }

View file

@ -23,8 +23,8 @@
<ImageView <ImageView
android:id="@+id/icon" android:id="@+id/icon"
android:layout_width="128dp" android:layout_width="@dimen/notification_big_image_size"
android:layout_height="128dp" android:layout_height="@dimen/notification_big_image_size"
android:scaleType="centerCrop" android:scaleType="centerCrop"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />

View file

@ -24,8 +24,8 @@
<ImageView <ImageView
android:id="@+id/icon" android:id="@+id/icon"
android:layout_width="128dp" android:layout_width="@dimen/notification_big_image_size"
android:layout_height="128dp" android:layout_height="@dimen/notification_big_image_size"
android:scaleType="centerCrop" android:scaleType="centerCrop"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />

View file

@ -7,8 +7,8 @@
<ImageView <ImageView
android:id="@+id/album_art" android:id="@+id/album_art"
android:layout_width="96dp" android:layout_width="@dimen/widget_medium_image_size"
android:layout_height="96dp" android:layout_height="@dimen/widget_medium_image_size"
android:scaleType="centerCrop" android:scaleType="centerCrop"
android:src="@drawable/default_album_art" /> android:src="@drawable/default_album_art" />

View file

@ -17,12 +17,6 @@
<dimen name="status_bar_padding">0dp</dimen> <dimen name="status_bar_padding">0dp</dimen>
<!-- Notification template -->
<dimen name="notification_big_icon_height">64.0dip</dimen>
<dimen name="notification_big_icon_width">64.0dip</dimen>
<dimen name="notification_info_container_padding_left">8.0dip</dimen>
<dimen name="notification_info_container_padding_bottom">4.0dip</dimen>
<dimen name="tab_height">48dp</dimen> <dimen name="tab_height">48dp</dimen>
<dimen name="tagEditorHeaderVariableSpace">100dp</dimen> <dimen name="tagEditorHeaderVariableSpace">100dp</dimen>
@ -31,8 +25,6 @@
Refer to App Widget Documentation for margin information Refer to App Widget Documentation for margin information
http://developer.android.com/guide/topics/appwidgets/index.html#CreatingLayout http://developer.android.com/guide/topics/appwidgets/index.html#CreatingLayout
--> -->
<dimen name="app_widget_small_artwork_height">64dp</dimen>
<dimen name="pause_bar_width">5dp</dimen> <dimen name="pause_bar_width">5dp</dimen>
<dimen name="pause_bar_distance">4dp</dimen> <dimen name="pause_bar_distance">4dp</dimen>
<dimen name="pause_bar_height">14dp</dimen> <dimen name="pause_bar_height">14dp</dimen>
@ -48,7 +40,9 @@ http://developer.android.com/guide/topics/appwidgets/index.html#CreatingLayout
<dimen name="seek_bar_margin_left_right">-17dp</dimen> <dimen name="seek_bar_margin_left_right">-17dp</dimen>
<dimen name="list_padding_vertical">2dp</dimen> <dimen name="list_padding_vertical">2dp</dimen>
<dimen name="notification_albumart_size">128dp</dimen> <dimen name="widget_medium_image_size">96dp</dimen>
<dimen name="notification_big_image_size">128dp</dimen>
<dimen name="bottom_offset_fab_activity">86dp</dimen> <dimen name="bottom_offset_fab_activity">86dp</dimen>
<dimen name="scrollbar_width">8dp</dimen> <dimen name="scrollbar_width">8dp</dimen>