1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.quicksearchbox;
18 
19 import com.android.quicksearchbox.util.CachedLater;
20 import com.android.quicksearchbox.util.Consumer;
21 import com.android.quicksearchbox.util.Now;
22 import com.android.quicksearchbox.util.NowOrLater;
23 import com.android.quicksearchbox.util.NowOrLaterWrapper;
24 
25 import android.graphics.drawable.Drawable;
26 import android.net.Uri;
27 import android.text.TextUtils;
28 import android.util.Log;
29 
30 import java.util.WeakHashMap;
31 
32 /**
33  * Icon loader that caches the results of another icon loader.
34  *
35  */
36 public class CachingIconLoader implements IconLoader {
37 
38     private static final boolean DBG = false;
39     private static final String TAG = "QSB.CachingIconLoader";
40 
41     private final IconLoader mWrapped;
42 
43     private final WeakHashMap<String, Entry> mIconCache;
44 
45     /**
46      * Creates a new caching icon loader.
47      *
48      * @param wrapped IconLoader whose results will be cached.
49      */
CachingIconLoader(IconLoader wrapped)50     public CachingIconLoader(IconLoader wrapped) {
51         mWrapped = wrapped;
52         mIconCache = new WeakHashMap<String, Entry>();
53     }
54 
getIcon(String drawableId)55     public NowOrLater<Drawable> getIcon(String drawableId) {
56         if (DBG) Log.d(TAG, "getIcon(" + drawableId + ")");
57         if (TextUtils.isEmpty(drawableId) || "0".equals(drawableId)) {
58             return new Now<Drawable>(null);
59         }
60         Entry newEntry = null;
61         NowOrLater<Drawable.ConstantState> drawableState;
62         synchronized (this) {
63             drawableState = queryCache(drawableId);
64             if (drawableState == null) {
65                 newEntry = new Entry();
66                 storeInIconCache(drawableId, newEntry);
67             }
68         }
69         if (drawableState != null) {
70             return new NowOrLaterWrapper<Drawable.ConstantState, Drawable>(drawableState){
71                 @Override
72                 public Drawable get(Drawable.ConstantState value) {
73                     return value == null ? null : value.newDrawable();
74                 }};
75         }
76         NowOrLater<Drawable> drawable = mWrapped.getIcon(drawableId);
77         newEntry.set(drawable);
78         storeInIconCache(drawableId, newEntry);
79         return drawable;
80     }
81 
82     public Uri getIconUri(String drawableId) {
83         return mWrapped.getIconUri(drawableId);
84     }
85 
86     private synchronized NowOrLater<Drawable.ConstantState> queryCache(String drawableId) {
87         NowOrLater<Drawable.ConstantState> cached = mIconCache.get(drawableId);
88         if (DBG) {
89             if (cached != null) Log.d(TAG, "Found icon in cache: " + drawableId);
90         }
91         return cached;
92     }
93 
94     private synchronized void storeInIconCache(String resourceUri, Entry drawable) {
95         if (drawable != null) {
96             mIconCache.put(resourceUri, drawable);
97         }
98     }
99 
100     private static class Entry extends CachedLater<Drawable.ConstantState>
101             implements Consumer<Drawable>{
102         private NowOrLater<Drawable> mDrawable;
103         private boolean mGotDrawable;
104         private boolean mCreateRequested;
105 
106         public Entry() {
107         }
108 
109         public synchronized void set(NowOrLater<Drawable> drawable) {
110             if (mGotDrawable) throw new IllegalStateException("set() may only be called once.");
111             mGotDrawable = true;
112             mDrawable = drawable;
113             if (mCreateRequested) {
114                 getLater();
115             }
116         }
117 
118         @Override
119         protected synchronized void create() {
120             if (!mCreateRequested) {
121                 mCreateRequested = true;
122                 if (mGotDrawable) {
123                     getLater();
124                 }
125             }
126         }
127 
128         private void getLater() {
129             NowOrLater<Drawable> drawable = mDrawable;
130             mDrawable = null;
131             drawable.getLater(this);
132         }
133 
134         public boolean consume(Drawable value) {
135             store(value == null ? null : value.getConstantState());
136             return true;
137         }
138     }
139 
140 }
141