Cached bitmaps will now have the same size as their ImageViews

This commit is contained in:
daniel oeh 2012-08-26 21:53:34 +02:00
parent 40e0950c23
commit 616c247d84
9 changed files with 105 additions and 88 deletions

View File

@ -14,7 +14,10 @@
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_marginLeft="1dip" android:layout_marginLeft="1dip"
android:layout_marginRight="4dip" android:layout_marginRight="4dip"
android:cropToPadding="true" /> android:adjustViewBounds="true"
android:cropToPadding="true"
android:scaleType="fitXY"
android:src="@drawable/default_cover" />
<TextView <TextView
android:id="@+id/txtvNewEps" android:id="@+id/txtvNewEps"

View File

@ -25,7 +25,7 @@ public class FeedInfoActivity extends SherlockActivity {
public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId"; public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
private Feed feed; private Feed feed;
private ImageView imgvCover; private ImageView imgvCover;
private TextView txtvTitle; private TextView txtvTitle;
private TextView txtvDescription; private TextView txtvDescription;
@ -41,22 +41,32 @@ public class FeedInfoActivity extends SherlockActivity {
FeedManager manager = FeedManager.getInstance(); FeedManager manager = FeedManager.getInstance();
feed = manager.getFeed(feedId); feed = manager.getFeed(feedId);
if (feed != null) { if (feed != null) {
if (AppConfig.DEBUG) Log.d(TAG, "Language is " + feed.getLanguage()); if (AppConfig.DEBUG)
if (AppConfig.DEBUG) Log.d(TAG, "Author is " + feed.getAuthor()); Log.d(TAG, "Language is " + feed.getLanguage());
if (AppConfig.DEBUG)
Log.d(TAG, "Author is " + feed.getAuthor());
imgvCover = (ImageView) findViewById(R.id.imgvCover); imgvCover = (ImageView) findViewById(R.id.imgvCover);
txtvTitle = (TextView) findViewById(R.id.txtvTitle); txtvTitle = (TextView) findViewById(R.id.txtvTitle);
txtvDescription = (TextView) findViewById(R.id.txtvDescription); txtvDescription = (TextView) findViewById(R.id.txtvDescription);
txtvLanguage = (TextView) findViewById(R.id.txtvLanguage); txtvLanguage = (TextView) findViewById(R.id.txtvLanguage);
txtvAuthor = (TextView) findViewById(R.id.txtvAuthor); txtvAuthor = (TextView) findViewById(R.id.txtvAuthor);
FeedImageLoader.getInstance().loadCoverBitmap(feed.getImage(), imgvCover); imgvCover.post(new Runnable() {
@Override
public void run() {
FeedImageLoader.getInstance().loadThumbnailBitmap(
feed.getImage(), imgvCover);
}
});
txtvTitle.setText(feed.getTitle()); txtvTitle.setText(feed.getTitle());
txtvDescription.setText(feed.getDescription()); txtvDescription.setText(feed.getDescription());
if (feed.getAuthor() != null) { if (feed.getAuthor() != null) {
txtvAuthor.setText(feed.getAuthor()); txtvAuthor.setText(feed.getAuthor());
} }
if (feed.getLanguage() != null) { if (feed.getLanguage() != null) {
txtvLanguage.setText(LangUtils.getLanguageString(feed.getLanguage())); txtvLanguage.setText(LangUtils.getLanguageString(feed
.getLanguage()));
} }
} else { } else {
Log.e(TAG, "Activity was started with invalid arguments"); Log.e(TAG, "Activity was started with invalid arguments");
@ -64,27 +74,22 @@ public class FeedInfoActivity extends SherlockActivity {
} }
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = new MenuInflater(this); MenuInflater inflater = new MenuInflater(this);
inflater.inflate(R.menu.feedinfo, menu); inflater.inflate(R.menu.feedinfo, menu);
return true; return true;
} }
@Override @Override
public boolean onPrepareOptionsMenu(Menu menu) { public boolean onPrepareOptionsMenu(Menu menu) {
menu.findItem(R.id.support_item).setVisible(feed.getPaymentLink() != null); menu.findItem(R.id.support_item).setVisible(
feed.getPaymentLink() != null);
menu.findItem(R.id.share_link_item).setVisible(feed.getLink() != null); menu.findItem(R.id.share_link_item).setVisible(feed.getLink() != null);
return true; return true;
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {

View File

@ -39,8 +39,8 @@ public class FeedlistAdapter extends ArrayAdapter<Feed> {
@Override @Override
public View getView(int position, View convertView, ViewGroup parent) { public View getView(int position, View convertView, ViewGroup parent) {
Holder holder; final Holder holder;
Feed feed = getItem(position); final Feed feed = getItem(position);
// Inflate Layout // Inflate Layout
if (convertView == null) { if (convertView == null) {
@ -94,12 +94,14 @@ public class FeedlistAdapter extends ArrayAdapter<Feed> {
holder.newEpisodes.setVisibility(View.INVISIBLE); holder.newEpisodes.setVisibility(View.INVISIBLE);
} }
holder.image.setTag(feed.getImage()); holder.image.setTag(feed.getImage());
/*if (PodcastApp.getInstance().isLargeScreen()) {
imageLoader.loadCoverBitmap(feed.getImage(), holder.image);
} else {*/ holder.image.post(new Runnable() {
imageLoader.loadThumbnailBitmap(feed.getImage(), holder.image);
//} @Override
public void run() {
imageLoader.loadThumbnailBitmap(feed.getImage(), holder.image);
}
});
return convertView; return convertView;
} }

View File

@ -26,7 +26,7 @@ public class SearchlistAdapter extends ArrayAdapter<SearchResult> {
@Override @Override
public View getView(int position, View convertView, ViewGroup parent) { public View getView(int position, View convertView, ViewGroup parent) {
Holder holder; final Holder holder;
SearchResult result = getItem(position); SearchResult result = getItem(position);
FeedComponent component = result.getComponent(); FeedComponent component = result.getComponent();
@ -48,21 +48,34 @@ public class SearchlistAdapter extends ArrayAdapter<SearchResult> {
holder = (Holder) convertView.getTag(); holder = (Holder) convertView.getTag();
} }
if (component.getClass() == Feed.class) { if (component.getClass() == Feed.class) {
Feed feed = (Feed) component; final Feed feed = (Feed) component;
holder.title.setText(feed.getTitle()); holder.title.setText(feed.getTitle());
holder.subtitle.setVisibility(View.GONE); holder.subtitle.setVisibility(View.GONE);
FeedImageLoader.getInstance().loadThumbnailBitmap(feed.getImage(), holder.cover.post(new Runnable() {
holder.cover);
@Override
public void run() {
FeedImageLoader.getInstance().loadThumbnailBitmap(
feed.getImage(), holder.cover);
}
});
} else if (component.getClass() == FeedItem.class) { } else if (component.getClass() == FeedItem.class) {
FeedItem item = (FeedItem) component; final FeedItem item = (FeedItem) component;
holder.title.setText(item.getTitle()); holder.title.setText(item.getTitle());
if (result.getSubtitle() != null) { if (result.getSubtitle() != null) {
holder.subtitle.setVisibility(View.VISIBLE); holder.subtitle.setVisibility(View.VISIBLE);
holder.subtitle.setText(result.getSubtitle()); holder.subtitle.setText(result.getSubtitle());
} }
FeedImageLoader.getInstance().loadThumbnailBitmap(item.getFeed().getImage(), holder.cover.post(new Runnable() {
holder.cover);
@Override
public void run() {
FeedImageLoader.getInstance().loadThumbnailBitmap(
item.getFeed().getImage(), holder.cover);
}
});
} }
return convertView; return convertView;

View File

@ -17,32 +17,27 @@ import de.danoeh.antennapod.util.BitmapDecoder;
public abstract class BitmapDecodeWorkerTask extends Thread { public abstract class BitmapDecodeWorkerTask extends Thread {
protected int PREFERRED_LENGTH; protected int PREFERRED_LENGTH;
public static final int LENGTH_BASE_COVER = 200; /** Can be thumbnail or cover */
public static final int LENGTH_BASE_THUMBNAIL = 100; protected int imageType;
private static final String TAG = "BitmapDecodeWorkerTask"; private static final String TAG = "BitmapDecodeWorkerTask";
private ImageView target; private ImageView target;
protected Bitmap bitmap; protected Bitmap bitmap;
private Bitmap decodedBitmap; private Bitmap decodedBitmap;
protected int baseLength;
protected String fileUrl; protected String fileUrl;
private Handler handler; private Handler handler;
public BitmapDecodeWorkerTask(Handler handler, ImageView target, public BitmapDecodeWorkerTask(Handler handler, ImageView target,
String fileUrl, int length) { String fileUrl, int length, int imageType) {
super(); super();
this.handler = handler; this.handler = handler;
this.target = target; this.target = target;
this.fileUrl = fileUrl; this.fileUrl = fileUrl;
this.baseLength = length; this.PREFERRED_LENGTH = length;
this.PREFERRED_LENGTH = (int) (length * PodcastApp.getLogicalDensity() + 0.5f); this.imageType = imageType;
if (PodcastApp.getInstance().isLargeScreen()) {
this.PREFERRED_LENGTH *= 2.0;
}
} }
/** /**
@ -108,9 +103,9 @@ public abstract class BitmapDecodeWorkerTask extends Thread {
protected void storeBitmapInCache(Bitmap bitmap) { protected void storeBitmapInCache(Bitmap bitmap) {
FeedImageLoader loader = FeedImageLoader.getInstance(); FeedImageLoader loader = FeedImageLoader.getInstance();
if (baseLength == LENGTH_BASE_COVER) { if (imageType == FeedImageLoader.IMAGE_TYPE_COVER) {
loader.addBitmapToCoverCache(fileUrl, bitmap); loader.addBitmapToCoverCache(fileUrl, bitmap);
} else if (baseLength == LENGTH_BASE_THUMBNAIL) { } else if (imageType == FeedImageLoader.IMAGE_TYPE_THUMBNAIL) {
loader.addBitmapToThumbnailCache(fileUrl, bitmap); loader.addBitmapToThumbnailCache(fileUrl, bitmap);
} }
} }

View File

@ -25,8 +25,8 @@ public class FeedImageLoader {
private static final String TAG = "FeedImageLoader"; private static final String TAG = "FeedImageLoader";
private static FeedImageLoader singleton; private static FeedImageLoader singleton;
public static final int LENGTH_BASE_COVER = 300; public static final int IMAGE_TYPE_THUMBNAIL = 0;
public static final int LENGTH_BASE_THUMBNAIL = 100; public static final int IMAGE_TYPE_COVER = 1;
private static final String CACHE_DIR = "miroguide_thumbnails"; private static final String CACHE_DIR = "miroguide_thumbnails";
private static final int CACHE_SIZE = 20 * 1024 * 1024; private static final int CACHE_SIZE = 20 * 1024 * 1024;
@ -45,7 +45,7 @@ public class FeedImageLoader {
// Use 1/8th of the available memory for this memory cache. // Use 1/8th of the available memory for this memory cache.
final int coverCacheSize = 1024 * 1024 * memClass / 8; final int coverCacheSize = 1024 * 1024 * memClass / 8;
final int thumbnailCacheSize = 1024 * 1024 * memClass / 6; final int thumbnailCacheSize = 1024 * 1024 * memClass / 8;
private LruCache<String, Bitmap> coverCache; private LruCache<String, Bitmap> coverCache;
private LruCache<String, Bitmap> thumbnailCache; private LruCache<String, Bitmap> thumbnailCache;
@ -103,6 +103,12 @@ public class FeedImageLoader {
return singleton; return singleton;
} }
/**
* Load a bitmap from the cover cache. If the bitmap is not in the cache, it
* will be loaded from the disk. This method should either be called if the
* ImageView's size has already been set or inside a Runnable which is posted
* to the ImageView's message queue.
*/
public void loadCoverBitmap(FeedImage image, ImageView target) { public void loadCoverBitmap(FeedImage image, ImageView target) {
if (image != null && image.getFile_url() != null) { if (image != null && image.getFile_url() != null) {
Bitmap bitmap = getBitmapFromCoverCache(image.getFile_url()); Bitmap bitmap = getBitmapFromCoverCache(image.getFile_url());
@ -111,7 +117,8 @@ public class FeedImageLoader {
} else { } else {
target.setImageResource(R.drawable.default_cover); target.setImageResource(R.drawable.default_cover);
FeedImageDecodeWorkerTask worker = new FeedImageDecodeWorkerTask( FeedImageDecodeWorkerTask worker = new FeedImageDecodeWorkerTask(
handler, target, image, LENGTH_BASE_COVER); handler, target, image, target.getHeight(),
IMAGE_TYPE_COVER);
executor.submit(worker); executor.submit(worker);
} }
} else { } else {
@ -119,6 +126,12 @@ public class FeedImageLoader {
} }
} }
/**
* Load a bitmap from the thumbnail cache. If the bitmap is not in the cache, it
* will be loaded from the disk. This method should either be called if the
* ImageView's size has already been set or inside a Runnable which is posted
* to the ImageView's message queue.
*/
public void loadThumbnailBitmap(FeedImage image, ImageView target) { public void loadThumbnailBitmap(FeedImage image, ImageView target) {
if (image != null && image.getFile_url() != null) { if (image != null && image.getFile_url() != null) {
Bitmap bitmap = getBitmapFromThumbnailCache(image.getFile_url()); Bitmap bitmap = getBitmapFromThumbnailCache(image.getFile_url());
@ -127,7 +140,8 @@ public class FeedImageLoader {
} else { } else {
target.setImageResource(R.drawable.default_cover); target.setImageResource(R.drawable.default_cover);
FeedImageDecodeWorkerTask worker = new FeedImageDecodeWorkerTask( FeedImageDecodeWorkerTask worker = new FeedImageDecodeWorkerTask(
handler, target, image, LENGTH_BASE_THUMBNAIL); handler, target, image, target.getHeight(),
IMAGE_TYPE_THUMBNAIL);
executor.submit(worker); executor.submit(worker);
} }
} else { } else {
@ -135,7 +149,8 @@ public class FeedImageLoader {
} }
} }
public void loadMiroGuideThumbnail(MiroGuideChannel channel, ImageView target) { public void loadMiroGuideThumbnail(MiroGuideChannel channel,
ImageView target) {
if (channel.getThumbnailUrl() != null) { if (channel.getThumbnailUrl() != null) {
Bitmap bitmap = getBitmapFromThumbnailCache(channel Bitmap bitmap = getBitmapFromThumbnailCache(channel
.getThumbnailUrl()); .getThumbnailUrl());
@ -145,7 +160,7 @@ public class FeedImageLoader {
target.setImageResource(R.drawable.default_cover); target.setImageResource(R.drawable.default_cover);
executor.submit(new MiroGuideThumbnailDownloader(handler, executor.submit(new MiroGuideThumbnailDownloader(handler,
target, channel, LENGTH_BASE_THUMBNAIL)); target, channel, target.getHeight(), coverCacheSize));
} else { } else {
target.setImageBitmap(bitmap); target.setImageBitmap(bitmap);
} }
@ -198,8 +213,8 @@ public class FeedImageLoader {
protected FeedImage image; protected FeedImage image;
public FeedImageDecodeWorkerTask(Handler handler, ImageView target, public FeedImageDecodeWorkerTask(Handler handler, ImageView target,
FeedImage image, int length) { FeedImage image, int length, int imageType) {
super(handler, target, image.getFile_url(), length); super(handler, target, image.getFile_url(), length, imageType);
this.image = image; this.image = image;
} }

View File

@ -25,8 +25,8 @@ public class MiroGuideThumbnailDownloader extends BitmapDecodeWorkerTask {
private MiroGuideChannel miroChannel; private MiroGuideChannel miroChannel;
public MiroGuideThumbnailDownloader(Handler handler, ImageView target, public MiroGuideThumbnailDownloader(Handler handler, ImageView target,
MiroGuideChannel miroChannel, int length) { MiroGuideChannel miroChannel, int length, int imageType) {
super(handler, target, miroChannel.getThumbnailUrl(), length); super(handler, target, miroChannel.getThumbnailUrl(), length, imageType);
this.miroChannel = miroChannel; this.miroChannel = miroChannel;
} }

View File

@ -83,8 +83,15 @@ public class CoverFragment extends SherlockFragment {
} }
private void loadMediaInfo() { private void loadMediaInfo() {
FeedImageLoader.getInstance().loadCoverBitmap( imgvCover.post(new Runnable() {
media.getItem().getFeed().getImage(), imgvCover);
@Override
public void run() {
FeedImageLoader.getInstance().loadCoverBitmap(
media.getItem().getFeed().getImage(), imgvCover);
}
});
txtvTitle.setText(media.getItem().getTitle()); txtvTitle.setText(media.getItem().getTitle());
txtvFeed.setText(media.getItem().getFeed().getTitle()); txtvFeed.setText(media.getItem().getFeed().getTitle());
} }

View File

@ -7,26 +7,12 @@ import android.util.Log;
public class BitmapDecoder { public class BitmapDecoder {
private static final String TAG = "BitmapDecoder"; private static final String TAG = "BitmapDecoder";
private static int calculateSampleSize(int preferredLength, int width, private static int calculateSampleSize(int preferredLength, int length) {
int height) { int sampleSize = 1;
int max = Math.max(width, height); if (length > preferredLength) {
if (max < preferredLength) { sampleSize = Math.round(((float) length / (float) preferredLength));
return 1;
} else {
// find first sample size where max / sampleSize <
// PREFERRED_LENGTH
for (int sampleSize = 1, power = 0;; power++, sampleSize = (int) Math
.pow(2, power)) {
int newLength = max / sampleSize;
if (newLength <= preferredLength) {
if (newLength > 0) {
return sampleSize;
} else {
return sampleSize - 1;
}
}
}
} }
return sampleSize;
} }
public static Bitmap decodeBitmap(int preferredLength, String fileUrl) { public static Bitmap decodeBitmap(int preferredLength, String fileUrl) {
@ -35,14 +21,13 @@ public class BitmapDecoder {
BitmapFactory.decodeFile(fileUrl, options); BitmapFactory.decodeFile(fileUrl, options);
int srcWidth = options.outWidth; int srcWidth = options.outWidth;
int srcHeight = options.outHeight; int srcHeight = options.outHeight;
int sampleSize = calculateSampleSize(preferredLength, srcWidth, int length = Math.max(srcWidth, srcHeight);
srcHeight); int sampleSize = calculateSampleSize(preferredLength, length);
Log.d(TAG, "Using samplesize " + sampleSize);
options.inJustDecodeBounds = false; options.inJustDecodeBounds = false;
options.inSampleSize = sampleSize; options.inSampleSize = sampleSize;
options.inPreferredConfig = Bitmap.Config.ARGB_8888; options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.inScaled = true;
Bitmap decodedBitmap = BitmapFactory.decodeFile(fileUrl, options); Bitmap decodedBitmap = BitmapFactory.decodeFile(fileUrl, options);
if (decodedBitmap == null) { if (decodedBitmap == null) {
Log.i(TAG, Log.i(TAG,
@ -50,14 +35,6 @@ public class BitmapDecoder {
+ fileUrl + ")"); + fileUrl + ")");
decodedBitmap = BitmapFactory.decodeFile(fileUrl); decodedBitmap = BitmapFactory.decodeFile(fileUrl);
} }
if (decodedBitmap != null) { return decodedBitmap;
return decodedBitmap;
/*
return Bitmap.createScaledBitmap(decodedBitmap,
preferredLength, preferredLength, false);
*/
} else {
return null;
}
} }
} }