diff options
| author | 2016-08-10 16:52:14 +0000 | |
|---|---|---|
| committer | 2016-08-10 16:52:15 +0000 | |
| commit | d4a95806ab86efadc1177e57a358b6a3323e3fc4 (patch) | |
| tree | 326452d64e0b43af16a5029176094df7d0752853 | |
| parent | ab6e7c3502e191abb956f9f17959b627e230b0db (diff) | |
| parent | c4337a35696b5456a18b9de5e29841612364a2e5 (diff) | |
Merge "Notification: Reuse drawable in Header if Icon unchanged" into nyc-mr1-dev
| -rw-r--r-- | core/java/android/app/Notification.java | 3 | ||||
| -rw-r--r-- | core/java/com/android/internal/widget/CachingIconView.java | 178 | ||||
| -rw-r--r-- | core/res/res/layout/notification_template_header.xml | 2 |
3 files changed, 181 insertions, 2 deletions
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 6e01922d20bf..29ed97e78dcb 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -3256,7 +3256,8 @@ public class Notification implements Parcelable * Resets the notification header to its original state */ private void resetNotificationHeader(RemoteViews contentView) { - contentView.setImageViewResource(R.id.icon, 0); + // Small icon doesn't need to be reset, as it's always set. Resetting would prevent + // re-using the drawable when the notification is updated. contentView.setBoolean(R.id.notification_header, "setExpanded", false); contentView.setTextViewText(R.id.app_name_text, null); contentView.setViewVisibility(R.id.chronometer, View.GONE); diff --git a/core/java/com/android/internal/widget/CachingIconView.java b/core/java/com/android/internal/widget/CachingIconView.java new file mode 100644 index 000000000000..293b77b91d37 --- /dev/null +++ b/core/java/com/android/internal/widget/CachingIconView.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2016 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.internal.widget; + +import android.annotation.DrawableRes; +import android.annotation.Nullable; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; +import android.net.Uri; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.RemotableViewMethod; +import android.widget.ImageView; +import android.widget.RemoteViews; + +import libcore.util.Objects; + +/** + * An ImageView for displaying an Icon. Avoids reloading the Icon when possible. + */ +@RemoteViews.RemoteView +public class CachingIconView extends ImageView { + + private String mLastPackage; + private int mLastResId; + private boolean mInternalSetDrawable; + + public CachingIconView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + @Override + @RemotableViewMethod(asyncImpl="setImageIconAsync") + public void setImageIcon(@Nullable Icon icon) { + if (!testAndSetCache(icon)) { + mInternalSetDrawable = true; + // This calls back to setImageDrawable, make sure we don't clear the cache there. + super.setImageIcon(icon); + mInternalSetDrawable = false; + } + } + + @Override + public Runnable setImageIconAsync(@Nullable Icon icon) { + resetCache(); + return super.setImageIconAsync(icon); + } + + @Override + @RemotableViewMethod(asyncImpl="setImageResourceAsync") + public void setImageResource(@DrawableRes int resId) { + if (!testAndSetCache(resId)) { + mInternalSetDrawable = true; + // This calls back to setImageDrawable, make sure we don't clear the cache there. + super.setImageResource(resId); + mInternalSetDrawable = false; + } + } + + @Override + public Runnable setImageResourceAsync(@DrawableRes int resId) { + resetCache(); + return super.setImageResourceAsync(resId); + } + + @Override + @RemotableViewMethod(asyncImpl="setImageURIAsync") + public void setImageURI(@Nullable Uri uri) { + resetCache(); + super.setImageURI(uri); + } + + @Override + public Runnable setImageURIAsync(@Nullable Uri uri) { + resetCache(); + return super.setImageURIAsync(uri); + } + + @Override + public void setImageDrawable(@Nullable Drawable drawable) { + if (!mInternalSetDrawable) { + // Only clear the cache if we were externally called. + resetCache(); + } + super.setImageDrawable(drawable); + } + + @Override + @RemotableViewMethod + public void setImageBitmap(Bitmap bm) { + resetCache(); + super.setImageBitmap(bm); + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + resetCache(); + } + + /** + * @return true if the currently set image is the same as {@param icon} + */ + private synchronized boolean testAndSetCache(Icon icon) { + if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) { + String iconPackage = normalizeIconPackage(icon); + + boolean isCached = mLastResId != 0 + && icon.getResId() == mLastResId + && Objects.equal(iconPackage, mLastPackage); + + mLastPackage = iconPackage; + mLastResId = icon.getResId(); + + return isCached; + } else { + resetCache(); + return false; + } + } + + /** + * @return true if the currently set image is the same as {@param resId} + */ + private synchronized boolean testAndSetCache(int resId) { + boolean isCached; + if (resId == 0 || mLastResId == 0) { + isCached = false; + } else { + isCached = resId == mLastResId && null == mLastPackage; + } + mLastPackage = null; + mLastResId = resId; + return isCached; + } + + /** + * Returns the normalized package name of {@param icon}. + * @return null if icon is null or if the icons package is null, empty or matches the current + * context. Otherwise returns the icon's package context. + */ + private String normalizeIconPackage(Icon icon) { + if (icon == null) { + return null; + } + + String pkg = icon.getResPackage(); + if (TextUtils.isEmpty(pkg)) { + return null; + } + if (pkg.equals(mContext.getPackageName())) { + return null; + } + return pkg; + } + + private synchronized void resetCache() { + mLastResId = 0; + mLastPackage = null; + } +} diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index 38f671c21cbb..1f71a180cdf4 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -26,7 +26,7 @@ android:paddingBottom="16dp" android:paddingStart="@dimen/notification_content_margin_start" android:paddingEnd="16dp"> - <ImageView + <com.android.internal.widget.CachingIconView android:id="@+id/icon" android:layout_width="18dp" android:layout_height="18dp" |