org.tigase.messenger.phone.pro.utils.ImageHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.tigase.messenger.phone.pro.utils.ImageHelper.java

Source

/*
 * ImageHelper.java
 *
 * Tigase Android Messenger
 * Copyright (C) 2011-2016 "Tigase, Inc." <office@tigase.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. Look for COPYING file in the top folder.
 * If not, see http://www.gnu.org/licenses/.
 */

package org.tigase.messenger.phone.pro.utils;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.support.v4.util.LruCache;
import android.util.Log;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Class implements generic image in-memory caching feature to ensure that we
 * have one memory cache for all images (to reduce memory usage and protect
 * against OutOfMemory).
 *
 * @author andrzej
 */
public class ImageHelper {

    private static final String TAG = "ImageHelper";

    public static float density = 1;
    protected static LruCache<String, Bitmap> memCache = null;
    private static Set<Bitmap> placeHolders = new HashSet<Bitmap>();
    private static ConcurrentHashMap<String, BitmapDiskLruCache> diskCaches = new ConcurrentHashMap<String, BitmapDiskLruCache>();

    protected static void initialize(Context context) {
        if (memCache == null) {
            // Get memory class of this device, exceeding this amount will throw
            // an OutOfMemory exception.
            final int memClass = ((android.app.ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE))
                    .getMemoryClass();

            // Use 1/8th of the available memory for this memory cache.
            final int cacheSize = 1024 * 1024 * memClass / 8;

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
                memCache = new LruCache<String, Bitmap>(cacheSize) {
                    @TargetApi(12)
                    @Override
                    protected int sizeOf(String key, Bitmap bitmap) {
                        // The cache size will be measured in bytes rather than
                        // number of items. Ignoring placeholder bitmap as well.
                        return placeHolders.contains(bitmap) ? 0 : bitmap.getByteCount();
                    }
                };
            } else {
                // below SDK 12 there is no getByteCount method
                memCache = 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. Ignoring placeholder bitmap as well.
                        return placeHolders.contains(bitmap) ? 0 : (bitmap.getRowBytes() * bitmap.getHeight());
                    }
                };
            }

            // maps images cache
            BitmapDiskLruCache diskCache = new BitmapDiskLruCache();
            diskCache.initialize(context, "maps", 10 * 1024 * 1024);
            diskCaches.put("maps", diskCache);

            // images from files shared with or by us
            diskCache = new BitmapDiskLruCache();
            diskCache.initialize(context, "images-mini", 10 * 1024 * 1024);
            diskCaches.put("images-mini", diskCache);
        }
    }

    protected static void addPlaceHolder(Bitmap placeHolder) {
        placeHolders.add(placeHolder);
    }

    @SuppressLint("NewApi")
    public static void onTrimMemory(int level) {
        int count = 0;
        if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
            count = 10;
            if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
                count = 0;
            }
        } else
            return;

        int size = 0;
        if (count > 0) {
            for (Bitmap b : placeHolders) {
                size += b.getByteCount();
            }
        }

        int trimSize = placeHolders.size() == 0 ? 0 : ((count * size) / placeHolders.size());
        Log.v(TAG, "trim image cache from " + memCache.size() + " to " + trimSize
                + " to reduce memory usage, max size " + memCache.maxSize());
        memCache.trimToSize(trimSize);
    }

    public static void put(String type, String key, Bitmap bitmap) {
        memCache.put(key, bitmap);
        BitmapDiskLruCache diskCache = diskCaches.get(type);
        if (diskCache != null) {
            diskCache.put(key, bitmap);
        }
    }

    public static Bitmap get(String type, String key) {
        Bitmap value = memCache.get(key);
        if (value == null) {
            BitmapDiskLruCache diskCache = diskCaches.get(type);
            if (diskCache != null) {
                value = diskCache.get(key);
            }
        }
        return value;
    }

    public static String hashKey(String key) {
        String cacheKey;
        try {
            final MessageDigest mDigest = MessageDigest.getInstance("MD5");
            mDigest.update(key.getBytes());
            cacheKey = bytesToHexString(mDigest.digest());
        } catch (NoSuchAlgorithmException e) {
            cacheKey = String.valueOf(key.hashCode());
        }
        return cacheKey;
    }

    private static String bytesToHexString(byte[] bytes) {
        // http://stackoverflow.com/questions/332079
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(0xFF & bytes[i]);
            if (hex.length() == 1) {
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString();
    }

    public static class BitmapDiskLruCache extends DiskLruCache<Bitmap> {

        @Override
        protected Bitmap decode(org.tigase.messenger.phone.pro.utils.DiskLruCache.Entry e) {
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(e.file);
                return BitmapFactory.decodeFileDescriptor(fis.getFD());
            } catch (Exception ex) {
                ex.printStackTrace();
                return null;
            } finally {
                if (fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                }
            }
        }

        @Override
        protected void encode(org.tigase.messenger.phone.pro.utils.DiskLruCache.Entry e, Bitmap value) {
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(e.file);
                value.compress(CompressFormat.PNG, 100, fos);
            } catch (FileNotFoundException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } finally {
                if (fos != null) {
                    try {
                        fos.close();
                    } catch (IOException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                }
            }
        }

    }
}