/*
* Copyright 2008-2013, ETH Zürich, Samuel Welten, Michael Kuhn, Tobias Langner,
* Sandro Affentranger, Lukas Bossard, Michael Grob, Rahul Jain,
* Dominic Langenegger, Sonia Mayor Alonso, Roger Odermatt, Tobias Schlueter,
* Yannick Stucki, Sebastian Wendland, Samuel Zehnder, Samuel Zihlmann,
* Samuel Zweifel
*
* This file is part of Jukefox.
*
* Jukefox is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or any later version. Jukefox is
* distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Jukefox. If not, see <http://www.gnu.org/licenses/>.
*/
package ch.ethz.dcg.pancho3.view.statistics.adapter;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.View;
import android.widget.ImageView;
import ch.ethz.dcg.jukefox.manager.model.albumart.FastBitmapDrawable;
import ch.ethz.dcg.jukefox.model.AndroidCollectionModelManager;
import ch.ethz.dcg.jukefox.model.collection.ListAlbum;
import ch.ethz.dcg.jukefox.model.collection.statistics.StatisticsAlbum;
import ch.ethz.dcg.jukefox.model.commons.NoAlbumArtException;
import ch.ethz.dcg.pancho3.R;
/**
* Helper that asynchronously loads the album icons into image views.
*/
public class StatisticsAlbumIconHelper<T extends StatisticsAlbum> {
/**
* The size of the album icons [in px].
*/
private static final int ICON_SIZE = 100;
/**
* How much the album icon loading should be delayed [in ms].
*/
private static final int ICON_LOAD_DELAY = 300;
/**
* How much album icons should kept in cache.
*/
private static final int ICON_CACHE_SIZE = 100;
/**
* Stores which icon is currently connected to which album.
*/
private final Map<ImageView, T> iconToAlbum;
/**
* The album icon cache.
*/
private final List<AlbumIconCacheEntry> albumIconCache;
/**
* Handler to run stuff in the ui thread from outside of it.
*/
private final Handler uiHandler;
/**
* Timer to run the icon loading processes a little bit delayed.
*/
private final Timer timer;
/**
* Album to use if none is available or the correct one is not loaded yet.
*/
private final FastBitmapDrawable defaultAlbumIcon;
private final AndroidCollectionModelManager collectionModelManager;
public StatisticsAlbumIconHelper(Context context, AndroidCollectionModelManager collectionModelManager) {
this.collectionModelManager = collectionModelManager;
iconToAlbum = new HashMap<ImageView, T>();
uiHandler = new Handler(); // Please make sure, that we are in the ui-thread at this point
timer = new Timer();
albumIconCache = new LinkedList<AlbumIconCacheEntry>();
// Load the default album icon
Resources r = context.getResources();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap b = resizeBitmap(BitmapFactory.decodeResource(r, R.drawable.d005_empty_cd, options));
defaultAlbumIcon = new FastBitmapDrawable(b);
}
public void fillAlbumIcon(View v, T album) {
ImageView icon = (ImageView) v.findViewById(R.id.icon);
// Unhide the icon
icon.setVisibility(ImageView.VISIBLE);
if (iconToAlbum.get(icon) == album) {
// This image is already correctly loaded
return;
}
iconToAlbum.put(icon, album); // Make this icon-album association the state of the art
// Search for this album icon in the cache
int cacheIndex = albumIconCache.indexOf(new AlbumIconCacheEntry(album, null));
if (cacheIndex > -1) {
// Cache hit
icon.setBackgroundDrawable(albumIconCache.get(cacheIndex).getIcon());
} else {
// Load the album image async
icon.setBackgroundDrawable(defaultAlbumIcon);
icon.setPadding(0, 0, 1, 0);
CoverLoadTask clt = new CoverLoadTask(icon, album);
timer.schedule(clt, ICON_LOAD_DELAY);
}
}
/**
* Resize the given bitmap to have the width & height equals {@value #ICON_SIZE}px.
*
* @param bm
* The source bitmap
* @return The resized bitmap
*/
private Bitmap resizeBitmap(Bitmap bm) {
// scale the image for opengl
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = (float) ICON_SIZE / width;
float scaleHeight = (float) ICON_SIZE / height;
// scale matrix
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
return resizedBitmap;
}
/**
* TimerTask which loads the icon of the given album into the given image view.
*/
private class CoverLoadTask extends TimerTask {
ListAlbum album;
ImageView image;
public CoverLoadTask(ImageView image, ListAlbum album) {
this.album = album;
this.image = image;
}
@Override
public void run() {
ListAlbum newAlbum = iconToAlbum.get(image);
if (newAlbum != null && newAlbum == album) { // Check, if our icon-album association is still valid
Drawable albumIcon;
try {
// Get the album icon
albumIcon = collectionModelManager.getAlbumArtProvider().getListAlbumArt(album);
} catch (NoAlbumArtException e) {
// The album icon could not be loaded -> set the default one
albumIcon = defaultAlbumIcon;
}
// Set the album icon in the ui-thread
final Drawable iconToBeSet = albumIcon;
uiHandler.post(new Runnable() {
@Override
public void run() {
synchronized (albumIconCache) {
albumIconCache.add(new AlbumIconCacheEntry(album, iconToBeSet));
if (albumIconCache.size() > ICON_CACHE_SIZE) {
albumIconCache.remove(0); // The cache is full -> remove first entry
}
}
image.setBackgroundDrawable(iconToBeSet);
}
});
}
}
}
/**
* Cache entry for album icons.
*/
private class AlbumIconCacheEntry {
private ListAlbum album;
private Drawable icon;
public AlbumIconCacheEntry(ListAlbum album, Drawable icon) {
this.album = album;
this.icon = icon;
}
public ListAlbum getAlbum() {
return album;
}
public Drawable getIcon() {
return icon;
}
/**
* Returns true, if the albums of the AlbumIconCacheEntries match.
*/
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object other) {
if (other.getClass().equals(AlbumIconCacheEntry.class)) {
return getAlbum().equals(((AlbumIconCacheEntry) other).getAlbum());
}
return false;
}
}
}