mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
Image background is drawn first not after bitmap. (#57)
Image file cache is used only for images downloaded through web. Image cache is now caching Bitmap instead of BitmapDrawables (drawables are not immutable).
This commit is contained in:
@@ -19,10 +19,8 @@ package org.nativescript.widgets.image;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.CompressFormat;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.os.Environment;
|
||||
import android.os.StatFs;
|
||||
@@ -30,11 +28,6 @@ import android.support.v4.util.LruCache;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
@@ -56,32 +49,16 @@ public class Cache {
|
||||
// Default memory cache size in kilobytes
|
||||
private static final int DEFAULT_MEM_CACHE_SIZE = 1024 * 5; // 5MB
|
||||
|
||||
// Default disk cache size in bytes
|
||||
private static final int DEFAULT_DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
|
||||
|
||||
// Compression settings when writing images to disk cache
|
||||
private static final CompressFormat DEFAULT_COMPRESS_FORMAT = CompressFormat.JPEG;
|
||||
private static final int DEFAULT_COMPRESS_QUALITY = 70;
|
||||
private static final int DISK_CACHE_INDEX = 0;
|
||||
|
||||
// Constants to easily toggle various caches
|
||||
private static final boolean DEFAULT_MEM_CACHE_ENABLED = true;
|
||||
private static final boolean DEFAULT_DISK_CACHE_ENABLED = true;
|
||||
private static final boolean DEFAULT_INIT_DISK_CACHE_ON_CREATE = false;
|
||||
|
||||
private static final String IMAGE_CACHE_DIR = "images";
|
||||
|
||||
private static Cache instance;
|
||||
private DiskLruCache mDiskLruCache;
|
||||
private LruCache<String, BitmapDrawable> mMemoryCache;
|
||||
private LruCache<String, Bitmap> mMemoryCache;
|
||||
private CacheParams mParams;
|
||||
private final Object mDiskCacheLock = new Object();
|
||||
private boolean mDiskCacheStarting = true;
|
||||
|
||||
private Set<SoftReference<Bitmap>> mReusableBitmaps;
|
||||
|
||||
private Fetcher mFetcher;
|
||||
|
||||
/**
|
||||
* Create a new Cache object using the specified parameters. This should not be
|
||||
* called directly by other classes, instead use
|
||||
@@ -102,7 +79,6 @@ public class Cache {
|
||||
public static Cache getInstance(CacheParams cacheParams) {
|
||||
if (instance == null) {
|
||||
instance = new Cache(cacheParams);
|
||||
instance.init(cacheParams);
|
||||
}
|
||||
else if (instance.mParams != cacheParams) {
|
||||
instance.init(cacheParams);
|
||||
@@ -119,7 +95,6 @@ public class Cache {
|
||||
private void init(CacheParams cacheParams) {
|
||||
mParams = cacheParams;
|
||||
|
||||
//BEGIN_INCLUDE(init_memory_cache)
|
||||
// Set up memory cache
|
||||
if (mParams.memoryCacheEnabled) {
|
||||
if (Worker.debuggable > 0) {
|
||||
@@ -140,18 +115,18 @@ public class Cache {
|
||||
Collections.synchronizedSet(new HashSet<SoftReference<Bitmap>>());
|
||||
}
|
||||
|
||||
mMemoryCache = new LruCache<String, BitmapDrawable>(mParams.memCacheSize) {
|
||||
mMemoryCache = new LruCache<String, Bitmap>(mParams.memCacheSize) {
|
||||
|
||||
/**
|
||||
* Notify the removed entry that is no longer being cached
|
||||
*/
|
||||
@Override
|
||||
protected void entryRemoved(boolean evicted, String key,
|
||||
BitmapDrawable oldValue, BitmapDrawable newValue) {
|
||||
Bitmap oldValue, Bitmap newValue) {
|
||||
if (Utils.hasHoneycomb()) {
|
||||
// We're running on Honeycomb or later, so add the bitmap
|
||||
// to a SoftReference set for possible use with inBitmap later
|
||||
mReusableBitmaps.add(new SoftReference<Bitmap>(oldValue.getBitmap()));
|
||||
mReusableBitmaps.add(new SoftReference<Bitmap>(oldValue));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,54 +135,12 @@ public class Cache {
|
||||
* for a bitmap cache
|
||||
*/
|
||||
@Override
|
||||
protected int sizeOf(String key, BitmapDrawable value) {
|
||||
protected int sizeOf(String key, Bitmap value) {
|
||||
final int bitmapSize = getBitmapSize(value) / 1024;
|
||||
return bitmapSize == 0 ? 1 : bitmapSize;
|
||||
}
|
||||
};
|
||||
}
|
||||
//END_INCLUDE(init_memory_cache)
|
||||
|
||||
// // By default the disk cache is not initialized here as it should be initialized
|
||||
// // on a separate thread due to disk access.
|
||||
// if (cacheParams.initDiskCacheOnCreate) {
|
||||
// // Set up disk cache
|
||||
// initDiskCache();
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the disk cache. Note that this includes disk access so this should not be
|
||||
* executed on the main/UI thread. By default an Cache does not initialize the disk
|
||||
* cache when it is created, instead you should call initDiskCache() to initialize it on a
|
||||
* background thread.
|
||||
*/
|
||||
public void initDiskCache() {
|
||||
// Set up disk cache
|
||||
synchronized (mDiskCacheLock) {
|
||||
if (mDiskLruCache == null || mDiskLruCache.isClosed()) {
|
||||
File diskCacheDir = mParams.diskCacheDir;
|
||||
if (mParams.diskCacheEnabled && diskCacheDir != null) {
|
||||
if (!diskCacheDir.exists()) {
|
||||
diskCacheDir.mkdirs();
|
||||
}
|
||||
if (getUsableSpace(diskCacheDir) > mParams.diskCacheSize) {
|
||||
try {
|
||||
mDiskLruCache = DiskLruCache.open(
|
||||
diskCacheDir, 1, 1, mParams.diskCacheSize);
|
||||
if (Worker.debuggable > 0) {
|
||||
Log.v(TAG, "Disk cache initialized");
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
mParams.diskCacheDir = null;
|
||||
Log.e(TAG, "initDiskCache - " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mDiskCacheStarting = false;
|
||||
mDiskCacheLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -216,66 +149,31 @@ public class Cache {
|
||||
* @param data Unique identifier for the bitmap to store
|
||||
* @param value The bitmap drawable to store
|
||||
*/
|
||||
public void addBitmapToCache(String data, BitmapDrawable value, boolean useDiskCache) {
|
||||
//BEGIN_INCLUDE(add_bitmap_to_cache)
|
||||
public void addBitmapToCache(String data, Bitmap value) {
|
||||
if (data == null || value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add to memory cache
|
||||
if (mMemoryCache != null) {
|
||||
Bitmap currentValue = mMemoryCache.get(data);
|
||||
// NOTE: If we have existing we probably loaded it sync so we don't want to add the new one,
|
||||
// because this will make the previous bitmap free for reuse but it is used somewhere.
|
||||
// Probably won't happen often.
|
||||
if (currentValue == null) {
|
||||
mMemoryCache.put(data, value);
|
||||
}
|
||||
|
||||
if (!useDiskCache) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mDiskCacheLock) {
|
||||
// Add to disk cache
|
||||
if (mDiskLruCache != null) {
|
||||
final String key = hashKeyForDisk(data);
|
||||
OutputStream out = null;
|
||||
try {
|
||||
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
|
||||
if (snapshot == null) {
|
||||
final DiskLruCache.Editor editor = mDiskLruCache.edit(key);
|
||||
if (editor != null) {
|
||||
out = editor.newOutputStream(DISK_CACHE_INDEX);
|
||||
value.getBitmap().compress(
|
||||
mParams.compressFormat, mParams.compressQuality, out);
|
||||
editor.commit();
|
||||
out.close();
|
||||
}
|
||||
} else {
|
||||
snapshot.getInputStream(DISK_CACHE_INDEX).close();
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
Log.e(TAG, "addBitmapToCache - " + e);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "addBitmapToCache - " + e);
|
||||
} finally {
|
||||
try {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//END_INCLUDE(add_bitmap_to_cache)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get from memory cache.
|
||||
*
|
||||
* @param data Unique identifier for which item to get
|
||||
* @return The bitmap drawable if found in cache, null otherwise
|
||||
* @return The bitmap if found in cache, null otherwise
|
||||
*/
|
||||
public BitmapDrawable getBitmapFromMemCache(String data) {
|
||||
//BEGIN_INCLUDE(get_bitmap_from_mem_cache)
|
||||
BitmapDrawable memValue = null;
|
||||
public Bitmap getBitmapFromMemCache(String data) {
|
||||
Bitmap memValue = null;
|
||||
|
||||
if (mMemoryCache != null) {
|
||||
memValue = mMemoryCache.get(data);
|
||||
@@ -286,59 +184,6 @@ public class Cache {
|
||||
}
|
||||
|
||||
return memValue;
|
||||
//END_INCLUDE(get_bitmap_from_mem_cache)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get from disk cache.
|
||||
*
|
||||
* @param data Unique identifier for which item to get
|
||||
* @return The bitmap if found in cache, null otherwise
|
||||
*/
|
||||
public Bitmap getBitmapFromDiskCache(String data) {
|
||||
//BEGIN_INCLUDE(get_bitmap_from_disk_cache)
|
||||
final String key = hashKeyForDisk(data);
|
||||
Bitmap bitmap = null;
|
||||
|
||||
synchronized (mDiskCacheLock) {
|
||||
while (mDiskCacheStarting) {
|
||||
try {
|
||||
mDiskCacheLock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
if (mDiskLruCache != null) {
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
final DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
|
||||
if (snapshot != null) {
|
||||
if (Worker.debuggable > 0) {
|
||||
Log.v(TAG, "Disk cache hit");
|
||||
}
|
||||
inputStream = snapshot.getInputStream(DISK_CACHE_INDEX);
|
||||
if (inputStream != null) {
|
||||
FileDescriptor fd = ((FileInputStream) inputStream).getFD();
|
||||
|
||||
// Decode bitmap, but we don't want to sample so give
|
||||
// MAX_VALUE as the target dimensions
|
||||
bitmap = Resizer.decodeSampledBitmapFromDescriptor(
|
||||
fd, Integer.MAX_VALUE, Integer.MAX_VALUE, this);
|
||||
}
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
Log.e(TAG, "getBitmapFromDiskCache - " + e);
|
||||
} finally {
|
||||
try {
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
//END_INCLUDE(get_bitmap_from_disk_cache)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -389,63 +234,6 @@ public class Cache {
|
||||
Log.v(TAG, "Memory cache cleared");
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (mDiskCacheLock) {
|
||||
mDiskCacheStarting = true;
|
||||
if (mDiskLruCache != null && !mDiskLruCache.isClosed()) {
|
||||
try {
|
||||
mDiskLruCache.delete();
|
||||
if (Worker.debuggable > 0) {
|
||||
Log.v(TAG, "Disk cache cleared");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "clearCache - " + e);
|
||||
}
|
||||
mDiskLruCache = null;
|
||||
// initDiskCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the disk cache associated with this Cache object. Note that this includes
|
||||
* disk access so this should not be executed on the main/UI thread.
|
||||
*/
|
||||
public void flush() {
|
||||
synchronized (mDiskCacheLock) {
|
||||
if (mDiskLruCache != null) {
|
||||
try {
|
||||
mDiskLruCache.flush();
|
||||
if (Worker.debuggable > 0) {
|
||||
Log.v(TAG, "Disk cache flushed");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "flush - " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the disk cache associated with this Cache object. Note that this includes
|
||||
* disk access so this should not be executed on the main/UI thread.
|
||||
*/
|
||||
public void close() {
|
||||
synchronized (mDiskCacheLock) {
|
||||
if (mDiskLruCache != null) {
|
||||
try {
|
||||
if (!mDiskLruCache.isClosed()) {
|
||||
mDiskLruCache.close();
|
||||
mDiskLruCache = null;
|
||||
if (Worker.debuggable > 0) {
|
||||
Log.v(TAG, "Disk cache closed");
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "close - " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -453,10 +241,6 @@ public class Cache {
|
||||
*/
|
||||
public static class CacheParams {
|
||||
public int memCacheSize = DEFAULT_MEM_CACHE_SIZE;
|
||||
public int diskCacheSize = DEFAULT_DISK_CACHE_SIZE;
|
||||
public File diskCacheDir;
|
||||
public CompressFormat compressFormat = DEFAULT_COMPRESS_FORMAT;
|
||||
public int compressQuality = DEFAULT_COMPRESS_QUALITY;
|
||||
public boolean memoryCacheEnabled = DEFAULT_MEM_CACHE_ENABLED;
|
||||
public boolean diskCacheEnabled = DEFAULT_DISK_CACHE_ENABLED;
|
||||
|
||||
@@ -470,7 +254,6 @@ public class Cache {
|
||||
* is sufficient.
|
||||
*/
|
||||
public CacheParams(Context context, String diskCacheDirectoryName) {
|
||||
diskCacheDir = getDiskCacheDir(context, diskCacheDirectoryName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -592,13 +375,11 @@ public class Cache {
|
||||
* onward this returns the allocated memory size of the bitmap which can be larger than the
|
||||
* actual bitmap data byte count (in the case it was re-used).
|
||||
*
|
||||
* @param value
|
||||
* @param bitmap
|
||||
* @return size in bytes
|
||||
*/
|
||||
@TargetApi(VERSION_CODES.KITKAT)
|
||||
public static int getBitmapSize(BitmapDrawable value) {
|
||||
Bitmap bitmap = value.getBitmap();
|
||||
|
||||
public static int getBitmapSize(Bitmap bitmap) {
|
||||
// From KitKat onward use getAllocationByteCount() as allocated bytes can potentially be
|
||||
// larger than bitmap byte count.
|
||||
if (Utils.hasKitKat()) {
|
||||
|
||||
@@ -18,8 +18,6 @@ package org.nativescript.widgets.image;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -28,6 +26,7 @@ import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
@@ -37,14 +36,12 @@ import java.net.URL;
|
||||
* A simple subclass of {@link Resizer} that fetch and resize images from a file, resource or URL.
|
||||
*/
|
||||
public class Fetcher extends Resizer {
|
||||
private static final String RESOURCE_PREFIX = "res://";
|
||||
private static final String FILE_PREFIX = "file:///";
|
||||
private static final int HTTP_CACHE_SIZE = 10 * 1024 * 1024; // 10MB
|
||||
private static final String HTTP_CACHE_DIR = "http";
|
||||
private static final int IO_BUFFER_SIZE = 8 * 1024;
|
||||
|
||||
private DiskLruCache mHttpDiskCache;
|
||||
private File mHttpCacheDir;
|
||||
private DiskLruCache mHttpDiskCache;
|
||||
private boolean mHttpDiskCacheStarting = true;
|
||||
private final Object mHttpDiskCacheLock = new Object();
|
||||
private static final int DISK_CACHE_INDEX = 0;
|
||||
@@ -65,11 +62,6 @@ public class Fetcher extends Resizer {
|
||||
|
||||
@Override
|
||||
protected void initDiskCacheInternal() {
|
||||
super.initDiskCacheInternal();
|
||||
initHttpDiskCache();
|
||||
}
|
||||
|
||||
private void initHttpDiskCache() {
|
||||
if (!mHttpCacheDir.exists()) {
|
||||
mHttpCacheDir.mkdirs();
|
||||
}
|
||||
@@ -104,14 +96,12 @@ public class Fetcher extends Resizer {
|
||||
}
|
||||
mHttpDiskCache = null;
|
||||
mHttpDiskCacheStarting = true;
|
||||
// initHttpDiskCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void flushCacheInternal() {
|
||||
super.flushCacheInternal();
|
||||
synchronized (mHttpDiskCacheLock) {
|
||||
if (mHttpDiskCache != null) {
|
||||
try {
|
||||
@@ -128,7 +118,6 @@ public class Fetcher extends Resizer {
|
||||
|
||||
@Override
|
||||
protected void closeCacheInternal() {
|
||||
super.closeCacheInternal();
|
||||
synchronized (mHttpDiskCacheLock) {
|
||||
if (mHttpDiskCache != null) {
|
||||
try {
|
||||
@@ -153,9 +142,9 @@ public class Fetcher extends Resizer {
|
||||
* @param data The data to load the bitmap, in this case, a regular http URL
|
||||
* @return The downloaded and resized bitmap
|
||||
*/
|
||||
private Bitmap processBitmap(String data, int decodeWidth, int decodeHeight) {
|
||||
private Bitmap processHttp(String data, int decodeWidth, int decodeHeight) {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "processBitmap - " + data);
|
||||
Log.v(TAG, "processHttp - " + data);
|
||||
}
|
||||
|
||||
final String key = Cache.hashKeyForDisk(data);
|
||||
@@ -212,7 +201,7 @@ public class Fetcher extends Resizer {
|
||||
Bitmap bitmap = null;
|
||||
if (fileDescriptor != null) {
|
||||
bitmap = decodeSampledBitmapFromDescriptor(fileDescriptor, decodeWidth,
|
||||
decodeHeight, getImageCache());
|
||||
decodeHeight, getCache());
|
||||
}
|
||||
if (fileInputStream != null) {
|
||||
try {
|
||||
@@ -223,34 +212,65 @@ public class Fetcher extends Resizer {
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
private Bitmap processHttpNoCache(String data, int decodeWidth, int decodeHeight) {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "processHttp - " + data);
|
||||
}
|
||||
final String key = Cache.hashKeyForDisk(data);
|
||||
FileOutputStream outputStream = null;
|
||||
Bitmap bitmap = null;
|
||||
|
||||
try {
|
||||
outputStream = new FileOutputStream(key);
|
||||
if (downloadUrlToStream(data, outputStream)) {
|
||||
bitmap = decodeSampledBitmapFromDescriptor(outputStream.getFD(), decodeWidth, decodeHeight, getCache());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "processBitmap - " + e);
|
||||
} catch (IllegalStateException e) {
|
||||
Log.e(TAG, "processBitmap - " + e);
|
||||
} finally {
|
||||
if (outputStream != null) {
|
||||
try {
|
||||
outputStream.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap processBitmap(Object data, int decodeWidth, int decodeHeight) {
|
||||
protected Bitmap processBitmap(Object data, int decodeWidth, int decodeHeight, boolean useCache) {
|
||||
if (data instanceof String) {
|
||||
String stringData = String.valueOf(data);
|
||||
if (stringData.startsWith(FILE_PREFIX)) {
|
||||
String filename = stringData.substring(FILE_PREFIX.length());
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "processBitmap - " + filename);
|
||||
Log.v(TAG, "processFile - " + filename);
|
||||
}
|
||||
return decodeSampledBitmapFromFile(filename, decodeWidth,
|
||||
decodeHeight, getImageCache());
|
||||
return decodeSampledBitmapFromFile(filename, decodeWidth, decodeHeight, getCache());
|
||||
} else if (stringData.startsWith(RESOURCE_PREFIX)) {
|
||||
String resPath = stringData.substring(RESOURCE_PREFIX.length());
|
||||
int resId = mResources.getIdentifier(resPath, "drawable", mPackageName);
|
||||
if (resId > 0) {
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "processBitmap - " + resId);
|
||||
Log.v(TAG, "processResource - " + resId);
|
||||
}
|
||||
return decodeSampledBitmapFromResource(mResources, resId, decodeWidth,
|
||||
decodeHeight, getImageCache());
|
||||
return decodeSampledBitmapFromResource(mResources, resId, decodeWidth, decodeHeight, getCache());
|
||||
} else {
|
||||
Log.v(TAG, "Missing ResourceID: " + stringData);
|
||||
}
|
||||
} else {
|
||||
return processBitmap(stringData, decodeWidth, decodeHeight);
|
||||
if (useCache) {
|
||||
return processHttp(stringData, decodeWidth, decodeHeight);
|
||||
} else {
|
||||
return processHttpNoCache(stringData, decodeWidth, decodeHeight);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.v(TAG, "Invalid Value: " + String.valueOf(data) + ". Expecting String or Integer.");
|
||||
Log.v(TAG, "Invalid Value: " + String.valueOf(data));
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -292,7 +312,8 @@ public class Fetcher extends Resizer {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
} catch (final IOException e) {}
|
||||
} catch (final IOException e) {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -23,9 +23,7 @@ import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.TransitionDrawable;
|
||||
import android.util.Log;
|
||||
import android.widget.ImageView;
|
||||
import java.lang.ref.WeakReference;
|
||||
@@ -37,6 +35,9 @@ import java.lang.ref.WeakReference;
|
||||
*/
|
||||
public abstract class Worker {
|
||||
|
||||
protected static final String RESOURCE_PREFIX = "res://";
|
||||
protected static final String FILE_PREFIX = "file:///";
|
||||
|
||||
static final String TAG = "JS";
|
||||
private static final int FADE_IN_TIME = 200;
|
||||
|
||||
@@ -78,7 +79,7 @@ public abstract class Worker {
|
||||
|
||||
/**
|
||||
* Load an image specified by the data parameter into an ImageView (override
|
||||
* {@link Worker#processBitmap(Object, int, int)} to define the processing logic). A memory and
|
||||
* {@link Worker#processBitmap(Object, int, int, boolean)} to define the processing logic). A memory and
|
||||
* disk cache will be used if an {@link Cache} has been added using
|
||||
* {@link Worker#addImageCache(Cache)}. If the
|
||||
* image is found in the memory cache, it is set immediately, otherwise an {@link AsyncTask}
|
||||
@@ -93,8 +94,11 @@ public abstract class Worker {
|
||||
return;
|
||||
}
|
||||
|
||||
BitmapDrawable value = null;
|
||||
Bitmap value = null;
|
||||
String dataString = String.valueOf(data);
|
||||
if (debuggable > 0) {
|
||||
Log.d(TAG, "loadImage on: " + imageView + " to: " + dataString);
|
||||
}
|
||||
|
||||
if (mCache != null && useCache) {
|
||||
value = mCache.getBitmapFromMemCache(dataString);
|
||||
@@ -102,21 +106,25 @@ public abstract class Worker {
|
||||
|
||||
if (value == null && !async) {
|
||||
// Decode sync.
|
||||
Bitmap bitmap = processBitmap(data, decodeWidth, decodeHeight);
|
||||
if (bitmap != null) {
|
||||
value = new BitmapDrawable(mResources, bitmap);
|
||||
value = processBitmap(data, decodeWidth, decodeHeight, useCache);
|
||||
if (value != null) {
|
||||
if (mCache != null && useCache) {
|
||||
// Don't add Images loaded from Resources to disk cache.
|
||||
boolean addToDiskCache = !(data instanceof Number);
|
||||
mCache.addBitmapToCache(dataString, value, addToDiskCache);
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "loadImage.addBitmapToCache: " + imageView + ", src: " + dataString);
|
||||
}
|
||||
mCache.addBitmapToCache(dataString, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
// Bitmap found in memory cache
|
||||
imageView.setImageDrawable(value);
|
||||
if (debuggable > 0) {
|
||||
Log.d(TAG, "Set ImageBitmap on: " + imageView + " to: " + dataString);
|
||||
}
|
||||
imageView.setImageBitmap(value);
|
||||
if (listener != null) {
|
||||
Log.d(TAG, "OnImageLoadedListener on: " + imageView + " to: " + dataString);
|
||||
listener.onImageLoaded(true);
|
||||
}
|
||||
} else if (cancelPotentialWork(data, imageView)) {
|
||||
@@ -176,15 +184,15 @@ public abstract class Worker {
|
||||
* example, you could resize a large bitmap here, or pull down an image from the network.
|
||||
*
|
||||
* @param data The data to identify which image to process, as provided by
|
||||
* {@link Worker#loadImage(Object, ImageView)}
|
||||
* {@link Worker#loadImage(Object, ImageView, int, int, boolean, boolean, OnImageLoadedListener)}
|
||||
* @return The processed bitmap
|
||||
*/
|
||||
protected abstract Bitmap processBitmap(Object data, int decodeWidth, int decodeHeight);
|
||||
protected abstract Bitmap processBitmap(Object data, int decodeWidth, int decodeHeight, boolean useCache);
|
||||
|
||||
/**
|
||||
* @return The {@link Cache} object currently being used by this Worker.
|
||||
*/
|
||||
protected Cache getImageCache() {
|
||||
protected Cache getCache() {
|
||||
return mCache;
|
||||
}
|
||||
|
||||
@@ -246,7 +254,7 @@ public abstract class Worker {
|
||||
/**
|
||||
* The actual AsyncTask that will asynchronously process the image.
|
||||
*/
|
||||
private class BitmapWorkerTask extends AsyncTask<Void, Void, BitmapDrawable> {
|
||||
private class BitmapWorkerTask extends AsyncTask<Void, Void, Bitmap> {
|
||||
private int mDecodeWidth;
|
||||
private int mDecodeHeight;
|
||||
private Object mData;
|
||||
@@ -271,14 +279,14 @@ public abstract class Worker {
|
||||
* Background processing.
|
||||
*/
|
||||
@Override
|
||||
protected BitmapDrawable doInBackground(Void... params) {
|
||||
protected Bitmap doInBackground(Void... params) {
|
||||
final String dataString = String.valueOf(mData);
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "doInBackground - starting work");
|
||||
Log.v(TAG, "doInBackground - starting work: " + imageViewReference.get() + ", on: " + dataString);
|
||||
}
|
||||
|
||||
final String dataString = String.valueOf(mData);
|
||||
|
||||
Bitmap bitmap = null;
|
||||
BitmapDrawable drawable = null;
|
||||
|
||||
// Wait here if work is paused and the task is not cancelled
|
||||
synchronized (mPauseWorkLock) {
|
||||
@@ -289,22 +297,13 @@ public abstract class Worker {
|
||||
}
|
||||
}
|
||||
|
||||
// If the image cache is available and this task has not been cancelled by another
|
||||
// thread and the ImageView that was originally bound to this task is still bound back
|
||||
// to this task and our "exit early" flag is not set then try and fetch the bitmap from
|
||||
// the cache
|
||||
if (mCache != null && !isCancelled() && getAttachedImageView() != null
|
||||
&& !mExitTasksEarly && mCacheImage) {
|
||||
bitmap = mCache.getBitmapFromDiskCache(dataString);
|
||||
}
|
||||
|
||||
// If the bitmap was not found in the cache and this task has not been cancelled by
|
||||
// another thread and the ImageView that was originally bound to this task is still
|
||||
// bound back to this task and our "exit early" flag is not set, then call the main
|
||||
// process method (as implemented by a subclass)
|
||||
if (bitmap == null && !isCancelled() && getAttachedImageView() != null
|
||||
&& !mExitTasksEarly) {
|
||||
bitmap = processBitmap(mData, mDecodeWidth, mDecodeHeight);
|
||||
bitmap = processBitmap(mData, mDecodeWidth, mDecodeHeight, mCacheImage);
|
||||
}
|
||||
|
||||
// If the bitmap was processed and the image cache is available, then add the processed
|
||||
@@ -312,11 +311,11 @@ public abstract class Worker {
|
||||
// here, if it was, and the thread is still running, we may as well add the processed
|
||||
// bitmap to our cache as it might be used again in the future
|
||||
if (bitmap != null) {
|
||||
drawable = new BitmapDrawable(mResources, bitmap);
|
||||
if (mCache != null && mCacheImage) {
|
||||
// Don't add Images loaded from Resources to disk cache.
|
||||
boolean addToDiskCache = !(mData instanceof Number);
|
||||
mCache.addBitmapToCache(dataString, drawable, addToDiskCache);
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "addBitmapToCache: " + imageViewReference.get() + ", src: " + dataString);
|
||||
}
|
||||
mCache.addBitmapToCache(dataString, bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,35 +323,48 @@ public abstract class Worker {
|
||||
Log.v(TAG, "doInBackground - finished work");
|
||||
}
|
||||
|
||||
return drawable;
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Once the image is processed, associates it to the imageView
|
||||
*/
|
||||
@Override
|
||||
protected void onPostExecute(BitmapDrawable value) {
|
||||
protected void onPostExecute(Bitmap value) {
|
||||
boolean success = false;
|
||||
// if cancel was called on this task or the "exit early" flag is set then we're done
|
||||
if (isCancelled() || mExitTasksEarly) {
|
||||
value = null;
|
||||
}
|
||||
|
||||
final ImageView imageView = getAttachedImageView();
|
||||
if (value != null && imageView != null) {
|
||||
final String dataString = String.valueOf(mData);
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "onPostExecute - setting bitmap");
|
||||
Log.v(TAG, "onPostExecute - setting bitmap for: " + imageViewReference.get() + " src: " + dataString);
|
||||
}
|
||||
|
||||
final ImageView imageView = getAttachedImageView();
|
||||
if (debuggable > 0) {
|
||||
Log.v(TAG, "onPostExecute - current ImageView: " + imageView);
|
||||
}
|
||||
|
||||
if (value != null && imageView != null) {
|
||||
success = true;
|
||||
setImageDrawable(imageView, value);
|
||||
if (debuggable > 0) {
|
||||
Log.d(TAG, "Set ImageDrawable on: " + imageView + " to: " + dataString);
|
||||
}
|
||||
imageView.setImageBitmap(value);
|
||||
}
|
||||
|
||||
if (mOnImageLoadedListener != null) {
|
||||
if (debuggable > 0) {
|
||||
Log.d(TAG, "OnImageLoadedListener on: " + imageView + " to: " + dataString);
|
||||
}
|
||||
mOnImageLoadedListener.onImageLoaded(success);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled(BitmapDrawable value) {
|
||||
protected void onCancelled(Bitmap value) {
|
||||
super.onCancelled(value);
|
||||
synchronized (mPauseWorkLock) {
|
||||
mPauseWorkLock.notifyAll();
|
||||
@@ -408,31 +420,31 @@ public abstract class Worker {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the processing is complete and the final drawable should be
|
||||
* set on the ImageView.
|
||||
*
|
||||
* @param imageView
|
||||
* @param drawable
|
||||
*/
|
||||
private void setImageDrawable(ImageView imageView, Drawable drawable) {
|
||||
if (mFadeInBitmap) {
|
||||
// Transition drawable with a transparent drawable and the final drawable
|
||||
final TransitionDrawable td =
|
||||
new TransitionDrawable(new Drawable[] {
|
||||
new ColorDrawable(0),
|
||||
drawable
|
||||
});
|
||||
// Set background to loading bitmap
|
||||
imageView.setBackgroundDrawable(
|
||||
new BitmapDrawable(mResources, mLoadingBitmap));
|
||||
|
||||
imageView.setImageDrawable(td);
|
||||
td.startTransition(FADE_IN_TIME);
|
||||
} else {
|
||||
imageView.setImageDrawable(drawable);
|
||||
}
|
||||
}
|
||||
// /**
|
||||
// * Called when the processing is complete and the final drawable should be
|
||||
// * set on the ImageView.
|
||||
// *
|
||||
// * @param imageView
|
||||
// * @param bitmap
|
||||
// */
|
||||
// private void setImageDrawable(ImageView imageView, Bitmap bitmap) {
|
||||
// if (mFadeInBitmap) {
|
||||
// // Transition drawable with a transparent drawable and the final drawable
|
||||
// final TransitionDrawable td =
|
||||
// new TransitionDrawable(new Drawable[] {
|
||||
// new ColorDrawable(0),
|
||||
// new BitmapDrawable(bitmap)
|
||||
// });
|
||||
// // Set background to loading bitmap
|
||||
// imageView.setBackgroundDrawable(
|
||||
// new BitmapDrawable(mResources, mLoadingBitmap));
|
||||
//
|
||||
// imageView.setImageDrawable(td);
|
||||
// td.startTransition(FADE_IN_TIME);
|
||||
// } else {
|
||||
// imageView.setImageBitmap(bitmap);
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Pause any ongoing background work. This can be used as a temporary
|
||||
@@ -477,30 +489,17 @@ public abstract class Worker {
|
||||
}
|
||||
}
|
||||
|
||||
protected void initDiskCacheInternal() {
|
||||
if (mCache != null) {
|
||||
mCache.initDiskCache();
|
||||
}
|
||||
}
|
||||
|
||||
protected void clearCacheInternal() {
|
||||
if (mCache != null) {
|
||||
mCache.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
protected void flushCacheInternal() {
|
||||
if (mCache != null) {
|
||||
mCache.flush();
|
||||
}
|
||||
}
|
||||
protected abstract void initDiskCacheInternal();
|
||||
|
||||
protected void closeCacheInternal() {
|
||||
if (mCache != null) {
|
||||
mCache.close();
|
||||
mCache = null;
|
||||
}
|
||||
}
|
||||
protected abstract void flushCacheInternal();
|
||||
|
||||
protected abstract void closeCacheInternal();
|
||||
|
||||
public void initCache() {
|
||||
new CacheAsyncTask().execute(MESSAGE_INIT_DISK_CACHE);
|
||||
|
||||
@@ -5,8 +5,8 @@ package org.nativescript.widgets;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.*;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* @author hhristov
|
||||
@@ -29,6 +29,7 @@ public class ImageView extends android.widget.ImageView {
|
||||
|
||||
public void setRotationAngle(float rotationAngle) {
|
||||
this.rotationAngle = rotationAngle;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public ImageView(Context context) {
|
||||
@@ -140,6 +141,14 @@ public class ImageView extends android.widget.ImageView {
|
||||
this.pBitmap = bm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImageDrawable(Drawable drawable) {
|
||||
super.setImageDrawable(drawable);
|
||||
if (drawable instanceof BitmapDrawable) {
|
||||
this.pBitmap = ((BitmapDrawable)drawable).getBitmap();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
BorderDrawable background = this.getBackground() instanceof BorderDrawable ? (BorderDrawable)this.getBackground() : null;
|
||||
@@ -150,6 +159,10 @@ public class ImageView extends android.widget.ImageView {
|
||||
float roundedBorderWidth = (float) Math.floor(uniformBorderWidth);
|
||||
float innerRadius = Math.max(0, uniformBorderRadius - roundedBorderWidth);
|
||||
|
||||
if (background != null) {
|
||||
background.draw(canvas);
|
||||
}
|
||||
|
||||
// The border width is included in the padding so there is no need for
|
||||
// clip if there is no inner border radius.
|
||||
if (innerRadius > 0) {
|
||||
@@ -215,9 +228,5 @@ public class ImageView extends android.widget.ImageView {
|
||||
else {
|
||||
super.onDraw(canvas);
|
||||
}
|
||||
|
||||
if (background != null) {
|
||||
background.draw(canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user