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