Revert to using Bitmaps and a cache for cover art instead of URIs

While storing a Bitmap in a Bundle for RemoteViews seems to be spectacularly
slow, using URIs to prevent this does not seem to be worth the additional
disk I/O they incur.
This commit is contained in:
Christopher Eby 2011-11-08 21:10:31 -06:00
parent 0866b50ac4
commit 0f54cb6242
8 changed files with 53 additions and 75 deletions

View File

@ -216,7 +216,6 @@ public final class CoverBitmap {
int y = (bitmapHeight - coverHeight) / 2;
Rect rect = new Rect(x, y, x + coverWidth, y + coverHeight);
canvas.drawBitmap(cover, null, rect, paint);
cover.recycle();
}
int left = (bitmapWidth - boxWidth) / 2;
@ -313,7 +312,6 @@ public final class CoverBitmap {
int y = horizontal ? (bitmapHeight - coverHeight) / 2 : 0;
Rect rect = new Rect(x, y, x + coverWidth, y + coverHeight);
canvas.drawBitmap(cover, null, rect, paint);
cover.recycle();
}
int top;
@ -382,7 +380,6 @@ public final class CoverBitmap {
Rect src = new Rect(xOffset, yOffset, sourceWidth - xOffset, sourceHeight - yOffset);
Rect dest = new Rect(0, 0, width, height);
canvas.drawBitmap(source, src, dest, null);
source.recycle();
return bitmap;
}
@ -409,9 +406,7 @@ public final class CoverBitmap {
float scale = Math.min((float)width / sourceWidth, (float)height / sourceHeight);
sourceWidth *= scale;
sourceHeight *= scale;
Bitmap result = Bitmap.createScaledBitmap(source, sourceWidth, sourceHeight, false);
source.recycle();
return result;
return Bitmap.createScaledBitmap(source, sourceWidth, sourceHeight, false);
}
/**

View File

@ -28,7 +28,7 @@ import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.graphics.Bitmap;
import android.view.View;
import android.widget.RemoteViews;
@ -106,11 +106,11 @@ public class FourLongWidget extends AppWidgetProvider {
views.setViewVisibility(R.id.buttons, View.VISIBLE);
views.setTextViewText(R.id.title, song.title);
views.setTextViewText(R.id.artist, song.artist);
Uri uri = song.getCoverUri();
if (uri == null)
Bitmap cover = song.getCover(context);
if (cover == null)
views.setImageViewResource(R.id.cover, 0);
else
views.setImageViewUri(R.id.cover, uri);
views.setImageViewBitmap(R.id.cover, cover);
}
boolean playing = (state & PlaybackService.FLAG_PLAYING) != 0;

View File

@ -28,7 +28,7 @@ import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.graphics.Bitmap;
import android.view.View;
import android.widget.RemoteViews;
@ -106,11 +106,11 @@ public class FourSquareWidget extends AppWidgetProvider {
views.setViewVisibility(R.id.buttons, View.VISIBLE);
views.setTextViewText(R.id.title, song.title);
views.setTextViewText(R.id.artist, song.artist);
Uri uri = song.getCoverUri();
if (uri == null)
Bitmap cover = song.getCover(context);
if (cover == null)
views.setImageViewResource(R.id.cover, 0);
else
views.setImageViewUri(R.id.cover, uri);
views.setImageViewBitmap(R.id.cover, cover);
}
boolean playing = (state & PlaybackService.FLAG_PLAYING) != 0;

View File

@ -31,6 +31,7 @@ import android.content.res.Resources;
import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.PaintDrawable;
import android.net.Uri;
@ -1073,7 +1074,7 @@ public class LibraryActivity
super.onSongChange(song);
if (mTitle != null) {
Uri cover = null;
Bitmap cover = null;
if (song == null) {
mTitle.setText(R.string.none);
@ -1084,7 +1085,7 @@ public class LibraryActivity
String artist = song.artist == null ? res.getString(R.string.unknown) : song.artist;
mTitle.setText(title);
mArtist.setText(artist);
cover = song.hasCover(this) ? song.getCoverUri() : null;
cover = song.getCover(this);
}
if (Song.mDisableCoverArt)
@ -1092,7 +1093,7 @@ public class LibraryActivity
else if (cover == null)
mCover.setImageResource(R.drawable.albumart_mp_unknown_list);
else
mCover.setImageURI(cover);
mCover.setImageBitmap(cover);
}
}

View File

@ -29,7 +29,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.graphics.Bitmap;
import android.widget.RemoteViews;
/**
@ -108,18 +108,18 @@ public class OneCellWidget extends AppWidgetProvider {
views.setOnClickPendingIntent(R.id.next, PendingIntent.getService(context, 0, next, 0));
if ((state & PlaybackService.FLAG_NO_MEDIA) != 0) {
views.setImageViewResource(R.id.cover, 0);
views.setInt(R.id.title, "setText", R.string.no_songs);
} else if (song == null) {
views.setImageViewResource(R.id.cover, 0);
} else if (song == null) {
views.setInt(R.id.title, "setText", R.string.app_name);
views.setImageViewResource(R.id.cover, 0);
} else {
Uri uri = song.getCoverUri();
if (uri == null)
views.setTextViewText(R.id.title, song.title);
Bitmap cover = song.getCover(context);
if (cover == null)
views.setImageViewResource(R.id.cover, 0);
else
views.setImageViewUri(R.id.cover, uri);
views.setTextViewText(R.id.title, song.title);
views.setImageViewBitmap(R.id.cover, cover);
}
manager.updateAppWidget(new ComponentName(context, OneCellWidget.class), views);

View File

@ -36,6 +36,7 @@ import android.content.res.TypedArray;
import android.content.SharedPreferences;
import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Build;
@ -1437,10 +1438,11 @@ public final class PlaybackService extends Service
RemoteViews views = new RemoteViews(getPackageName(), R.layout.notification);
if (!Song.mDisableCoverArt && song.hasCover(this)) {
views.setImageViewUri(R.id.icon, song.getCoverUri());
} else {
Bitmap cover = song.getCover(this);
if (cover == null) {
views.setImageViewResource(R.id.icon, R.drawable.icon);
} else {
views.setImageViewBitmap(R.id.icon, cover);
}
if (playing) {
views.setTextViewText(R.id.title, song.title);

View File

@ -32,7 +32,6 @@ import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import android.util.Log;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
/**
* Represents a Song backed by the MediaStore. Includes basic metadata and
@ -80,6 +79,11 @@ public class Song implements Comparable<Song> {
MediaStore.Audio.Playlists.Members.TRACK,
};
/**
* A cache of 8 covers.
*/
private static final Cache<Bitmap> sCoverCache = new Cache<Bitmap>(8);
/**
* If true, will not attempt to load any cover art in getCover()
*/
@ -131,9 +135,10 @@ public class Song implements Comparable<Song> {
public int flags;
/**
* 1 if this song has cover art in the MediaStore, 0 if not, -1 if uninitialized.
* If true, this song has no cover art. If false, this song may or may not
* have cover art.
*/
public int hasCover = -1;
public boolean noCover;
/**
* Initialize the song with the specified id. Call populate to fill fields
@ -193,42 +198,6 @@ public class Song implements Comparable<Song> {
return song.id;
}
/**
* Returns true if this song has cover art stored in the MediaStore.
*
* @param context A context to use to query the MediaStore
*/
public boolean hasCover(Context context)
{
if (hasCover == -1) {
Uri uri = getCoverUri();
if (uri == null) {
hasCover = 0;
} else {
try {
hasCover = context.getContentResolver().openFileDescriptor(uri, "r") == null ? 0 : 1;
} catch (FileNotFoundException e) {
hasCover = 0;
}
}
}
return hasCover == 1;
}
/**
* Return a URI describing where the cover art is stored, or null if this
* song has not been populated.
*/
public Uri getCoverUri()
{
// Use undocumented API to extract the cover from the media file from Eclair
// See http://android.git.kernel.org/?p=platform/packages/apps/Music.git;a=blob;f=src/com/android/music/MusicUtils.java;h=d1aea0660009940a0160cb981f381e2115768845;hb=0749a3f1c11e052f97a3ba60fd624c9283ee7331#l986
if (id == -1 || hasCover == 0 || mDisableCoverArt)
return null;
return Uri.parse("content://media/external/audio/media/" + id + "/albumart");
}
private static final BitmapFactory.Options BITMAP_OPTIONS = new BitmapFactory.Options();
static {
@ -244,23 +213,34 @@ public class Song implements Comparable<Song> {
*/
public Bitmap getCover(Context context)
{
Uri uri = getCoverUri();
if (uri == null)
if (mDisableCoverArt || id == -1 || noCover)
return null;
Bitmap cover = sCoverCache.get(id);
if (cover != null) {
return cover;
}
Uri uri = Uri.parse("content://media/external/audio/media/" + id + "/albumart");
ContentResolver res = context.getContentResolver();
hasCover = 0;
try {
ParcelFileDescriptor parcelFileDescriptor = res.openFileDescriptor(uri, "r");
if (parcelFileDescriptor != null) {
hasCover = 1;
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
return BitmapFactory.decodeFileDescriptor(fileDescriptor, null, BITMAP_OPTIONS);
cover = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, BITMAP_OPTIONS);
if (cover != null) {
Bitmap discarded = sCoverCache.discardOldest();
if (discarded != null)
discarded.recycle();
sCoverCache.put(id, cover);
return cover;
}
}
} catch (Exception e) {
Log.d("VanillaMusic", "Failed to load cover art for " + path, e);
}
noCover = true;
return null;
}

View File

@ -28,7 +28,7 @@ import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.graphics.Bitmap;
import android.view.View;
import android.widget.RemoteViews;
@ -106,11 +106,11 @@ public class WidgetD extends AppWidgetProvider {
views.setViewVisibility(R.id.buttons, View.VISIBLE);
views.setTextViewText(R.id.title, song.title);
views.setTextViewText(R.id.artist, song.artist);
Uri uri = song.getCoverUri();
if (uri == null)
Bitmap cover = song.getCover(context);
if (cover == null)
views.setImageViewResource(R.id.cover, 0);
else
views.setImageViewUri(R.id.cover, uri);
views.setImageViewBitmap(R.id.cover, cover);
}
boolean playing = (state & PlaybackService.FLAG_PLAYING) != 0;