1 /*
2  * Copyright (C) 2022 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 package com.android.quicksearchbox
17 
18 import android.graphics.drawable.Drawable
19 import android.net.Uri
20 import android.text.TextUtils
21 import android.util.Log
22 import com.android.quicksearchbox.util.*
23 import java.util.WeakHashMap
24 
25 /** Icon loader that caches the results of another icon loader. */
26 class CachingIconLoader(private val mWrapped: IconLoader) : IconLoader {
27   private val mIconCache: WeakHashMap<String, Entry>
getIconnull28   override fun getIcon(drawableId: String?): NowOrLater<Drawable?>? {
29     if (DBG) Log.d(TAG, "getIcon($drawableId)")
30     if (TextUtils.isEmpty(drawableId) || "0".equals(drawableId)) {
31       return Now<Drawable>(null)
32     }
33     var newEntry: Entry? = null
34     var drawableState: NowOrLater<Drawable.ConstantState?>?
35     synchronized(this) {
36       drawableState = queryCache(drawableId)
37       if (drawableState == null) {
38         newEntry = Entry()
39         storeInIconCache(drawableId, newEntry)
40       }
41     }
42     if (drawableState != null) {
43       return object : NowOrLaterWrapper<Drawable.ConstantState?, Drawable?>(drawableState!!) {
44         @Override
45         override operator fun get(value: Drawable.ConstantState?): Drawable? {
46           return if (value == null) null else value.newDrawable()
47         }
48       }
49     }
50     val drawable: NowOrLater<Drawable?>? = mWrapped.getIcon(drawableId)
51     newEntry?.set(drawable)
52     storeInIconCache(drawableId, newEntry)
53     return drawable!!
54   }
55 
getIconUrinull56   override fun getIconUri(drawableId: String?): Uri? {
57     return mWrapped.getIconUri(drawableId)
58   }
59 
60   @Synchronized
queryCachenull61   private fun queryCache(drawableId: String?): NowOrLater<Drawable.ConstantState?>? {
62     val cached: Entry? = mIconCache.get(drawableId)
63     if (DBG) {
64       if (cached != null) Log.d(TAG, "Found icon in cache: $drawableId")
65     }
66     return cached
67   }
68 
69   @Synchronized
storeInIconCachenull70   private fun storeInIconCache(resourceUri: String?, drawable: Entry?) {
71     if (drawable != null) {
72       mIconCache.put(resourceUri, drawable)
73     }
74   }
75 
76   private class Entry : CachedLater<Drawable.ConstantState?>(), Consumer<Drawable?> {
77     private var mDrawable: NowOrLater<Drawable?>? = null
78     private var mGotDrawable = false
79     private var mCreateRequested = false
80 
81     @Synchronized
setnull82     fun set(drawable: NowOrLater<Drawable?>?) {
83       if (mGotDrawable) throw IllegalStateException("set() may only be called once.")
84       mGotDrawable = true
85       mDrawable = drawable
86       if (mCreateRequested) {
87         later
88       }
89     }
90 
91     @Override
92     @Synchronized
createnull93     override fun create() {
94       if (!mCreateRequested) {
95         mCreateRequested = true
96         if (mGotDrawable) {
97           later
98         }
99       }
100     }
101 
102     private val later: Unit
103       get() {
104         val drawable: NowOrLater<Drawable?>? = mDrawable
105         mDrawable = null
106         drawable!!.getLater(this)
107       }
108 
consumenull109     override fun consume(value: Drawable?): Boolean {
110       store(if (value == null) null else value.getConstantState())
111       return true
112     }
113   }
114 
115   companion object {
116     private const val DBG = false
117     private const val TAG = "QSB.CachingIconLoader"
118   }
119 
120   /**
121    * Creates a new caching icon loader.
122    *
123    * @param wrapped IconLoader whose results will be cached.
124    */
125   init {
126     mIconCache = WeakHashMap<String, Entry>()
127   }
128 }
129