/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.quicksearchbox import android.graphics.drawable.Drawable import android.net.Uri import android.text.TextUtils import android.util.Log import com.android.quicksearchbox.util.* import java.util.WeakHashMap /** Icon loader that caches the results of another icon loader. */ class CachingIconLoader(private val mWrapped: IconLoader) : IconLoader { private val mIconCache: WeakHashMap override fun getIcon(drawableId: String?): NowOrLater? { if (DBG) Log.d(TAG, "getIcon($drawableId)") if (TextUtils.isEmpty(drawableId) || "0".equals(drawableId)) { return Now(null) } var newEntry: Entry? = null var drawableState: NowOrLater? synchronized(this) { drawableState = queryCache(drawableId) if (drawableState == null) { newEntry = Entry() storeInIconCache(drawableId, newEntry) } } if (drawableState != null) { return object : NowOrLaterWrapper(drawableState!!) { @Override override operator fun get(value: Drawable.ConstantState?): Drawable? { return if (value == null) null else value.newDrawable() } } } val drawable: NowOrLater? = mWrapped.getIcon(drawableId) newEntry?.set(drawable) storeInIconCache(drawableId, newEntry) return drawable!! } override fun getIconUri(drawableId: String?): Uri? { return mWrapped.getIconUri(drawableId) } @Synchronized private fun queryCache(drawableId: String?): NowOrLater? { val cached: Entry? = mIconCache.get(drawableId) if (DBG) { if (cached != null) Log.d(TAG, "Found icon in cache: $drawableId") } return cached } @Synchronized private fun storeInIconCache(resourceUri: String?, drawable: Entry?) { if (drawable != null) { mIconCache.put(resourceUri, drawable) } } private class Entry : CachedLater(), Consumer { private var mDrawable: NowOrLater? = null private var mGotDrawable = false private var mCreateRequested = false @Synchronized fun set(drawable: NowOrLater?) { if (mGotDrawable) throw IllegalStateException("set() may only be called once.") mGotDrawable = true mDrawable = drawable if (mCreateRequested) { later } } @Override @Synchronized override fun create() { if (!mCreateRequested) { mCreateRequested = true if (mGotDrawable) { later } } } private val later: Unit get() { val drawable: NowOrLater? = mDrawable mDrawable = null drawable!!.getLater(this) } override fun consume(value: Drawable?): Boolean { store(if (value == null) null else value.getConstantState()) return true } } companion object { private const val DBG = false private const val TAG = "QSB.CachingIconLoader" } /** * Creates a new caching icon loader. * * @param wrapped IconLoader whose results will be cached. */ init { mIconCache = WeakHashMap() } }