Created
October 24, 2015 20:02
-
-
Save d4rken/35838889b3d6994cea2f to your computer and use it in GitHub Desktop.
A set of classes that was used to load previews in SD Maid during development. Inspired by Picasso, removed in favor of Glide.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package eu.thedarken.sdm.preview.loader; | |
import android.annotation.TargetApi; | |
import android.content.Context; | |
import android.content.pm.ApplicationInfo; | |
import android.content.pm.PackageInfo; | |
import android.content.pm.PackageManager; | |
import android.graphics.Bitmap; | |
import android.graphics.drawable.Drawable; | |
import android.os.Build; | |
import java.io.File; | |
import java.util.Locale; | |
import eu.thedarken.sdm.SDMContext; | |
import eu.thedarken.sdm.SDMaid; | |
import eu.thedarken.sdm.preview.BitmapRequest; | |
import eu.thedarken.sdm.preview.PreviewHelper; | |
public class ApkPreviewLoader extends PreviewLoader { | |
private static final String TAG = SDMaid.LOG_PREFIX + ":Preview:ApkPreviewLoader"; | |
public ApkPreviewLoader(Context context) { | |
super(context); | |
} | |
@TargetApi(Build.VERSION_CODES.LOLLIPOP) | |
private Bitmap buildAppPreview(File file, float[] size) { | |
String filePath = file.getAbsolutePath(); | |
PackageInfo packageInfo = SDMContext.getInstance(getContext()).getIPCFunnel().getPackageArchiveInfo(file, PackageManager.GET_ACTIVITIES); | |
if (packageInfo != null) { | |
ApplicationInfo appInfo = packageInfo.applicationInfo; | |
// http://stackoverflow.com/questions/5674683/how-to-show-icon-of-apk-in-my-file-manager | |
if (Build.VERSION.SDK_INT >= 8) { | |
appInfo.sourceDir = filePath; | |
appInfo.publicSourceDir = filePath; | |
} | |
Drawable icon = null; | |
try { | |
icon = appInfo.loadIcon(getContext().getPackageManager()); | |
} catch (OutOfMemoryError e) { | |
e.printStackTrace(); | |
} | |
if (icon != null) { | |
if (size[0] == 0 || size[1] == 0) { | |
size[0] = icon.getIntrinsicWidth(); | |
size[1] = icon.getIntrinsicHeight(); | |
} | |
return PreviewHelper.getBitmapFromDrawable(icon, (int) size[0], (int) size[1]); | |
} | |
} | |
return null; | |
} | |
@Override | |
public Bitmap build(BitmapRequest request) { | |
return buildAppPreview(new File(request.getUri().getPath()), request.getSize()); | |
} | |
private final static String[] app_extensions = {".apk"}; | |
@Override | |
public boolean isCompatible(BitmapRequest request) { | |
if (!request.getUri().toString().startsWith("file://")) | |
return false; | |
File file = new File(request.getUri().getPath()); | |
int cutoff = file.getName().lastIndexOf("."); | |
if (cutoff != -1) { | |
String extension = file.getName().substring(cutoff); | |
for (String app : app_extensions) { | |
if (extension.toLowerCase(Locale.getDefault()).equals(app.toLowerCase(Locale.getDefault()))) | |
return true; | |
} | |
} | |
return false; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package eu.thedarken.sdm.preview; | |
import android.app.ActivityManager; | |
import android.content.Context; | |
import android.graphics.Bitmap; | |
import android.graphics.BitmapFactory; | |
import android.support.v4.util.LruCache; | |
import java.io.File; | |
import java.io.FileOutputStream; | |
import java.io.IOException; | |
public class BitmapCache { | |
private LruCache<String, Bitmap> cache; | |
private Context mContext; | |
public BitmapCache(Context context) { | |
mContext = context; | |
int memClass = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass(); | |
if (memClass == 0) | |
memClass = 16; | |
// Use 1/8th of the available memory for this memory cache. | |
final int cacheSize = 1024 * 1024 * memClass / 32; | |
cache = new LruCache<String, Bitmap>(cacheSize) { | |
@Override | |
protected int sizeOf(String key, Bitmap bitmap) { | |
// The cache size will be measured in bytes rather than number | |
// of items. | |
return bitmap.getRowBytes() * bitmap.getHeight(); | |
} | |
}; | |
} | |
public synchronized void addBitmapToCache(String key, Bitmap bitmap) { | |
addBitmapToCache(key, bitmap, false); | |
} | |
public synchronized void addBitmapToCache(String key, Bitmap bitmap, boolean cacheExternal) { | |
if (getBitmapFromCache(key, true) == null && key != null && bitmap != null) { | |
cache.put(key, bitmap); | |
if (cacheExternal) { | |
File cachefile = getExternalCacheFile(key); | |
if (cachefile.exists()) | |
cachefile.delete(); | |
if (cachefile.isFile()) { | |
FileOutputStream out; | |
try { | |
out = new FileOutputStream(cachefile); | |
bitmap.compress(Bitmap.CompressFormat.JPEG, 85, out); | |
out.flush(); | |
out.close(); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
} | |
} | |
public Bitmap getBitmapFromCache(String key) { | |
return getBitmapFromCache(key, false); | |
} | |
public Bitmap getBitmapFromCache(String key, boolean tryExternal) { | |
if (cache.get(key) == null) { | |
if (tryExternal) { | |
File img = getExternalCacheFile(key); | |
if (img.exists()) { | |
Bitmap image = BitmapFactory.decodeFile(img.getAbsolutePath()); | |
// Don't cache the image if it is null, i.e. there was a decoding error | |
if (image != null) | |
addBitmapToCache(key, image, false); | |
return image; | |
} | |
} | |
} | |
return cache.get(key); | |
} | |
public void evictAll() { | |
cache.evictAll(); | |
} | |
private File getExternalCacheFile(String tag) { | |
File cacheDir = new File(mContext.getExternalCacheDir() + "/bitmapcache/"); | |
if (!cacheDir.exists()) | |
cacheDir.mkdirs(); | |
return new File(cacheDir.getAbsolutePath() + File.separator + tag); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package eu.thedarken.sdm.preview; | |
import android.net.Uri; | |
import android.support.annotation.NonNull; | |
import java.util.Arrays; | |
public class BitmapRequest { | |
final boolean mCacheExternal; | |
final String mAlias; | |
final float[] mSize; | |
private final Uri mUri; | |
public BitmapRequest(@NonNull Uri uri, boolean cacheExternal, @NonNull String alias, @NonNull float[] size) { | |
mCacheExternal = cacheExternal; | |
mAlias = alias; | |
mSize = size; | |
mUri = uri; | |
} | |
public boolean isCacheExternal() { | |
return mCacheExternal; | |
} | |
public String getAlias() { | |
return mAlias; | |
} | |
public float[] getSize() { | |
return mSize; | |
} | |
@NonNull | |
public Uri getUri() { | |
return mUri; | |
} | |
@Override | |
public boolean equals(Object o) { | |
if (this == o) | |
return true; | |
if (!(o instanceof BitmapRequest)) | |
return false; | |
BitmapRequest foo = (BitmapRequest) o; | |
// if (mCacheExternal != foo.mCacheExternal | |
// || !Arrays.equals(mSize, foo.mSize) | |
// || !mAlias.equals(foo.mAlias) | |
// || !mUri.equals(foo.mUri)) | |
// return false; | |
if(!mAlias.equals(foo.mAlias)) | |
return false; | |
return true; | |
} | |
@Override | |
public int hashCode() { | |
int hash = 17; | |
// hash = hash * 31 + Boolean.valueOf(mCacheExternal).hashCode(); | |
// hash = hash * 31 + Arrays.hashCode(mSize); | |
hash = hash * 31 + mAlias.hashCode(); | |
// hash = hash * 31 + mUri.hashCode(); | |
return hash; | |
} | |
@Override | |
public String toString() { | |
return getUri().toString() + " | alias:" + getAlias(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package eu.thedarken.sdm.preview; | |
import android.view.View; | |
import android.view.ViewTreeObserver; | |
import android.widget.ImageView; | |
import java.lang.ref.WeakReference; | |
public class DeferredRequestBuilder implements ViewTreeObserver.OnPreDrawListener { | |
private static final String TAG = Preview.LOG_PREFIX + "DeferredRequestBuilder"; | |
final RequestBuilder mRequestBuilder; | |
final WeakReference<ImageView> mDestination; | |
public DeferredRequestBuilder(RequestBuilder requestBuilder, ImageView destination) { | |
mRequestBuilder = requestBuilder; | |
destination.getViewTreeObserver().addOnPreDrawListener(this); | |
mDestination = new WeakReference<>(destination); | |
if (destination.getVisibility() == View.GONE) | |
destination.setVisibility(View.INVISIBLE); | |
} | |
@Override | |
public boolean onPreDraw() { | |
View destination = mDestination.get(); | |
if (destination == null) | |
return true; | |
ViewTreeObserver observer = destination.getViewTreeObserver(); | |
if (!observer.isAlive()) | |
return true; | |
int width = destination.getWidth(); | |
int height = destination.getHeight(); | |
if (width <= 0 || height <= 0) | |
return true; | |
observer.removeOnPreDrawListener(this); | |
mRequestBuilder.go(); | |
return true; | |
} | |
void cancel() { | |
View destination = mDestination.get(); | |
if (destination == null) | |
return; | |
ViewTreeObserver observer = destination.getViewTreeObserver(); | |
if (!observer.isAlive()) | |
return; | |
observer.removeOnPreDrawListener(this); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package eu.thedarken.sdm.preview.loader; | |
import android.content.Context; | |
import android.graphics.Bitmap; | |
import android.graphics.BitmapFactory; | |
import java.io.File; | |
import java.util.Locale; | |
import eu.thedarken.sdm.preview.BitmapRequest; | |
public class ImagePreviewLoader extends PreviewLoader { | |
private final static String[] image_extensions = {".jpg", ".jpeg", ".gif", ".bmp", ".png", ".raw"}; | |
public ImagePreviewLoader(Context context) { | |
super(context); | |
} | |
@Override | |
public boolean isCompatible(BitmapRequest request) { | |
if (!request.getUri().toString().startsWith("file://")) | |
return false; | |
File file = new File(request.getUri().getPath()); | |
int cutoff = file.getName().lastIndexOf("."); | |
if (cutoff != -1) { | |
String extension = file.getName().substring(cutoff); | |
for (String img : image_extensions) { | |
if (extension.toLowerCase(Locale.getDefault()).equals(img.toLowerCase(Locale.getDefault()))) | |
return true; | |
} | |
} | |
return false; | |
} | |
@Override | |
public Bitmap build(BitmapRequest request) { | |
File file = new File(request.getUri().getPath()); | |
return buildImagePreview(file, request.getSize()); | |
} | |
private Bitmap buildImagePreview(File file, float size[]) { | |
final BitmapFactory.Options options = new BitmapFactory.Options(); | |
options.inJustDecodeBounds = true; | |
BitmapFactory.decodeFile(file.getAbsolutePath(), options); | |
if (size[0] == 0 || size[1] == 0) { | |
size[0] = options.outWidth; | |
size[1] = options.outHeight; | |
} | |
options.inSampleSize = calculateInSampleSize(options, (int) size[0], (int) size[1]); | |
options.inJustDecodeBounds = false; | |
return BitmapFactory.decodeFile(file.getAbsolutePath(), options); | |
} | |
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { | |
final int height = options.outHeight; | |
final int width = options.outWidth; | |
int inSampleSize = 1; | |
if (height > reqHeight || width > reqWidth) { | |
final int heightRatio = Math.round((float) height / (float) reqHeight); | |
final int widthRatio = Math.round((float) width / (float) reqWidth); | |
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; | |
} | |
return inSampleSize; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package eu.thedarken.sdm.preview; | |
import android.content.Context; | |
import android.graphics.Bitmap; | |
import android.net.Uri; | |
import android.os.Handler; | |
import android.os.Looper; | |
import android.support.annotation.NonNull; | |
import android.view.View; | |
import android.widget.ImageView; | |
import java.io.File; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.Map; | |
import java.util.Set; | |
import java.util.WeakHashMap; | |
import eu.thedarken.sdm.SDMaid; | |
import eu.thedarken.sdm.tools.Logy; | |
public class Preview implements PreviewCallback { | |
private static final String TAG = SDMaid.LOG_PREFIX + "Preview"; | |
public static final String LOG_PREFIX = TAG + ":"; | |
private final Object mAccessLock = new Object(); | |
private Context mContext; | |
private final BitmapCache mCache; | |
private final PreviewWorker mWorker; | |
private volatile static Preview sInstance; | |
private final Map<ImageView, PreviewTask> mImageTargetMap = new WeakHashMap<>(); | |
private final HashMap<BitmapRequest, Set<PreviewTask>> mBitMapRequestMap = new HashMap<>(); | |
private final Map<ImageView, DeferredRequestBuilder> mDeferredRequestMap = new WeakHashMap<>(); | |
public static Preview with(Context c) { | |
if (sInstance == null) { | |
synchronized (Preview.class) { | |
if (sInstance == null) | |
sInstance = new Preview(c.getApplicationContext()); | |
} | |
} | |
return sInstance; | |
} | |
private Preview(Context m) { | |
mContext = m; | |
mCache = new BitmapCache(m); | |
mWorker = new PreviewWorker(mContext, mCache, this); | |
} | |
Context getContext() { | |
return mContext; | |
} | |
BitmapCache getCache() { | |
return mCache; | |
} | |
public RequestBuilder load(File file) { | |
return load(Uri.fromFile(file)); | |
} | |
public RequestBuilder load(String pkg) { | |
return load(Uri.parse("package://" + pkg)); | |
} | |
public RequestBuilder load(Uri uri) { | |
RequestBuilder builder = new RequestBuilder(this, uri); | |
return builder; | |
} | |
public boolean isLoading() { | |
return mWorker.isRunning(); | |
} | |
public void clearCache() { | |
Logy.d(TAG, "Clearing data..."); | |
mCache.evictAll(); | |
} | |
public void stopLoading() { | |
Logy.d(TAG, "Stopping loaders..."); | |
synchronized (mAccessLock) { | |
mWorker.stop(); | |
mImageTargetMap.clear(); | |
mImageTargetMap.clear(); | |
for (DeferredRequestBuilder deferredRequestBuilder : mDeferredRequestMap.values()) | |
deferredRequestBuilder.cancel(); | |
mDeferredRequestMap.clear(); | |
} | |
} | |
void submit(PreviewTask previewTask) { | |
ensureUIThread(); | |
BitmapRequest newBitmapRequest = previewTask.getBitmapRequest(); | |
ImageView destination = previewTask.getDestination(); | |
synchronized (mAccessLock) { | |
if (destination != null) { | |
cancelRequest(destination); | |
mImageTargetMap.put(destination, previewTask); | |
} | |
Set<PreviewTask> tasksForThisBitmap = mBitMapRequestMap.get(newBitmapRequest); | |
if (tasksForThisBitmap == null) | |
tasksForThisBitmap = new HashSet<>(); | |
tasksForThisBitmap.add(previewTask); | |
mBitMapRequestMap.put(newBitmapRequest, tasksForThisBitmap); | |
Logy.v(TAG, "Submitted: " + previewTask.getDestination().toString() + " | " + newBitmapRequest.toString()); | |
} | |
mWorker.submit(newBitmapRequest); | |
} | |
public void cancelRequest(@NonNull ImageView destination) { | |
ensureUIThread(); | |
synchronized (mAccessLock) { | |
PreviewTask obsoleteTask = mImageTargetMap.remove(destination); | |
if (obsoleteTask != null) { | |
BitmapRequest possiblyObsoleteBitmapRequest = obsoleteTask.getBitmapRequest(); | |
final Set<PreviewTask> previewTasks = mBitMapRequestMap.get(possiblyObsoleteBitmapRequest); | |
if (previewTasks != null) { | |
if (previewTasks.remove(obsoleteTask)) | |
Logy.v(TAG, "Removed obsolete task."); | |
if (previewTasks.isEmpty()) { | |
Logy.v(TAG, "BitmapRequest has no associated tasks. Canceling it."); | |
mBitMapRequestMap.remove(possiblyObsoleteBitmapRequest); | |
mWorker.cancel(possiblyObsoleteBitmapRequest); | |
} | |
} | |
} | |
DeferredRequestBuilder deferredBuilder = mDeferredRequestMap.remove(destination); | |
if (deferredBuilder != null) | |
deferredBuilder.cancel(); | |
} | |
} | |
protected void deferRequest(DeferredRequestBuilder deferred) { | |
ensureUIThread(); | |
synchronized (mAccessLock) { | |
ImageView destination = deferred.mDestination.get(); | |
if (mDeferredRequestMap.containsKey(destination)) | |
cancelRequest(destination); | |
mDeferredRequestMap.put(destination, deferred); | |
Logy.v(TAG, "Deferred:" + deferred); | |
} | |
} | |
@Override | |
public void onBitmapRequestDone(final BitmapRequest request, final Bitmap bitmap) { | |
Logy.v(TAG, "Postprocessing:" + request.toString()); | |
Set<PreviewTask> previewTasks; | |
synchronized (mAccessLock) { | |
previewTasks = mBitMapRequestMap.remove(request); | |
if (previewTasks == null) { | |
Logy.v(TAG, "Preview tasks were invalidated for this bitmap:" + request.toString()); | |
return; | |
} | |
for (PreviewTask task : previewTasks) { | |
mImageTargetMap.remove(task.getDestination()); | |
} | |
} | |
if (previewTasks.isEmpty()) | |
Logy.v(TAG, "previewtasks were empty for:" + request.toString()); | |
Handler handler = new Handler(Looper.getMainLooper()); | |
for (final PreviewTask task : previewTasks) { | |
final ImageView destination = task.getDestination(); | |
if (destination != null) { | |
if (handler.post(new Runnable() { | |
@Override | |
public void run() { | |
Logy.v(TAG, "detination:" + destination.toString() + " request:" + request.getAlias()); | |
destination.setImageBitmap(bitmap); | |
destination.setVisibility(View.VISIBLE); | |
} | |
})) { | |
Logy.v(TAG, "destination posted:" + destination.toString()); | |
if (destination.getHandler() == null) | |
Logy.v(TAG, "destination handler null:" + destination.toString()); | |
} else { | |
Logy.v(TAG, "destination failed:" + destination.toString()); | |
} | |
} else { | |
Logy.v(TAG, "detination was null, request:" + request.getAlias()); | |
} | |
final View placeHolder = task.getPlaceHolder(); | |
if (placeHolder != null) { | |
if (handler.post(new Runnable() { | |
@Override | |
public void run() { | |
Logy.v(TAG, "placeHolder:" + placeHolder.toString() + " request:" + request.getAlias()); | |
placeHolder.setVisibility(View.INVISIBLE); | |
} | |
})) { | |
Logy.v(TAG, "placeHolder posted:" + placeHolder.toString()); | |
} else { | |
Logy.v(TAG, "placeHolder failed:" + placeHolder.toString()); | |
} | |
} else { | |
Logy.v(TAG, "placeHolder was null, request:" + request.getAlias()); | |
} | |
Logy.v(TAG, "Preview tasks completed:" + task.toString()); | |
} | |
} | |
static void ensureUIThread() { | |
if (Looper.getMainLooper().getThread() != Thread.currentThread()) | |
throw new IllegalStateException("Not called from UI thread!"); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package eu.thedarken.sdm.preview; | |
import android.graphics.Bitmap; | |
public interface PreviewCallback { | |
void onBitmapRequestDone(BitmapRequest bitmapRequest, Bitmap bitmap); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package eu.thedarken.sdm.preview; | |
import android.annotation.TargetApi; | |
import android.graphics.Bitmap; | |
import android.graphics.Canvas; | |
import android.graphics.drawable.BitmapDrawable; | |
import android.graphics.drawable.Drawable; | |
import android.graphics.drawable.DrawableContainer; | |
import android.graphics.drawable.StateListDrawable; | |
import android.graphics.drawable.VectorDrawable; | |
import android.os.Build; | |
import eu.thedarken.sdm.tools.ApiHelper; | |
public class PreviewHelper { | |
@TargetApi(Build.VERSION_CODES.LOLLIPOP) | |
public static Bitmap getBitmapFromDrawable(Drawable drawable, int width, int height) { | |
if (drawable instanceof BitmapDrawable) { | |
return Bitmap.createScaledBitmap(((BitmapDrawable) drawable).getBitmap(), width, height, true); | |
} else if (drawable instanceof StateListDrawable) { | |
StateListDrawable stateDrwbl = (StateListDrawable) drawable; | |
stateDrwbl.mutate(); | |
Drawable.ConstantState constantState = stateDrwbl.getConstantState(); | |
if (constantState instanceof DrawableContainer.DrawableContainerState) { | |
DrawableContainer.DrawableContainerState drwblContainerState = (DrawableContainer.DrawableContainerState) constantState; | |
final Drawable[] drawables = drwblContainerState.getChildren(); | |
for (Drawable drwbl : drawables) { | |
if (drwbl instanceof BitmapDrawable) | |
return Bitmap.createScaledBitmap(((BitmapDrawable) drwbl).getBitmap(), width, height, true); | |
} | |
} | |
} else if (ApiHelper.hasLolliPop() && drawable instanceof VectorDrawable) { | |
VectorDrawable vIcon = (VectorDrawable) drawable; | |
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); | |
Canvas canvas = new Canvas(bitmap); | |
vIcon.setBounds(0, 0, width, height); | |
vIcon.draw(canvas); | |
return bitmap; | |
} | |
return null; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package eu.thedarken.sdm.preview; | |
import android.view.View; | |
import android.widget.ImageView; | |
import java.lang.ref.ReferenceQueue; | |
import java.lang.ref.WeakReference; | |
class PreviewTask { | |
private final BitmapRequest mBitmapRequest; | |
private final WeakReference<ImageView> mDestination; | |
private final WeakReference<View> mPlaceHolder; | |
private final long mTimeStamp; | |
PreviewTask(BitmapRequest bitmapRequest, ImageView destination, View placeHolder) { | |
mTimeStamp = System.nanoTime(); | |
mBitmapRequest = bitmapRequest; | |
if (destination != null) { | |
mDestination = new WeakReference<>(destination); | |
} else { | |
mDestination = null; | |
} | |
if (placeHolder != null) { | |
mPlaceHolder = new WeakReference<>(placeHolder); | |
} else { | |
mPlaceHolder = null; | |
} | |
} | |
public long getTimeStamp() { | |
return mTimeStamp; | |
} | |
public BitmapRequest getBitmapRequest() { | |
return mBitmapRequest; | |
} | |
public ImageView getDestination() { | |
return mDestination == null ? null : mDestination.get(); | |
} | |
public View getPlaceHolder() { | |
return mPlaceHolder == null ? null : mPlaceHolder.get(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package eu.thedarken.sdm.preview; | |
import android.content.Context; | |
import android.graphics.Bitmap; | |
import java.util.ArrayList; | |
import java.util.HashSet; | |
import java.util.List; | |
import java.util.Set; | |
import java.util.Stack; | |
import java.util.concurrent.ExecutorService; | |
import java.util.concurrent.Executors; | |
import java.util.concurrent.atomic.AtomicInteger; | |
import eu.thedarken.sdm.SDMaid; | |
import eu.thedarken.sdm.preview.loader.ApkPreviewLoader; | |
import eu.thedarken.sdm.preview.loader.AppPreviewLoader; | |
import eu.thedarken.sdm.preview.loader.FallbackPreviewLoader; | |
import eu.thedarken.sdm.preview.loader.ImagePreviewLoader; | |
import eu.thedarken.sdm.preview.loader.PreviewLoader; | |
import eu.thedarken.sdm.tools.Logy; | |
public class PreviewWorker { | |
private static final String TAG = SDMaid.LOG_PREFIX + "PreviewWorker"; | |
private final List<PreviewLoader> mPreviewLoaders = new ArrayList<>(); | |
private final Context mContext; | |
private final PreviewCallback mListener; | |
private final BitmapCache mCache; | |
private final AtomicInteger mRunningWorkers = new AtomicInteger(0); | |
private final Stack<BitmapRequest> mRequestOrder = new Stack<>(); | |
private final Set<BitmapRequest> mActiveRequests = new HashSet<>(); | |
private final Object mRequestLock = new Object(); | |
private final ExecutorService mThreadPool = Executors.newFixedThreadPool(MAXWORKERS); | |
private static final int MAXWORKERS = 3; | |
public PreviewWorker(Context context, BitmapCache cache, PreviewCallback listener) { | |
mContext = context; | |
mListener = listener; | |
mCache = cache; | |
mPreviewLoaders.add(new ImagePreviewLoader(mContext)); | |
mPreviewLoaders.add(new ApkPreviewLoader(mContext)); | |
mPreviewLoaders.add(new AppPreviewLoader(mContext)); | |
mPreviewLoaders.add(new FallbackPreviewLoader(mContext)); | |
} | |
private PreviewLoader determineLoader(BitmapRequest request) { | |
for (PreviewLoader loader : mPreviewLoaders) | |
if (loader.isCompatible(request)) | |
return loader; | |
return null; | |
} | |
public boolean isRunning() { | |
return mRunningWorkers.get() > 0; | |
} | |
public void stop() { | |
mRequestOrder.clear(); | |
} | |
public Set<BitmapRequest> getActiveRequests() { | |
return mActiveRequests; | |
} | |
public void submit(BitmapRequest bitmapRequest) { | |
Bitmap bitmap = mCache.getBitmapFromCache(bitmapRequest.getAlias()); | |
if (bitmap != null) { | |
Logy.v(TAG, "Quick return:" + bitmapRequest.toString()); | |
mListener.onBitmapRequestDone(bitmapRequest, bitmap); | |
return; | |
} | |
synchronized (mRequestLock) { | |
if (mActiveRequests.contains(bitmapRequest)) | |
return; | |
if (mRequestOrder.contains(bitmapRequest)) | |
mRequestOrder.remove(bitmapRequest); | |
mRequestOrder.push(bitmapRequest); | |
if (mRunningWorkers.get() < MAXWORKERS) { | |
mRunningWorkers.incrementAndGet(); | |
mThreadPool.submit(new WorkerLoop()); | |
} | |
} | |
} | |
public void cancel(BitmapRequest request) { | |
synchronized (mRequestLock) { | |
if (mRequestOrder.remove(request)) | |
Logy.v(TAG, "Removed request:" + request.toString()); | |
} | |
} | |
private class WorkerLoop implements Runnable { | |
@Override | |
public void run() { | |
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_LOWEST); | |
while (true) { | |
BitmapRequest bitmapRequest; | |
synchronized (mRequestLock) { | |
if (mRequestOrder.isEmpty()) { | |
break; | |
} else { | |
bitmapRequest = mRequestOrder.pop(); | |
mActiveRequests.add(bitmapRequest); | |
} | |
} | |
String targetTag = bitmapRequest.getAlias(); | |
boolean cacheExternal = bitmapRequest.isCacheExternal(); | |
Bitmap previewBitmap = mCache.getBitmapFromCache(targetTag, cacheExternal); | |
if (previewBitmap == null) { | |
Logy.v(TAG, "Loading new bitmap for " + bitmapRequest.toString()); | |
PreviewLoader loader = determineLoader(bitmapRequest); | |
if (loader != null) { | |
previewBitmap = loader.build(bitmapRequest); | |
if (previewBitmap != null) | |
mCache.addBitmapToCache(targetTag, previewBitmap, cacheExternal); | |
} | |
} else { | |
Logy.v(TAG, "Using cached bitmap of " + bitmapRequest.toString()); | |
} | |
synchronized (mRequestLock) { | |
mActiveRequests.remove(bitmapRequest); | |
} | |
Logy.v(TAG, "Processed: " + bitmapRequest.getAlias() + ", source " + bitmapRequest.getUri()); | |
mListener.onBitmapRequestDone(bitmapRequest, previewBitmap); | |
} | |
mRunningWorkers.decrementAndGet(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package eu.thedarken.sdm.preview; | |
import android.content.Context; | |
import android.content.res.Resources; | |
import android.net.Uri; | |
import android.support.annotation.DimenRes; | |
import android.support.annotation.NonNull; | |
import android.view.View; | |
import android.widget.ImageView; | |
import java.lang.ref.WeakReference; | |
import eu.thedarken.sdm.SDMaid; | |
public class RequestBuilder { | |
private static final String TAG = SDMaid.LOG_PREFIX + "RequestBuilder"; | |
private final Context mContext; | |
private final Preview mPreviewInstance; | |
private final Uri mTargetUri; | |
private String mTargetAlias; | |
private float[] mDesiredTargetSize; | |
private WeakReference<ImageView> mDestination; | |
private WeakReference<View> mPlaceHolder; | |
public RequestBuilder(Preview loader, @NonNull Uri uri) { | |
mPreviewInstance = loader; | |
mContext = loader.getContext(); | |
mTargetUri = uri; | |
} | |
public RequestBuilder withCustomAlias(String alias) { | |
mTargetAlias = alias; | |
return this; | |
} | |
public RequestBuilder inSize(@DimenRes int sizeRes) { | |
Resources r = mContext.getResources(); | |
float calcDpi = r.getDimension(sizeRes); | |
mDesiredTargetSize = new float[]{calcDpi, calcDpi}; | |
return this; | |
} | |
public RequestBuilder into(ImageView destination) { | |
mDestination = new WeakReference<>(destination); | |
return this; | |
} | |
public RequestBuilder withPlaceholder(View placeholder) { | |
mPlaceHolder = new WeakReference<>(placeholder); | |
return this; | |
} | |
private boolean ensureSize() { | |
if (mDesiredTargetSize == null) { | |
float[] size = null; | |
ImageView dest = mDestination.get(); | |
if (dest != null) { | |
int width = dest.getWidth(); | |
int height = dest.getHeight(); | |
if (width != 0 && height != 0) { | |
size = new float[]{ | |
width, height | |
}; | |
} | |
if (size != null) { | |
mDesiredTargetSize = size; | |
} else { | |
mPreviewInstance.deferRequest(new DeferredRequestBuilder(this, dest)); | |
return false; | |
} | |
} else { | |
mDesiredTargetSize = new float[]{0, 0}; | |
} | |
} | |
return true; | |
} | |
public void go() { | |
final ImageView destination = mDestination.get(); | |
if (destination != null) { | |
Preview.ensureUIThread(); | |
destination.setImageBitmap(null); | |
destination.setVisibility(View.INVISIBLE); | |
} | |
final View placeHolder = mPlaceHolder.get(); | |
if (placeHolder != null) { | |
Preview.ensureUIThread(); | |
placeHolder.setVisibility(View.VISIBLE); | |
} | |
if (!ensureSize()) | |
return; | |
if (mTargetAlias == null) { | |
mTargetAlias = mTargetUri.toString() + ":" + mDesiredTargetSize[0] + "x" + mDesiredTargetSize[1]; | |
} | |
BitmapRequest bitmapRequest = new BitmapRequest(mTargetUri, false, mTargetAlias, mDesiredTargetSize); | |
PreviewTask previewTask = new PreviewTask(bitmapRequest, destination, placeHolder); | |
mPreviewInstance.submit(previewTask); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment