Use AsyncTasks and attached Drawables to track background image loading
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
package com.limelight.grid;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
@@ -14,27 +14,18 @@ import com.limelight.grid.assets.MemoryAssetLoader;
|
||||
import com.limelight.grid.assets.NetworkAssetLoader;
|
||||
import com.limelight.nvstream.http.ComputerDetails;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
|
||||
private final Activity activity;
|
||||
|
||||
private static final int ART_WIDTH_PX = 300;
|
||||
private static final int SMALL_WIDTH_DP = 100;
|
||||
private static final int LARGE_WIDTH_DP = 150;
|
||||
|
||||
private final CachedAppAssetLoader loader;
|
||||
private final ConcurrentHashMap<WeakReference<ImageView>, CachedAppAssetLoader.LoaderTuple> loadingTuples = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<Object, CachedAppAssetLoader.LoaderTuple> backgroundLoadingTuples = new ConcurrentHashMap<>();
|
||||
|
||||
public AppGridAdapter(Activity activity, boolean listMode, boolean small, ComputerDetails computer, String uniqueId) throws KeyManagementException, NoSuchAlgorithmException {
|
||||
super(activity, listMode ? R.layout.simple_row : (small ? R.layout.app_grid_item_small : R.layout.app_grid_item), R.drawable.image_loading);
|
||||
@@ -56,26 +47,20 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
|
||||
}
|
||||
LimeLog.info("Art scaling divisor: " + scalingDivisor);
|
||||
|
||||
this.activity = activity;
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inSampleSize = (int) scalingDivisor;
|
||||
|
||||
this.loader = new CachedAppAssetLoader(computer, scalingDivisor,
|
||||
new NetworkAssetLoader(context, uniqueId),
|
||||
new MemoryAssetLoader(), new DiskAssetLoader(context.getCacheDir()));
|
||||
}
|
||||
|
||||
private static void cancelTuples(ConcurrentHashMap<?, CachedAppAssetLoader.LoaderTuple> map) {
|
||||
Collection<CachedAppAssetLoader.LoaderTuple> tuples = map.values();
|
||||
|
||||
for (CachedAppAssetLoader.LoaderTuple tuple : tuples) {
|
||||
tuple.cancel();
|
||||
}
|
||||
|
||||
map.clear();
|
||||
new MemoryAssetLoader(),
|
||||
new DiskAssetLoader(context.getCacheDir()),
|
||||
BitmapFactory.decodeResource(activity.getResources(),
|
||||
R.drawable.image_loading, options));
|
||||
}
|
||||
|
||||
public void cancelQueuedOperations() {
|
||||
cancelTuples(loadingTuples);
|
||||
cancelTuples(backgroundLoadingTuples);
|
||||
|
||||
loader.cancelForegroundLoads();
|
||||
loader.cancelBackgroundLoads();
|
||||
loader.freeCacheMemory();
|
||||
}
|
||||
|
||||
@@ -89,14 +74,10 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
|
||||
}
|
||||
|
||||
public void addApp(AppView.AppObject app) {
|
||||
// Queue a request to fetch this bitmap in the background
|
||||
Object tupleKey = new Object();
|
||||
CachedAppAssetLoader.LoaderTuple tuple =
|
||||
loader.loadBitmapWithContextInBackground(app.app, tupleKey, backgroundLoadListener);
|
||||
if (tuple != null) {
|
||||
backgroundLoadingTuples.put(tupleKey, tuple);
|
||||
}
|
||||
// Queue a request to fetch this bitmap into cache
|
||||
loader.queueCacheLoad(app.app);
|
||||
|
||||
// Add the app to our sorted list
|
||||
itemList.add(app);
|
||||
sortList();
|
||||
}
|
||||
@@ -105,100 +86,9 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
|
||||
itemList.remove(app);
|
||||
}
|
||||
|
||||
private final CachedAppAssetLoader.LoadListener imageViewLoadListener = new CachedAppAssetLoader.LoadListener() {
|
||||
@Override
|
||||
public void notifyLongLoad(Object object) {
|
||||
final WeakReference<ImageView> viewRef = (WeakReference<ImageView>) object;
|
||||
|
||||
// If the view isn't there anymore, don't bother scheduling on the UI thread
|
||||
if (viewRef.get() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ImageView view = viewRef.get();
|
||||
if (view != null) {
|
||||
view.setImageResource(R.drawable.image_loading);
|
||||
fadeInImage(view);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyLoadComplete(Object object, final Bitmap bitmap) {
|
||||
final WeakReference<ImageView> viewRef = (WeakReference<ImageView>) object;
|
||||
|
||||
loadingTuples.remove(viewRef);
|
||||
|
||||
// Just leave the loading icon in place
|
||||
if (bitmap == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the view isn't there anymore, don't bother scheduling on the UI thread
|
||||
if (viewRef.get() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ImageView view = viewRef.get();
|
||||
if (view != null) {
|
||||
view.setImageBitmap(bitmap);
|
||||
fadeInImage(view);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
private final CachedAppAssetLoader.LoadListener backgroundLoadListener = new CachedAppAssetLoader.LoadListener() {
|
||||
@Override
|
||||
public void notifyLongLoad(Object object) {}
|
||||
|
||||
@Override
|
||||
public void notifyLoadComplete(Object object, final Bitmap bitmap) {
|
||||
backgroundLoadingTuples.remove(object);
|
||||
}
|
||||
};
|
||||
|
||||
private void reapLoaderTuples(ImageView view) {
|
||||
// Poor HashMap doesn't deserve this...
|
||||
Iterator<Map.Entry<WeakReference<ImageView>, CachedAppAssetLoader.LoaderTuple>> i = loadingTuples.entrySet().iterator();
|
||||
while (i.hasNext()) {
|
||||
Map.Entry<WeakReference<ImageView>, CachedAppAssetLoader.LoaderTuple> entry = i.next();
|
||||
ImageView imageView = entry.getKey().get();
|
||||
|
||||
// Remove tuples that refer to this view or no view
|
||||
if (imageView == null || imageView == view) {
|
||||
// FIXME: There's a small chance that this can race if we've already gone down
|
||||
// the path to notification but haven't been notified yet
|
||||
entry.getValue().cancel();
|
||||
|
||||
// Remove it from the tuple list
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean populateImageView(final ImageView imgView, final AppView.AppObject obj) {
|
||||
// Cancel pending loads on this image view
|
||||
reapLoaderTuples(imgView);
|
||||
|
||||
// Clear existing contents of the image view
|
||||
imgView.setAlpha(0.0f);
|
||||
|
||||
// Start loading the bitmap
|
||||
WeakReference<ImageView> viewRef = new WeakReference<>(imgView);
|
||||
CachedAppAssetLoader.LoaderTuple tuple = loader.loadBitmapWithContext(obj.app, viewRef, imageViewLoadListener);
|
||||
if (tuple != null) {
|
||||
// The load was issued asynchronously
|
||||
loadingTuples.put(viewRef, tuple);
|
||||
}
|
||||
public boolean populateImageView(ImageView imgView, AppView.AppObject obj) {
|
||||
// Let the cached asset loader handle it
|
||||
loader.populateImageView(obj.app, imgView);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -222,8 +112,4 @@ public class AppGridAdapter extends GenericGridAdapter<AppView.AppObject> {
|
||||
// No overlay
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void fadeInImage(ImageView view) {
|
||||
view.animate().alpha(1.0f).setDuration(100).start();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user