OSDN Git Service

Update notification visuals (part 2)
authorTony Wickham <twickham@google.com>
Fri, 24 Feb 2017 23:08:13 +0000 (15:08 -0800)
committerTony Wickham <twickham@google.com>
Thu, 2 Mar 2017 19:27:16 +0000 (11:27 -0800)
- Background is now white, and color beneath and divider color
  updated accordingly (not from color extraction)
- Removed overflow text ("+6") and added it to a header
  ("6 Notifications"). Use "..." instead if there is an overflow.
- Even spaced out icons in notification footer between the
  far right icon and the ellipsis
- Remove code to change arrow tint, since it is always white
  now. This also fixes the issue where it was drawn as a rect.

Bug: 35766387
Change-Id: I03bfda4ff029f23dd8b3dd1b72f534ea0e2c0816

15 files changed:
res/drawable/horizontal_ellipsis.xml [new file with mode: 0644]
res/layout/notification.xml
res/layout/notification_footer.xml
res/layout/notification_main.xml
res/values/colors.xml
res/values/dimens.xml
res/values/strings.xml
src/com/android/launcher3/BubbleTextView.java
src/com/android/launcher3/FastBitmapDrawable.java
src/com/android/launcher3/notification/NotificationFooterLayout.java
src/com/android/launcher3/notification/NotificationHeaderView.java [new file with mode: 0644]
src/com/android/launcher3/notification/NotificationItemView.java
src/com/android/launcher3/notification/NotificationMainView.java
src/com/android/launcher3/popup/PopupContainerWithArrow.java
src/com/android/launcher3/popup/PopupItemView.java

diff --git a/res/drawable/horizontal_ellipsis.xml b/res/drawable/horizontal_ellipsis.xml
new file mode 100644 (file)
index 0000000..ad08529
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="@dimen/horizontal_ellipsis_size"
+        android:height="@dimen/horizontal_ellipsis_size"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/textColorSecondary" >
+
+    <path
+        android:pathData="M6 10c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm12 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm-6 0c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"
+        android:fillColor="@android:color/white" />
+</vector>
\ No newline at end of file
index e148cbb..6922ad9 100644 (file)
@@ -20,7 +20,8 @@
     android:layout_width="@dimen/bg_popup_item_width"
     android:layout_height="wrap_content"
     android:elevation="@dimen/deep_shortcuts_elevation"
-    android:background="@drawable/bg_white_round_rect">
+    android:background="@drawable/bg_white_round_rect"
+    android:backgroundTint="@color/notification_color_beneath">
 
     <RelativeLayout
         android:layout_width="match_parent"
         android:orientation="vertical"
         android:clipChildren="false">
 
+        <com.android.launcher3.notification.NotificationHeaderView
+            android:id="@+id/header"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/notification_header_height"
+            android:orientation="horizontal"
+            android:paddingStart="@dimen/notification_padding"
+            android:background="@color/notification_header_background_color"
+            android:elevation="@dimen/notification_elevation">
+            <TextView
+                android:id="@+id/notification_count"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:gravity="start|center_vertical"
+                android:paddingEnd="@dimen/notification_header_padding_after_count"
+                android:textSize="@dimen/notification_main_text_size"
+                android:textColor="?android:attr/textColorPrimary" />
+            <TextView
+                android:id="@+id/notification_text"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:gravity="start|center_vertical"
+                android:textSize="@dimen/notification_main_text_size"
+                android:textColor="?android:attr/textColorSecondary" />
+        </com.android.launcher3.notification.NotificationHeaderView>
+
         <include layout="@layout/notification_main"
             android:id="@+id/main_view"
             android:layout_width="match_parent"
-            android:layout_height="@dimen/notification_main_height" />
+            android:layout_height="@dimen/notification_main_height"
+            android:layout_below="@id/header" />
+
+        <View
+            android:id="@+id/divider"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/popup_item_divider_height"
+            android:background="@color/divider_color"
+            android:layout_below="@id/main_view"/>
 
         <include layout="@layout/notification_footer"
             android:id="@+id/footer"
             android:layout_width="match_parent"
             android:layout_height="@dimen/notification_footer_height"
-            android:layout_below="@id/main_view" />
+            android:layout_below="@id/divider" />
 
     </RelativeLayout>
 
index 5428208..f1f5724 100644 (file)
 <com.android.launcher3.notification.NotificationFooterLayout
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical"
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:elevation="@dimen/notification_elevation"
-    android:clipChildren="false" >
-
-    <View
-        android:id="@+id/divider"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/popup_item_divider_height"/>
+    android:clipChildren="false"
+    android:layout_gravity="center_vertical"
+    android:background="@color/notification_background_color">
 
     <LinearLayout
         android:id="@+id/icon_row"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="horizontal"
-        android:gravity="end"
+        android:gravity="end|center_vertical"
         android:padding="@dimen/notification_footer_icon_row_padding"
         android:clipToPadding="false"
         android:clipChildren="false"/>
 
+    <View
+        android:id="@+id/overflow"
+        android:layout_width="@dimen/horizontal_ellipsis_size"
+        android:layout_height="@dimen/horizontal_ellipsis_size"
+        android:background="@drawable/horizontal_ellipsis"
+        android:layout_marginStart="@dimen/horizontal_ellipsis_offset"
+        android:layout_gravity="start|center_vertical" />
+
 </com.android.launcher3.notification.NotificationFooterLayout>
 
index d036fe5..9c5847f 100644 (file)
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="horizontal"
     android:focusable="true"
-    android:padding="@dimen/notification_padding"
     android:elevation="@dimen/notification_elevation" >
 
     <LinearLayout
+        android:id="@+id/text_and_background"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="vertical"
-        android:layout_weight="1"
-        android:gravity="center_vertical">
+        android:gravity="center_vertical"
+        android:background="@color/notification_background_color"
+        android:paddingStart="@dimen/notification_padding"
+        android:paddingEnd="@dimen/notification_main_text_padding_end">
         <TextView
             android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
             android:textAlignment="viewStart"
             android:fontFamily="sans-serif"
-            android:textSize="14sp"
-            android:textColor="?android:attr/textColorSecondary"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
+            android:textSize="@dimen/notification_main_text_size"
+            android:textColor="?android:attr/textColorPrimary"
+            android:lines="1"
+            android:ellipsize="end" />
 
         <TextView
             android:id="@+id/text"
-            android:paddingEnd="4dp"
-            android:textSize="12sp"
-            android:textAlignment="viewStart"
-            android:fontFamily="sans-serif"
-            android:textColor="?android:attr/textColorTertiary"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
+            android:layout_height="wrap_content"
+            android:fontFamily="sans-serif"
+            android:textSize="@dimen/notification_main_text_size"
+            android:textColor="?android:attr/textColorSecondary"
+            android:lines="1"
+            android:ellipsize="end" />
     </LinearLayout>
 
     <View
         android:id="@+id/popup_item_icon"
         android:layout_width="@dimen/notification_icon_size"
         android:layout_height="@dimen/notification_icon_size"
-        android:layout_weight="0"
-        android:layout_gravity="center_vertical" />
+        android:layout_marginEnd="@dimen/notification_padding"
+        android:layout_gravity="center_vertical|end" />
 
 </com.android.launcher3.notification.NotificationMainView>
 
index a02df16..68f0092 100644 (file)
@@ -36,4 +36,8 @@
     <color name="spring_loaded_highlighted_panel_border_color">#FFF</color>
 
     <color name="notification_icon_default_color">#757575</color> <!-- Gray 600 -->
+    <color name="notification_header_background_color">#F5F5F5</color> <!-- Gray 100 -->
+    <color name="notification_background_color">#FFF</color>
+    <color name="notification_color_beneath">#E0E0E0</color> <!-- Gray 300 -->
+    <color name="divider_color">@color/notification_color_beneath</color>
 </resources>
index 8fee26b..132ae07 100644 (file)
     <dimen name="badge_text_size">12dp</dimen>
     <dimen name="badge_small_padding">0dp</dimen>
     <dimen name="badge_large_padding">3dp</dimen>
-    <dimen name="notification_icon_size">28dp</dimen>
-    <dimen name="notification_footer_icon_size">24dp</dimen>
+    <dimen name="notification_icon_size">24dp</dimen>
+    <dimen name="notification_footer_icon_size">18dp</dimen>
 
 <!-- Notifications -->
     <dimen name="bg_round_rect_radius">12dp</dimen>
     <dimen name="notification_padding">12dp</dimen>
-    <!-- (icon_size - footer_icon_size) / 2 -->
-    <dimen name="notification_footer_icon_row_padding">2dp</dimen>
+    <!-- notification_padding + (icon_size - footer_icon_size) / 2 -->
+    <dimen name="notification_footer_icon_row_padding">15dp</dimen>
+    <dimen name="notification_header_padding_after_count">8dp</dimen>
+    <dimen name="notification_header_height">32dp</dimen>
     <dimen name="notification_main_height">60dp</dimen>
     <dimen name="notification_footer_height">@dimen/bg_popup_item_height</dimen>
+    <dimen name="notification_header_text_size">12dp</dimen>
+    <dimen name="notification_main_text_size">14dp</dimen>
+    <!-- notification_icon_size + notification+padding + padding we want between icon and text -->
+    <dimen name="notification_main_text_padding_end">40dp</dimen>
     <dimen name="notification_elevation">2dp</dimen>
+    <dimen name="horizontal_ellipsis_size">18dp</dimen>
+    <!-- arrow_horizontal_offset - (ellipsis_size - arrow_width) / 2 -->
+    <dimen name="horizontal_ellipsis_offset">15dp</dimen>
     <dimen name="popup_item_divider_height">0.5dp</dimen>
     <dimen name="swipe_helper_falsing_threshold">70dp</dimen>
 
index c1280c7..58bfb49 100644 (file)
          The text must fit in the size of a small icon [CHAR_LIMIT=3] -->
     <string name="deep_notifications_overflow" translatable="false">+%1$d</string>
     <!-- Text to display as the header above notifications. [CHAR_LIMIT=30] -->
-    <string name="notifications_header" translatable="false">Notifications</string>
+    <plurals name="notifications_header" translatable="false">
+        <item quantity="one">Notification</item>
+        <item quantity="other">Notifications</item>
+    </plurals>
 
     <!-- Drag and drop -->
     <skip />
index 107d700..bad7018 100644 (file)
@@ -42,9 +42,9 @@ import com.android.launcher3.badge.BadgeRenderer;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.graphics.DrawableFactory;
 import com.android.launcher3.graphics.HolographicOutlineHelper;
-import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.graphics.PreloadIconDrawable;
 import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.popup.PopupContainerWithArrow;
 
 import java.text.NumberFormat;
 
@@ -502,15 +502,14 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
         if (mIcon instanceof FastBitmapDrawable) {
             BadgeInfo badgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo);
             BadgeRenderer badgeRenderer = mLauncher.getDeviceProfile().mBadgeRenderer;
+            PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(mLauncher);
+            if (popup != null) {
+                popup.updateNotificationHeader(badgeInfo, itemInfo);
+            }
             ((FastBitmapDrawable) mIcon).applyIconBadge(badgeInfo, badgeRenderer, animate);
         }
     }
 
-    public IconPalette getIconPalette() {
-        return mIcon instanceof FastBitmapDrawable ? ((FastBitmapDrawable) mIcon).getIconPalette()
-                : null;
-    }
-
     /**
      * Sets the icon for this view based on the layout direction.
      */
index be3ba90..5a44f75 100644 (file)
@@ -167,7 +167,7 @@ public class FastBitmapDrawable extends Drawable {
         }
     }
 
-    public IconPalette getIconPalette() {
+    protected IconPalette getIconPalette() {
         if (mIconPalette == null) {
             mIconPalette = IconPalette.fromDominantColor(Utilities
                     .findDominantColorByHue(mBitmap, 20));
index 62126ef..2e80341 100644 (file)
@@ -21,14 +21,15 @@ import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.FrameLayout;
 import android.widget.LinearLayout;
-import android.widget.TextView;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAnimUtils;
@@ -36,7 +37,6 @@ import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.PropertyListBuilder;
 import com.android.launcher3.anim.PropertyResetListener;
-import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 
 import java.util.ArrayList;
@@ -44,16 +44,16 @@ import java.util.Iterator;
 import java.util.List;
 
 /**
- * A {@link LinearLayout} that contains only icons of notifications.
- * If there are more than {@link #MAX_FOOTER_NOTIFICATIONS} icons, we add a "+x" overflow.
+ * A {@link FrameLayout} that contains only icons of notifications.
+ * If there are more than {@link #MAX_FOOTER_NOTIFICATIONS} icons, we add a "..." overflow.
  */
-public class NotificationFooterLayout extends LinearLayout {
+public class NotificationFooterLayout extends FrameLayout {
 
     public interface IconAnimationEndListener {
         void onIconAnimationEnd(NotificationInfo animatedNotification);
     }
 
-    private static final int MAX_FOOTER_NOTIFICATIONS = 4;
+    private static final int MAX_FOOTER_NOTIFICATIONS = 5;
 
     private static final Rect sTempRect = new Rect();
 
@@ -61,11 +61,10 @@ public class NotificationFooterLayout extends LinearLayout {
     private final List<NotificationInfo> mOverflowNotifications = new ArrayList<>();
     private final boolean mRtl;
 
-    LinearLayout.LayoutParams mIconLayoutParams;
+    FrameLayout.LayoutParams mIconLayoutParams;
+    private View mOverflowEllipsis;
     private LinearLayout mIconRow;
-    private final ColorDrawable mBackgroundColor;
-    private int mTextColor;
-    private TextView mOverflowView;
+    private int mBackgroundColor;
 
     public NotificationFooterLayout(Context context) {
         this(context, null, 0);
@@ -78,33 +77,29 @@ public class NotificationFooterLayout extends LinearLayout {
     public NotificationFooterLayout(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
-        mRtl = Utilities.isRtl(getResources());
+        Resources res = getResources();
+        mRtl = Utilities.isRtl(res);
 
-        int size = getResources().getDimensionPixelSize(
-                R.dimen.notification_footer_icon_size);
-        int padding = getResources().getDimensionPixelSize(R.dimen.notification_padding);
-        mIconLayoutParams = new LayoutParams(size, size);
-        mIconLayoutParams.setMarginEnd(padding);
+        int iconSize = res.getDimensionPixelSize(R.dimen.notification_footer_icon_size);
+        mIconLayoutParams = new LayoutParams(iconSize, iconSize);
         mIconLayoutParams.gravity = Gravity.CENTER_VERTICAL;
-
-        mBackgroundColor = new ColorDrawable();
-        setBackground(mBackgroundColor);
+        // Compute margin start for each icon such that the icons between the first one
+        // and the ellipsis are evenly spaced out.
+        int paddingEnd = res.getDimensionPixelSize(R.dimen.notification_footer_icon_row_padding);
+        int ellipsisSpace = res.getDimensionPixelSize(R.dimen.horizontal_ellipsis_offset)
+                + res.getDimensionPixelSize(R.dimen.horizontal_ellipsis_size);
+        int footerWidth = res.getDimensionPixelSize(R.dimen.bg_popup_item_width);
+        int availableIconRowSpace = footerWidth - paddingEnd - ellipsisSpace
+                - iconSize * MAX_FOOTER_NOTIFICATIONS;
+        mIconLayoutParams.setMarginStart(availableIconRowSpace / MAX_FOOTER_NOTIFICATIONS);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        mOverflowEllipsis = findViewById(R.id.overflow);
         mIconRow = (LinearLayout) findViewById(R.id.icon_row);
-    }
-
-    public void applyColors(IconPalette iconPalette) {
-        mBackgroundColor.setColor(iconPalette.backgroundColor);
-        findViewById(R.id.divider).setBackgroundColor(iconPalette.secondaryColor);
-        mTextColor = iconPalette.textColor;
-    }
-
-    public int getBackgroundColor() {
-        return mBackgroundColor.getColor();
+        mBackgroundColor = ((ColorDrawable) getBackground()).getColor();
     }
 
     /**
@@ -128,35 +123,26 @@ public class NotificationFooterLayout extends LinearLayout {
 
         for (int i = 0; i < mNotifications.size(); i++) {
             NotificationInfo info = mNotifications.get(i);
-            addNotificationIconForInfo(info, false /* fromOverflow */);
+            addNotificationIconForInfo(info);
         }
+        updateOverflowEllipsisVisibility();
+    }
 
-        if (!mOverflowNotifications.isEmpty()) {
-            mOverflowView = new TextView(getContext());
-            mOverflowView.setTextColor(mTextColor);
-            updateOverflowText();
-            mIconRow.addView(mOverflowView, 0, mIconLayoutParams);
-        }
+    private void updateOverflowEllipsisVisibility() {
+        mOverflowEllipsis.setVisibility(mOverflowNotifications.isEmpty() ? GONE : VISIBLE);
     }
 
-    private void addNotificationIconForInfo(NotificationInfo info, boolean fromOverflow) {
+    /**
+     * Creates an icon for the given NotificationInfo, and adds it to the icon row.
+     * @return the icon view that was added
+     */
+    private View addNotificationIconForInfo(NotificationInfo info) {
         View icon = new View(getContext());
-        icon.setBackground(info.getIconForBackground(getContext(), getBackgroundColor()));
+        icon.setBackground(info.getIconForBackground(getContext(), mBackgroundColor));
         icon.setOnClickListener(info);
-        int addIndex = 0;
-        if (fromOverflow) {
-            // Add the notification before the overflow view.
-            addIndex = 1;
-            icon.setAlpha(0);
-            icon.animate().alpha(1);
-        }
         icon.setTag(info);
-        mIconRow.addView(icon, addIndex, mIconLayoutParams);
-    }
-
-    private void updateOverflowText() {
-        mOverflowView.setText(getResources().getString(R.string.deep_notifications_overflow,
-                mOverflowNotifications.size()));
+        mIconRow.addView(icon, 0, mIconLayoutParams);
+        return icon;
     }
 
     public void animateFirstNotificationTo(Rect toBounds,
@@ -180,16 +166,22 @@ public class NotificationFooterLayout extends LinearLayout {
         animation.play(moveAndScaleIcon);
 
         // Shift all notifications (not the overflow) over to fill the gap.
-        int gapWidth = mIconLayoutParams.width + mIconLayoutParams.getMarginEnd();
+        int gapWidth = mIconLayoutParams.width + mIconLayoutParams.getMarginStart();
         if (mRtl) {
             gapWidth = -gapWidth;
         }
-        int numIcons = mIconRow.getChildCount() - 1;
-        // We have to set the translation X to 0 when the new main notification
+        if (!mOverflowNotifications.isEmpty()) {
+            NotificationInfo notification = mOverflowNotifications.remove(0);
+            mNotifications.add(notification);
+            View iconFromOverflow = addNotificationIconForInfo(notification);
+            animation.play(ObjectAnimator.ofFloat(iconFromOverflow, ALPHA, 0, 1));
+        }
+        int numIcons = mIconRow.getChildCount() - 1; // All children besides the one leaving.
+        // We have to reset the translation X to 0 when the new main notification
         // is removed from the footer.
         PropertyResetListener<View, Float> propertyResetListener
                 = new PropertyResetListener<>(TRANSLATION_X, 0f);
-        for (int i = mOverflowNotifications.isEmpty() ? 0 : 1; i < numIcons; i++) {
+        for (int i = 0; i < numIcons; i++) {
             final View child = mIconRow.getChildAt(i);
             Animator shiftChild = ObjectAnimator.ofFloat(child, TRANSLATION_X, gapWidth);
             shiftChild.addListener(propertyResetListener);
@@ -201,19 +193,7 @@ public class NotificationFooterLayout extends LinearLayout {
     private void removeViewFromIconRow(View child) {
         mIconRow.removeView(child);
         mNotifications.remove((NotificationInfo) child.getTag());
-        if (!mOverflowNotifications.isEmpty()) {
-            NotificationInfo notification = mOverflowNotifications.remove(0);
-            mNotifications.add(notification);
-            addNotificationIconForInfo(notification, true /* fromOverflow */);
-        }
-        if (mOverflowView != null) {
-            if (mOverflowNotifications.isEmpty()) {
-                mIconRow.removeView(mOverflowView);
-                mOverflowView = null;
-            } else {
-                updateOverflowText();
-            }
-        }
+        updateOverflowEllipsisVisibility();
         if (mIconRow.getChildCount() == 0) {
             // There are no more icons in the footer, so hide it.
             PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(
@@ -240,16 +220,11 @@ public class NotificationFooterLayout extends LinearLayout {
                 overflowIterator.remove();
             }
         }
-        TextView overflowView = null;
         for (int i = mIconRow.getChildCount() - 1; i >= 0; i--) {
             View child = mIconRow.getChildAt(i);
-            if (child instanceof TextView) {
-                overflowView = (TextView) child;
-            } else {
-                NotificationInfo childInfo = (NotificationInfo) child.getTag();
-                if (!notifications.contains(childInfo.notificationKey)) {
-                    removeViewFromIconRow(child);
-                }
+            NotificationInfo childInfo = (NotificationInfo) child.getTag();
+            if (!notifications.contains(childInfo.notificationKey)) {
+                removeViewFromIconRow(child);
             }
         }
     }
diff --git a/src/com/android/launcher3/notification/NotificationHeaderView.java b/src/com/android/launcher3/notification/NotificationHeaderView.java
new file mode 100644 (file)
index 0000000..e70b489
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 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.launcher3.notification;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.launcher3.R;
+
+/**
+ * A {@link LinearLayout} that contains two text views: one for the notification count
+ * and one just to say "Notification" or "Notifications"
+ */
+public class NotificationHeaderView extends LinearLayout {
+
+    private TextView mNotificationCount;
+    private TextView mNotificationText;
+
+    public NotificationHeaderView(Context context) {
+        this(context, null, 0);
+    }
+
+    public NotificationHeaderView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public NotificationHeaderView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mNotificationCount = (TextView) findViewById(R.id.notification_count);
+        mNotificationText = (TextView) findViewById(R.id.notification_text);
+    }
+
+    public void update(int notificationCount) {
+        mNotificationCount.setText(String.valueOf(notificationCount));
+        mNotificationText.setText(getResources().getQuantityString(
+                R.plurals.notifications_header, notificationCount));
+    }
+}
index efd9a3b..a340742 100644 (file)
@@ -19,7 +19,6 @@ package com.android.launcher3.notification;
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
-import android.content.res.ColorStateList;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
@@ -30,7 +29,6 @@ import android.widget.FrameLayout;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.PillHeightRevealOutlineProvider;
-import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
 import com.android.launcher3.popup.PopupItemView;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -47,7 +45,7 @@ public class NotificationItemView extends PopupItemView implements LogContainerP
 
     private static final Rect sTempRect = new Rect();
 
-    private View mDivider;
+    private NotificationHeaderView mHeader;
     private NotificationMainView mMainView;
     private NotificationFooterLayout mFooter;
     private SwipeHelper mSwipeHelper;
@@ -68,7 +66,7 @@ public class NotificationItemView extends PopupItemView implements LogContainerP
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mDivider = findViewById(R.id.divider);
+        mHeader = (NotificationHeaderView) findViewById(R.id.header);
         mMainView = (NotificationMainView) findViewById(R.id.main_view);
         mFooter = (NotificationFooterLayout) findViewById(R.id.footer);
         mSwipeHelper = new SwipeHelper(SwipeHelper.X, mMainView, getContext());
@@ -95,6 +93,10 @@ public class NotificationItemView extends PopupItemView implements LogContainerP
         return heightAnimator;
     }
 
+    public void updateHeader(int notificationCount) {
+        mHeader.update(notificationCount);
+    }
+
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (mMainView.getNotificationInfo() == null) {
@@ -114,13 +116,6 @@ public class NotificationItemView extends PopupItemView implements LogContainerP
         return mSwipeHelper.onTouchEvent(ev) || super.onTouchEvent(ev);
     }
 
-    @Override
-    protected ColorStateList getAttachedArrowColor() {
-        // This NotificationView itself has a different color that is only
-        // revealed when dismissing notifications.
-        return ColorStateList.valueOf(mFooter.getBackgroundColor());
-    }
-
     public void applyNotificationInfos(final List<NotificationInfo> notificationInfos) {
         if (notificationInfos.isEmpty()) {
             return;
@@ -135,13 +130,6 @@ public class NotificationItemView extends PopupItemView implements LogContainerP
         mFooter.commitNotificationInfos();
     }
 
-    public void applyColors(IconPalette iconPalette) {
-        setBackgroundTintList(ColorStateList.valueOf(iconPalette.secondaryColor));
-        mDivider.setBackgroundColor(iconPalette.secondaryColor);
-        mMainView.applyColors(iconPalette);
-        mFooter.applyColors(iconPalette);
-    }
-
     public void trimNotifications(final List<String> notificationKeys) {
         boolean dismissedMainNotification = !notificationKeys.contains(
                 mMainView.getNotificationInfo().notificationKey);
index 824dbf2..bb2dac0 100644 (file)
 
 package com.android.launcher3.notification;
 
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.drawable.ColorDrawable;
@@ -28,28 +24,27 @@ import android.graphics.drawable.RippleDrawable;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
-import android.widget.LinearLayout;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
 import android.widget.TextView;
 
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.R;
-import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.util.Themes;
 
 /**
- * A {@link LinearLayout} that contains a single notification, e.g. icon + title + text.
+ * A {@link android.widget.FrameLayout} that contains a single notification,
+ * e.g. icon + title + text.
  */
-public class NotificationMainView extends LinearLayout implements SwipeHelper.Callback {
-
-    private final ArgbEvaluator mArgbEvaluator = new ArgbEvaluator();
+public class NotificationMainView extends FrameLayout implements SwipeHelper.Callback {
 
     private NotificationInfo mNotificationInfo;
+    private ViewGroup mTextAndBackground;
+    private int mBackgroundColor;
     private TextView mTitleView;
     private TextView mTextView;
-    private IconPalette mIconPalette;
-    private ColorDrawable mColorBackground;
 
     public NotificationMainView(Context context) {
         this(context, null, 0);
@@ -67,16 +62,15 @@ public class NotificationMainView extends LinearLayout implements SwipeHelper.Ca
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        mTitleView = (TextView) findViewById(R.id.title);
-        mTextView = (TextView) findViewById(R.id.text);
-    }
-
-    public void applyColors(IconPalette iconPalette) {
-        mColorBackground = new ColorDrawable(iconPalette.backgroundColor);
-        RippleDrawable rippleDrawable = new RippleDrawable(ColorStateList.valueOf(
-                iconPalette.secondaryColor), mColorBackground, null);
-        setBackground(rippleDrawable);
-        mIconPalette = iconPalette;
+        mTextAndBackground = (ViewGroup) findViewById(R.id.text_and_background);
+        ColorDrawable colorBackground = (ColorDrawable) mTextAndBackground.getBackground();
+        mBackgroundColor = colorBackground.getColor();
+        RippleDrawable rippleBackground = new RippleDrawable(ColorStateList.valueOf(
+                Themes.getAttrColor(getContext(), android.R.attr.colorControlHighlight)),
+                colorBackground, null);
+        mTextAndBackground.setBackground(rippleBackground);
+        mTitleView = (TextView) mTextAndBackground.findViewById(R.id.title);
+        mTextView = (TextView) mTextAndBackground.findViewById(R.id.text);
     }
 
     public void applyNotificationInfo(NotificationInfo mainNotification, View iconView) {
@@ -88,30 +82,18 @@ public class NotificationMainView extends LinearLayout implements SwipeHelper.Ca
      */
     public void applyNotificationInfo(NotificationInfo mainNotification, View iconView,
            boolean animate) {
-        if (animate) {
-            mTitleView.setAlpha(0);
-            mTextView.setAlpha(0);
-            mColorBackground.setColor(mIconPalette.secondaryColor);
-        }
         mNotificationInfo = mainNotification;
         mTitleView.setText(mNotificationInfo.title);
         mTextView.setText(mNotificationInfo.text);
-        iconView.setBackground(mNotificationInfo.getIconForBackground(
-                getContext(), mIconPalette.backgroundColor));
+        iconView.setBackground(mNotificationInfo.getIconForBackground(getContext(),
+                mBackgroundColor));
         setOnClickListener(mNotificationInfo);
         setTranslationX(0);
         // Add a dummy ItemInfo so that logging populates the correct container and item types
         // instead of DEFAULT_CONTAINERTYPE and DEFAULT_ITEMTYPE, respectively.
         setTag(new ItemInfo());
         if (animate) {
-            AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
-            Animator textFade = ObjectAnimator.ofFloat(mTextView, View.ALPHA, 1);
-            Animator titleFade = ObjectAnimator.ofFloat(mTitleView, View.ALPHA, 1);
-            ValueAnimator colorChange = ObjectAnimator.ofObject(mColorBackground, "color",
-                    mArgbEvaluator, mIconPalette.secondaryColor, mIconPalette.backgroundColor);
-            animation.playTogether(textFade, titleFade, colorChange);
-            animation.setDuration(150);
-            animation.start();
+            ObjectAnimator.ofFloat(mTextAndBackground, ALPHA, 0, 1).setDuration(150).start();
         }
     }
 
index 0b0f88a..b8d38f5 100644 (file)
@@ -63,14 +63,12 @@ import com.android.launcher3.badge.BadgeInfo;
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.graphics.TriangleShape;
 import com.android.launcher3.notification.NotificationItemView;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.shortcuts.ShortcutsItemView;
 import com.android.launcher3.util.PackageUserKey;
 
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -184,20 +182,20 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
             orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset);
         }
 
+        ItemInfo originalItemInfo = (ItemInfo) originalIcon.getTag();
         List<DeepShortcutView> shortcutViews = mShortcutsItemView == null
                 ? Collections.EMPTY_LIST
                 : mShortcutsItemView.getDeepShortcutViews(reverseOrder);
         if (mNotificationItemView != null) {
-            IconPalette iconPalette = originalIcon.getIconPalette();
-            mNotificationItemView.applyColors(iconPalette);
+            BadgeInfo badgeInfo = mLauncher.getPopupDataProvider()
+                    .getBadgeInfoForItem(originalItemInfo);
+            updateNotificationHeader(badgeInfo);
         }
 
         // Add the arrow.
         mArrow = addArrowView(arrowHorizontalOffset, arrowVerticalOffset, arrowWidth, arrowHeight);
         mArrow.setPivotX(arrowWidth / 2);
         mArrow.setPivotY(mIsAboveIcon ? 0 : arrowHeight);
-        PopupItemView firstItem = getItemViewAt(mIsAboveIcon ? getItemCount() - 1 : 0);
-        mArrow.setBackgroundTintList(firstItem.getAttachedArrowColor());
 
         animateOpen();
 
@@ -208,7 +206,7 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
         // Load the shortcuts on a background thread and update the container as it animates.
         final Looper workerLooper = LauncherModel.getWorkerLooper();
         new Handler(workerLooper).postAtFrontOfQueue(PopupPopulator.createUpdateRunnable(
-                mLauncher, (ItemInfo) originalIcon.getTag(), new Handler(Looper.getMainLooper()),
+                mLauncher, originalItemInfo, new Handler(Looper.getMainLooper()),
                 this, shortcutIds, shortcutViews, notificationKeys, mNotificationItemView));
     }
 
@@ -540,6 +538,24 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
         return true;
     }
 
+    /**
+     * Updates the notification header to reflect the badge info. Since this can be called
+     * for any badge info (not necessarily the one associated with this app), we first
+     * check that the ItemInfo matches the one of this popup.
+     */
+    public void updateNotificationHeader(BadgeInfo badgeInfo, ItemInfo originalItemInfo) {
+        if (originalItemInfo != mOriginalIcon.getTag()) {
+            return;
+        }
+        updateNotificationHeader(badgeInfo);
+    }
+
+    private void updateNotificationHeader(BadgeInfo badgeInfo) {
+        if (mNotificationItemView != null && badgeInfo != null) {
+            mNotificationItemView.updateHeader(badgeInfo.getNotificationCount());
+        }
+    }
+
     public void trimNotifications(Map<PackageUserKey, BadgeInfo> updatedBadges) {
         if (mNotificationItemView == null) {
             return;
@@ -576,8 +592,6 @@ public class PopupContainerWithArrow extends AbstractFloatingView implements Dra
                         close(false);
                         return;
                     }
-                    View firstItem = getItemViewAt(mIsAboveIcon ? getItemCount() - 1 : 0);
-                    mArrow.setBackgroundTintList(firstItem.getBackgroundTintList());
                 }
             });
             removeNotification.play(fade);
index dc4f415..b839d99 100644 (file)
@@ -20,7 +20,6 @@ import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.content.Context;
-import android.content.res.ColorStateList;
 import android.graphics.Bitmap;
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
@@ -109,10 +108,6 @@ public abstract class PopupItemView extends FrameLayout
         canvas.restoreToCount(saveCount);
     }
 
-    protected ColorStateList getAttachedArrowColor() {
-        return getBackgroundTintList();
-    }
-
     /**
      * Creates an animator to play when the shortcut container is being opened.
      */