Home | History | Annotate | Download | only in widget
      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 java.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     private boolean mForceHidden;
     45     private int mDesiredVisibility;
     46 
     47     public CachingIconView(Context context, @Nullable AttributeSet attrs) {
     48         super(context, attrs);
     49     }
     50 
     51     @Override
     52     @RemotableViewMethod(asyncImpl="setImageIconAsync")
     53     public void setImageIcon(@Nullable Icon icon) {
     54         if (!testAndSetCache(icon)) {
     55             mInternalSetDrawable = true;
     56             // This calls back to setImageDrawable, make sure we don't clear the cache there.
     57             super.setImageIcon(icon);
     58             mInternalSetDrawable = false;
     59         }
     60     }
     61 
     62     @Override
     63     public Runnable setImageIconAsync(@Nullable Icon icon) {
     64         resetCache();
     65         return super.setImageIconAsync(icon);
     66     }
     67 
     68     @Override
     69     @RemotableViewMethod(asyncImpl="setImageResourceAsync")
     70     public void setImageResource(@DrawableRes int resId) {
     71         if (!testAndSetCache(resId)) {
     72             mInternalSetDrawable = true;
     73             // This calls back to setImageDrawable, make sure we don't clear the cache there.
     74             super.setImageResource(resId);
     75             mInternalSetDrawable = false;
     76         }
     77     }
     78 
     79     @Override
     80     public Runnable setImageResourceAsync(@DrawableRes int resId) {
     81         resetCache();
     82         return super.setImageResourceAsync(resId);
     83     }
     84 
     85     @Override
     86     @RemotableViewMethod(asyncImpl="setImageURIAsync")
     87     public void setImageURI(@Nullable Uri uri) {
     88         resetCache();
     89         super.setImageURI(uri);
     90     }
     91 
     92     @Override
     93     public Runnable setImageURIAsync(@Nullable Uri uri) {
     94         resetCache();
     95         return super.setImageURIAsync(uri);
     96     }
     97 
     98     @Override
     99     public void setImageDrawable(@Nullable Drawable drawable) {
    100         if (!mInternalSetDrawable) {
    101             // Only clear the cache if we were externally called.
    102             resetCache();
    103         }
    104         super.setImageDrawable(drawable);
    105     }
    106 
    107     @Override
    108     @RemotableViewMethod
    109     public void setImageBitmap(Bitmap bm) {
    110         resetCache();
    111         super.setImageBitmap(bm);
    112     }
    113 
    114     @Override
    115     protected void onConfigurationChanged(Configuration newConfig) {
    116         super.onConfigurationChanged(newConfig);
    117         resetCache();
    118     }
    119 
    120     /**
    121      * @return true if the currently set image is the same as {@param icon}
    122      */
    123     private synchronized boolean testAndSetCache(Icon icon) {
    124         if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
    125             String iconPackage = normalizeIconPackage(icon);
    126 
    127             boolean isCached = mLastResId != 0
    128                     && icon.getResId() == mLastResId
    129                     && Objects.equals(iconPackage, mLastPackage);
    130 
    131             mLastPackage = iconPackage;
    132             mLastResId = icon.getResId();
    133 
    134             return isCached;
    135         } else {
    136             resetCache();
    137             return false;
    138         }
    139     }
    140 
    141     /**
    142      * @return true if the currently set image is the same as {@param resId}
    143      */
    144     private synchronized boolean testAndSetCache(int resId) {
    145         boolean isCached;
    146         if (resId == 0 || mLastResId == 0) {
    147             isCached = false;
    148         } else {
    149             isCached = resId == mLastResId && null == mLastPackage;
    150         }
    151         mLastPackage = null;
    152         mLastResId = resId;
    153         return isCached;
    154     }
    155 
    156     /**
    157      * Returns the normalized package name of {@param icon}.
    158      * @return null if icon is null or if the icons package is null, empty or matches the current
    159      *         context. Otherwise returns the icon's package context.
    160      */
    161     private String normalizeIconPackage(Icon icon) {
    162         if (icon == null) {
    163             return null;
    164         }
    165 
    166         String pkg = icon.getResPackage();
    167         if (TextUtils.isEmpty(pkg)) {
    168             return null;
    169         }
    170         if (pkg.equals(mContext.getPackageName())) {
    171             return null;
    172         }
    173         return pkg;
    174     }
    175 
    176     private synchronized void resetCache() {
    177         mLastResId = 0;
    178         mLastPackage = null;
    179     }
    180 
    181     /**
    182      * Set the icon to be forcibly hidden, even when it's visibility is changed to visible.
    183      */
    184     public void setForceHidden(boolean forceHidden) {
    185         mForceHidden = forceHidden;
    186         updateVisibility();
    187     }
    188 
    189     @Override
    190     @RemotableViewMethod
    191     public void setVisibility(int visibility) {
    192         mDesiredVisibility = visibility;
    193         updateVisibility();
    194     }
    195 
    196     private void updateVisibility() {
    197         int visibility = mDesiredVisibility == VISIBLE && mForceHidden ? INVISIBLE
    198                 : mDesiredVisibility;
    199         super.setVisibility(visibility);
    200     }
    201 }
    202