blob: f9ec8b22daea68b5384463cd65919ef3aed5ecba [file] [log] [blame]
/*
* Copyright (c) 2016, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.android.gallery3d.filtershow.mediapicker.imageloader;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Looper;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ImageLoaderConfig {
private final String TAG = "ImageLoaderConfig";
private static final String URI_AND_SIZE_SEPARATOR = "_";
private static final String WIDTH_AND_HEIGHT_SEPARATOR = "x";
final Resources resources;
public static final int DEFAULT_THREAD_POOL_SIZE = 4;
public static final int DEFAULT_THREAD_PRIORITY = Thread.NORM_PRIORITY - 2;
public Context context;
public Executor taskExecutor = null;
public boolean customExecutor = false;
public int threadPoolSize = DEFAULT_THREAD_POOL_SIZE;
public int threadPriority = DEFAULT_THREAD_PRIORITY;
private LinkedHashMap<String, Bitmap> imageCacheMap;
private int icmMaxSize;
private int icmSize;
public ImageLoaderOptions defaultOptions = null;
public ImageLoaderConfig(Context context) {
this.context = context.getApplicationContext();
resources = context.getResources();
initEmptyFieldsWithDefaultValues();
}
public void cacheMapSizePercentage(int availableMemoryPercent) {
if (availableMemoryPercent <= 0 || availableMemoryPercent >= 100) {
throw new IllegalArgumentException("availableMemoryPercent must be in range (0 < % < 100)");
}
long availableMemory = Runtime.getRuntime().maxMemory();
icmMaxSize = (int) (availableMemory * (availableMemoryPercent / 100f));
}
private void initEmptyFieldsWithDefaultValues() {
if (taskExecutor == null) {
taskExecutor = createExecutor(threadPoolSize, threadPriority);
} else {
customExecutor = true;
}
if (imageCacheMap == null) {
icmSize = 0;
imageCacheMap = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);
}
if (defaultOptions == null) {
defaultOptions = new ImageLoaderOptions();
}
}
public final Bitmap get(String key) {
if (key == null) {
throw new NullPointerException("key == null");
}
synchronized (this) {
return imageCacheMap.get(key);
}
}
public final boolean put(String key, Bitmap value) {
if (key == null || value == null) {
throw new NullPointerException("key == null || value == null");
}
synchronized (this) {
icmSize += sizeOf(value);
Bitmap previous = imageCacheMap.put(key, value);
if (previous != null) {
icmSize -= sizeOf(previous);
}
}
trimToSize(icmMaxSize);
return true;
}
private void trimToSize(int maxSize) {
while (true) {
String key;
Bitmap value;
synchronized (this) {
if (icmSize < 0 || (imageCacheMap.isEmpty() && icmSize != 0)) {
throw new IllegalStateException(getClass().getName() + ".sizeOf() is inconsistent!");
}
if (icmSize <= maxSize || imageCacheMap.isEmpty()) {
break;
}
Map.Entry<String, Bitmap> toEvict = imageCacheMap.entrySet().iterator().next();
if (toEvict == null) {
break;
}
key = toEvict.getKey();
value = toEvict.getValue();
imageCacheMap.remove(key);
icmSize -= sizeOf(value);
}
}
}
public final Bitmap remove(String key) {
if (key == null) {
throw new NullPointerException("key == null");
}
synchronized (this) {
Bitmap previous = imageCacheMap.remove(key);
if (previous != null) {
icmSize -= sizeOf(previous);
}
return previous;
}
}
private int sizeOf(Bitmap value) {
return value.getRowBytes() * value.getHeight();
}
public int defineHeightForImage(ImageViewImpl imageView) {
int height = imageView.getHeight();
if (height <= 0)
height = resources.getDisplayMetrics().heightPixels;
return height;
}
public int defineWidthForImage(ImageViewImpl imageView) {
int width = imageView.getWidth();
if (width <= 0)
width = resources.getDisplayMetrics().widthPixels;
return width;
}
public static String generateKey(String imageUri, int w, int h) {
return new StringBuilder(imageUri).append(URI_AND_SIZE_SEPARATOR).append(w)
.append(WIDTH_AND_HEIGHT_SEPARATOR).append(h).toString();
}
public static Executor createExecutor(int threadPoolSize, int threadPriority) {
BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>();
return new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 0L, TimeUnit.MILLISECONDS,
taskQueue, new ThreadFactoryImpl(threadPriority));
}
public static Handler defineHandler(ImageLoaderOptions options) {
Handler handler = options.getHandler();
if (handler == null && Looper.myLooper() == Looper.getMainLooper()) {
handler = new Handler();
}
return handler;
}
}