From 39fa59fc4907d3c8faad41bf20e1f855dbcda5e6 Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Tue, 25 Feb 2014 15:38:45 +0100 Subject: Change appearance of notifications to light theme. Change-Id: Ic15e96582d6b46b4dc195b1c7c0cef38f25f0d38 --- .../res/drawable/notification_icon_legacy_bg.xml | 22 ++ .../drawable/notification_icon_legacy_bg_inset.xml | 21 ++ packages/SystemUI/res/values/colors.xml | 2 + .../src/com/android/systemui/ImageUtils.java | 82 ++++++++ .../android/systemui/statusbar/BaseStatusBar.java | 226 ++++++++++++++++++++- 5 files changed, 347 insertions(+), 6 deletions(-) create mode 100644 packages/SystemUI/res/drawable/notification_icon_legacy_bg.xml create mode 100644 packages/SystemUI/res/drawable/notification_icon_legacy_bg_inset.xml create mode 100644 packages/SystemUI/src/com/android/systemui/ImageUtils.java (limited to 'packages') diff --git a/packages/SystemUI/res/drawable/notification_icon_legacy_bg.xml b/packages/SystemUI/res/drawable/notification_icon_legacy_bg.xml new file mode 100644 index 0000000..4ac67c3 --- /dev/null +++ b/packages/SystemUI/res/drawable/notification_icon_legacy_bg.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/packages/SystemUI/res/drawable/notification_icon_legacy_bg_inset.xml b/packages/SystemUI/res/drawable/notification_icon_legacy_bg_inset.xml new file mode 100644 index 0000000..96c5573 --- /dev/null +++ b/packages/SystemUI/res/drawable/notification_icon_legacy_bg_inset.xml @@ -0,0 +1,21 @@ + + + + diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 5cf0453..a59dc75 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -39,6 +39,8 @@ #FFFFFFFF #ff111111 #ff454545 + #ff4285F4 + #ff555555 #ff404040 diff --git a/packages/SystemUI/src/com/android/systemui/ImageUtils.java b/packages/SystemUI/src/com/android/systemui/ImageUtils.java new file mode 100644 index 0000000..540ba20 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/ImageUtils.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2014 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.systemui; + +import android.graphics.Bitmap; + +/** + * Utility class for image analysis and processing. + */ +public class ImageUtils { + + // Amount (max is 255) that two channels can differ before the color is no longer "gray". + private static final int TOLERANCE = 20; + + // Alpha amount for which values below are considered transparent. + private static final int ALPHA_TOLERANCE = 50; + + private int[] mTempBuffer; + + /** + * Checks whether a bitmap is grayscale. Grayscale here means "very close to a perfect + * gray". + */ + public boolean isGrayscale(Bitmap bitmap) { + final int height = bitmap.getHeight(); + final int width = bitmap.getWidth(); + int size = height*width; + + ensureBufferSize(size); + bitmap.getPixels(mTempBuffer, 0, width, 0, 0, width, height); + for (int i = 0; i < size; i++) { + if (!isGrayscale(mTempBuffer[i])) { + return false; + } + } + return true; + } + + /** + * Makes sure that {@code mTempBuffer} has at least length {@code size}. + */ + private void ensureBufferSize(int size) { + if (mTempBuffer == null || mTempBuffer.length < size) { + mTempBuffer = new int[size]; + } + } + + /** + * Classifies a color as grayscale or not. Grayscale here means "very close to a perfect + * gray"; if all three channels are approximately equal, this will return true. + * + * Note that really transparent colors are always grayscale. + */ + public boolean isGrayscale(int color) { + int alpha = 0xFF & (color >> 24); + if (alpha < ALPHA_TOLERANCE) { + return true; + } + + int r = 0xFF & (color >> 16); + int g = 0xFF & (color >> 8); + int b = 0xFF & color; + + return Math.abs(r - g) < TOLERANCE + && Math.abs(r - b) < TOLERANCE + && Math.abs(g - b) < TOLERANCE; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index fb11743..eb07d88 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -28,9 +28,15 @@ import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.ColorStateList; import android.content.res.Configuration; import android.database.ContentObserver; +import android.graphics.Color; +import android.graphics.PorterDuff; import android.graphics.Rect; +import android.graphics.drawable.AnimationDrawable; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.os.Handler; @@ -44,9 +50,13 @@ import android.provider.Settings; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; import android.service.notification.StatusBarNotification; +import android.text.SpannableStringBuilder; +import android.text.Spanned; import android.text.TextUtils; +import android.text.style.TextAppearanceSpan; import android.util.Log; import android.util.SparseBooleanArray; +import android.view.ContextThemeWrapper; import android.view.Display; import android.view.IWindowManager; import android.view.LayoutInflater; @@ -57,6 +67,7 @@ import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.WindowManager; import android.view.WindowManagerGlobal; +import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.PopupMenu; @@ -67,6 +78,7 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; import com.android.internal.widget.SizeAdaptiveLayout; +import com.android.systemui.ImageUtils; import com.android.systemui.R; import com.android.systemui.RecentsComponent; import com.android.systemui.SearchPanelView; @@ -76,6 +88,7 @@ import com.android.systemui.statusbar.policy.NotificationRowLayout; import java.util.ArrayList; import java.util.Locale; +import java.util.Stack; public abstract class BaseStatusBar extends SystemUI implements CommandQueue.Callbacks { @@ -140,6 +153,8 @@ public abstract class BaseStatusBar extends SystemUI implements // public mode, private notifications, etc private boolean mLockscreenPublicMode = false; private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray(); + private Context mLightThemeContext; + private ImageUtils mImageUtils = new ImageUtils(); // UI-specific methods @@ -261,6 +276,8 @@ public abstract class BaseStatusBar extends SystemUI implements true, mLockscreenSettingsObserver, UserHandle.USER_ALL); + mLightThemeContext = new RemoteViewsThemeContextWrapper(mContext, + android.R.style.Theme_Holo_Light); mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); @@ -412,6 +429,158 @@ public abstract class BaseStatusBar extends SystemUI implements } } + private void processLegacyHoloNotification(StatusBarNotification sbn, View content) { + + // TODO: Also skip processing if it is a holo-style notification. + // If the notification is custom, we can't process it. + if (!sbn.getNotification().extras.getBoolean(Notification.EXTRA_BUILDER_REMOTE_VIEWS)) { + return; + } + + processLegacyHoloLargeIcon(content); + processLegacyHoloActions(content); + processLegacyNotificationIcon(content); + processLegacyTextViews(content); + } + + /** + * @return the context to be used for the inflation of the specified {@code sbn}; this is + * dependent whether the notification is quantum-style or holo-style + */ + private Context getInflationContext(StatusBarNotification sbn) { + + // TODO: Adjust this logic when we change the theme of the status bar windows. + if (sbn.getNotification().extras.getBoolean(Notification.EXTRA_BUILDER_REMOTE_VIEWS)) { + return mLightThemeContext; + } else { + return mContext; + } + } + + private void processLegacyNotificationIcon(View content) { + View v = content.findViewById(com.android.internal.R.id.right_icon); + if (v != null & v instanceof ImageView) { + ImageView iv = (ImageView) v; + Drawable d = iv.getDrawable(); + if (isMonochrome(d)) { + d.mutate(); + d.setColorFilter(mLightThemeContext.getResources().getColor( + R.color.notification_action_legacy_color_filter), PorterDuff.Mode.MULTIPLY); + } + } + } + + private void processLegacyHoloLargeIcon(View content) { + View v = content.findViewById(com.android.internal.R.id.icon); + if (v != null & v instanceof ImageView) { + ImageView iv = (ImageView) v; + if (isMonochrome(iv.getDrawable())) { + iv.setBackground(mLightThemeContext.getResources().getDrawable( + R.drawable.notification_icon_legacy_bg_inset)); + } + } + } + + private boolean isMonochrome(Drawable d) { + if (d == null) { + return false; + } else if (d instanceof BitmapDrawable) { + BitmapDrawable bd = (BitmapDrawable) d; + return bd.getBitmap() != null && mImageUtils.isGrayscale(bd.getBitmap()); + } else if (d instanceof AnimationDrawable) { + AnimationDrawable ad = (AnimationDrawable) d; + int count = ad.getNumberOfFrames(); + return count > 0 && isMonochrome(ad.getFrame(0)); + } else { + return false; + } + } + + private void processLegacyHoloActions(View content) { + View v = content.findViewById(com.android.internal.R.id.actions); + if (v != null & v instanceof ViewGroup) { + ViewGroup vg = (ViewGroup) v; + int childCount = vg.getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = vg.getChildAt(i); + if (child instanceof Button) { + Button button = (Button) child; + Drawable[] compoundDrawables = button.getCompoundDrawablesRelative(); + if (isMonochrome(compoundDrawables[0])) { + Drawable d = compoundDrawables[0]; + d.mutate(); + d.setColorFilter(mLightThemeContext.getResources().getColor( + R.color.notification_action_legacy_color_filter), + PorterDuff.Mode.MULTIPLY); + } + } + } + } + } + + private void processLegacyTextViews(View content) { + Stack viewStack = new Stack(); + viewStack.push(content); + while(!viewStack.isEmpty()) { + View current = viewStack.pop(); + if(current instanceof ViewGroup){ + ViewGroup currentGroup = (ViewGroup) current; + int numChildren = currentGroup.getChildCount(); + for(int i=0;i