1bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch/* 2bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * Copyright (C) 2013 The Android Open Source Project 3bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * 4bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * Licensed under the Apache License, Version 2.0 (the "License"); 5bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * you may not use this file except in compliance with the License. 6bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * You may obtain a copy of the License at 7bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * 8bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * http://www.apache.org/licenses/LICENSE-2.0 9bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * 10bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * Unless required by applicable law or agreed to in writing, software 11bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * distributed under the License is distributed on an "AS IS" BASIS, 12bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * See the License for the specific language governing permissions and 14bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * limitations under the License. 15bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch */ 16bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 17bc1a645f26a30fd95e68043b608038537b7c798fAdam Kochpackage com.example.android.contactslist.util; 18bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 19bc1a645f26a30fd95e68043b608038537b7c798fAdam Kochimport android.annotation.TargetApi; 20bc1a645f26a30fd95e68043b608038537b7c798fAdam Kochimport android.graphics.Bitmap; 21bc1a645f26a30fd95e68043b608038537b7c798fAdam Kochimport android.os.Bundle; 22bc1a645f26a30fd95e68043b608038537b7c798fAdam Kochimport android.support.v4.app.Fragment; 23bc1a645f26a30fd95e68043b608038537b7c798fAdam Kochimport android.support.v4.app.FragmentManager; 24bc1a645f26a30fd95e68043b608038537b7c798fAdam Kochimport android.support.v4.util.LruCache; 25bc1a645f26a30fd95e68043b608038537b7c798fAdam Kochimport android.util.Log; 26bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 27bc1a645f26a30fd95e68043b608038537b7c798fAdam Kochimport com.example.android.contactslist.BuildConfig; 28bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 29bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch/** 30bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * This class holds our bitmap caches (memory and disk). 31bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch */ 32bc1a645f26a30fd95e68043b608038537b7c798fAdam Kochpublic class ImageCache { 33bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch private static final String TAG = "ImageCache"; 34bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch private LruCache<String, Bitmap> mMemoryCache; 35bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 36bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch /** 37bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * Creating a new ImageCache object using the specified parameters. 38bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * 39bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * @param memCacheSizePercent The cache size as a percent of available app memory. 40bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch */ 41bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch private ImageCache(float memCacheSizePercent) { 42bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch init(memCacheSizePercent); 43bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 44bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 45bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch /** 46bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * Find and return an existing ImageCache stored in a {@link RetainFragment}, if not found a new 47bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * one is created using the supplied params and saved to a {@link RetainFragment}. 48bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * 49bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * @param fragmentManager The fragment manager to use when dealing with the retained fragment. 50bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * @param memCacheSizePercent The cache size as a percent of available app memory. 51bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * @return An existing retained ImageCache object or a new one if one did not exist 52bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch */ 53bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch public static ImageCache getInstance( 54bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch FragmentManager fragmentManager, float memCacheSizePercent) { 55bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 56bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch // Search for, or create an instance of the non-UI RetainFragment 57bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch final RetainFragment mRetainFragment = findOrCreateRetainFragment(fragmentManager); 58bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 59bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch // See if we already have an ImageCache stored in RetainFragment 60bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch ImageCache imageCache = (ImageCache) mRetainFragment.getObject(); 61bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 62bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch // No existing ImageCache, create one and store it in RetainFragment 63bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch if (imageCache == null) { 64bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch imageCache = new ImageCache(memCacheSizePercent); 65bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch mRetainFragment.setObject(imageCache); 66bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 67bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 68bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch return imageCache; 69bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 70bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 71bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch /** 72bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * Initialize the cache. 73bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * 74bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * @param memCacheSizePercent The cache size as a percent of available app memory. 75bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch */ 76bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch private void init(float memCacheSizePercent) { 77bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch int memCacheSize = calculateMemCacheSize(memCacheSizePercent); 78bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 79bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch // Set up memory cache 80bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch if (BuildConfig.DEBUG) { 81bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch Log.d(TAG, "Memory cache created (size = " + memCacheSize + ")"); 82bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 83bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch mMemoryCache = new LruCache<String, Bitmap>(memCacheSize) { 84bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch /** 85bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * Measure item size in kilobytes rather than units which is more practical 86bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * for a bitmap cache 87bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch */ 88bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch @Override 89bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch protected int sizeOf(String key, Bitmap bitmap) { 90bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch final int bitmapSize = getBitmapSize(bitmap) / 1024; 91bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch return bitmapSize == 0 ? 1 : bitmapSize; 92bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 93bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch }; 94bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 95bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 96bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch /** 97bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * Adds a bitmap to both memory and disk cache. 98bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * @param data Unique identifier for the bitmap to store 99bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * @param bitmap The bitmap to store 100bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch */ 101bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch public void addBitmapToCache(String data, Bitmap bitmap) { 102bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch if (data == null || bitmap == null) { 103bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch return; 104bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 105bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 106bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch // Add to memory cache 107bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch if (mMemoryCache != null && mMemoryCache.get(data) == null) { 108bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch mMemoryCache.put(data, bitmap); 109bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 110bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 111bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 112bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch /** 113bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * Get from memory cache. 114bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * 115bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * @param data Unique identifier for which item to get 116bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * @return The bitmap if found in cache, null otherwise 117bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch */ 118bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch public Bitmap getBitmapFromMemCache(String data) { 119bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch if (mMemoryCache != null) { 120bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch final Bitmap memBitmap = mMemoryCache.get(data); 121bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch if (memBitmap != null) { 122bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch if (BuildConfig.DEBUG) { 123bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch Log.d(TAG, "Memory cache hit"); 124bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 125bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch return memBitmap; 126bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 127bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 128bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch return null; 129bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 130bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 131bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch /** 132bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * Get the size in bytes of a bitmap. 133bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * 134bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * @param bitmap The bitmap to calculate the size of. 135bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * @return size of bitmap in bytes. 136bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch */ 137bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch @TargetApi(12) 138bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch public static int getBitmapSize(Bitmap bitmap) { 139bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch if (Utils.hasHoneycombMR1()) { 140bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch return bitmap.getByteCount(); 141bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 142bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch // Pre HC-MR1 143bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch return bitmap.getRowBytes() * bitmap.getHeight(); 144bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 145bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 146bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch /** 147bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * Calculates the memory cache size based on a percentage of the max available VM memory. 148bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * Eg. setting percent to 0.2 would set the memory cache to one fifth of the available 149bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * memory. Throws {@link IllegalArgumentException} if percent is < 0.05 or > .8. 150bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * memCacheSize is stored in kilobytes instead of bytes as this will eventually be passed 151bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * to construct a LruCache which takes an int in its constructor. 152bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * 153bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * This value should be chosen carefully based on a number of factors 154bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * Refer to the corresponding Android Training class for more discussion: 155bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * http://developer.android.com/training/displaying-bitmaps/ 156bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * 157bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * @param percent Percent of available app memory to use to size memory cache. 158bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch */ 159bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch public static int calculateMemCacheSize(float percent) { 160bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch if (percent < 0.05f || percent > 0.8f) { 161bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch throw new IllegalArgumentException("setMemCacheSizePercent - percent must be " 162bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch + "between 0.05 and 0.8 (inclusive)"); 163bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 164bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch return Math.round(percent * Runtime.getRuntime().maxMemory() / 1024); 165bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 166bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 167bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch /** 168bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * Locate an existing instance of this Fragment or if not found, create and 169bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * add it using FragmentManager. 170bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * 171bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * @param fm The FragmentManager manager to use. 172bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * @return The existing instance of the Fragment or the new instance if just 173bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * created. 174bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch */ 175bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) { 176bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch // Check to see if we have retained the worker fragment. 177bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch RetainFragment mRetainFragment = (RetainFragment) fm.findFragmentByTag(TAG); 178bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 179bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch // If not retained (or first time running), we need to create and add it. 180bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch if (mRetainFragment == null) { 181bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch mRetainFragment = new RetainFragment(); 182bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch fm.beginTransaction().add(mRetainFragment, TAG).commitAllowingStateLoss(); 183bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 184bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 185bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch return mRetainFragment; 186bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 187bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 188bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch /** 189bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * A simple non-UI Fragment that stores a single Object and is retained over configuration 190bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * changes. It will be used to retain the ImageCache object. 191bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch */ 192bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch public static class RetainFragment extends Fragment { 193bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch private Object mObject; 194bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 195bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch /** 196bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * Empty constructor as per the Fragment documentation 197bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch */ 198bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch public RetainFragment() {} 199bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 200bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch @Override 201bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch public void onCreate(Bundle savedInstanceState) { 202bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch super.onCreate(savedInstanceState); 203bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 204bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch // Make sure this Fragment is retained over a configuration change 205bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch setRetainInstance(true); 206bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 207bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 208bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch /** 209bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * Store a single object in this Fragment. 210bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * 211bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * @param object The object to store 212bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch */ 213bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch public void setObject(Object object) { 214bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch mObject = object; 215bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 216bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 217bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch /** 218bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * Get the stored object. 219bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * 220bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch * @return The stored object 221bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch */ 222bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch public Object getObject() { 223bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch return mObject; 224bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 225bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch } 226bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch 227bc1a645f26a30fd95e68043b608038537b7c798fAdam Koch} 228