summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Adrian Roos <roosa@google.com> 2016-08-10 16:52:14 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2016-08-10 16:52:15 +0000
commitd4a95806ab86efadc1177e57a358b6a3323e3fc4 (patch)
tree326452d64e0b43af16a5029176094df7d0752853
parentab6e7c3502e191abb956f9f17959b627e230b0db (diff)
parentc4337a35696b5456a18b9de5e29841612364a2e5 (diff)
Merge "Notification: Reuse drawable in Header if Icon unchanged" into nyc-mr1-dev
-rw-r--r--core/java/android/app/Notification.java3
-rw-r--r--core/java/com/android/internal/widget/CachingIconView.java178
-rw-r--r--core/res/res/layout/notification_template_header.xml2
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"