• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.internal.widget;
18 
19 import android.annotation.DrawableRes;
20 import android.annotation.Nullable;
21 import android.content.Context;
22 import android.content.res.Configuration;
23 import android.graphics.Bitmap;
24 import android.graphics.drawable.Drawable;
25 import android.graphics.drawable.Icon;
26 import android.net.Uri;
27 import android.text.TextUtils;
28 import android.util.AttributeSet;
29 import android.view.RemotableViewMethod;
30 import android.widget.ImageView;
31 import android.widget.RemoteViews;
32 
33 import libcore.util.Objects;
34 
35 /**
36  * An ImageView for displaying an Icon. Avoids reloading the Icon when possible.
37  */
38 @RemoteViews.RemoteView
39 public class CachingIconView extends ImageView {
40 
41     private String mLastPackage;
42     private int mLastResId;
43     private boolean mInternalSetDrawable;
44 
CachingIconView(Context context, @Nullable AttributeSet attrs)45     public CachingIconView(Context context, @Nullable AttributeSet attrs) {
46         super(context, attrs);
47     }
48 
49     @Override
50     @RemotableViewMethod(asyncImpl="setImageIconAsync")
setImageIcon(@ullable Icon icon)51     public void setImageIcon(@Nullable Icon icon) {
52         if (!testAndSetCache(icon)) {
53             mInternalSetDrawable = true;
54             // This calls back to setImageDrawable, make sure we don't clear the cache there.
55             super.setImageIcon(icon);
56             mInternalSetDrawable = false;
57         }
58     }
59 
60     @Override
setImageIconAsync(@ullable Icon icon)61     public Runnable setImageIconAsync(@Nullable Icon icon) {
62         resetCache();
63         return super.setImageIconAsync(icon);
64     }
65 
66     @Override
67     @RemotableViewMethod(asyncImpl="setImageResourceAsync")
setImageResource(@rawableRes int resId)68     public void setImageResource(@DrawableRes int resId) {
69         if (!testAndSetCache(resId)) {
70             mInternalSetDrawable = true;
71             // This calls back to setImageDrawable, make sure we don't clear the cache there.
72             super.setImageResource(resId);
73             mInternalSetDrawable = false;
74         }
75     }
76 
77     @Override
setImageResourceAsync(@rawableRes int resId)78     public Runnable setImageResourceAsync(@DrawableRes int resId) {
79         resetCache();
80         return super.setImageResourceAsync(resId);
81     }
82 
83     @Override
84     @RemotableViewMethod(asyncImpl="setImageURIAsync")
setImageURI(@ullable Uri uri)85     public void setImageURI(@Nullable Uri uri) {
86         resetCache();
87         super.setImageURI(uri);
88     }
89 
90     @Override
setImageURIAsync(@ullable Uri uri)91     public Runnable setImageURIAsync(@Nullable Uri uri) {
92         resetCache();
93         return super.setImageURIAsync(uri);
94     }
95 
96     @Override
setImageDrawable(@ullable Drawable drawable)97     public void setImageDrawable(@Nullable Drawable drawable) {
98         if (!mInternalSetDrawable) {
99             // Only clear the cache if we were externally called.
100             resetCache();
101         }
102         super.setImageDrawable(drawable);
103     }
104 
105     @Override
106     @RemotableViewMethod
setImageBitmap(Bitmap bm)107     public void setImageBitmap(Bitmap bm) {
108         resetCache();
109         super.setImageBitmap(bm);
110     }
111 
112     @Override
onConfigurationChanged(Configuration newConfig)113     protected void onConfigurationChanged(Configuration newConfig) {
114         super.onConfigurationChanged(newConfig);
115         resetCache();
116     }
117 
118     /**
119      * @return true if the currently set image is the same as {@param icon}
120      */
testAndSetCache(Icon icon)121     private synchronized boolean testAndSetCache(Icon icon) {
122         if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
123             String iconPackage = normalizeIconPackage(icon);
124 
125             boolean isCached = mLastResId != 0
126                     && icon.getResId() == mLastResId
127                     && Objects.equal(iconPackage, mLastPackage);
128 
129             mLastPackage = iconPackage;
130             mLastResId = icon.getResId();
131 
132             return isCached;
133         } else {
134             resetCache();
135             return false;
136         }
137     }
138 
139     /**
140      * @return true if the currently set image is the same as {@param resId}
141      */
testAndSetCache(int resId)142     private synchronized boolean testAndSetCache(int resId) {
143         boolean isCached;
144         if (resId == 0 || mLastResId == 0) {
145             isCached = false;
146         } else {
147             isCached = resId == mLastResId && null == mLastPackage;
148         }
149         mLastPackage = null;
150         mLastResId = resId;
151         return isCached;
152     }
153 
154     /**
155      * Returns the normalized package name of {@param icon}.
156      * @return null if icon is null or if the icons package is null, empty or matches the current
157      *         context. Otherwise returns the icon's package context.
158      */
normalizeIconPackage(Icon icon)159     private String normalizeIconPackage(Icon icon) {
160         if (icon == null) {
161             return null;
162         }
163 
164         String pkg = icon.getResPackage();
165         if (TextUtils.isEmpty(pkg)) {
166             return null;
167         }
168         if (pkg.equals(mContext.getPackageName())) {
169             return null;
170         }
171         return pkg;
172     }
173 
resetCache()174     private synchronized void resetCache() {
175         mLastResId = 0;
176         mLastPackage = null;
177     }
178 }
179