OSDN Git Service

Add "clear all" button to gentle notif section header
authorNed Burns <pixel@google.com>
Thu, 2 May 2019 22:27:23 +0000 (18:27 -0400)
committerNed Burns <pixel@google.com>
Mon, 6 May 2019 18:52:18 +0000 (14:52 -0400)
Test: manual

Bug: 131358199
Change-Id: I10ccd09e3eda719e35c4f53fb81c0ef4ac3ae290

packages/SystemUI/res/drawable-night/status_bar_notification_section_header_clear_btn.xml [new file with mode: 0644]
packages/SystemUI/res/drawable/status_bar_notification_section_header_clear_btn.xml [new file with mode: 0644]
packages/SystemUI/res/layout/status_bar_notification_section_header.xml
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java

diff --git a/packages/SystemUI/res/drawable-night/status_bar_notification_section_header_clear_btn.xml b/packages/SystemUI/res/drawable-night/status_bar_notification_section_header_clear_btn.xml
new file mode 100644 (file)
index 0000000..c471b38
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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="40dp"
+        android:height="40dp"
+        android:viewportWidth="40"
+        android:viewportHeight="40">
+    <path
+        android:fillColor="#9AA0A6"
+        android:pathData="M24.6667 16.2733L23.7267 15.3333L20 19.06L16.2734 15.3333L15.3334 16.2733L19.06 20L15.3334 23.7266L16.2734 24.6666L20 20.94L23.7267 24.6666L24.6667 23.7266L20.94 20L24.6667 16.2733Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/status_bar_notification_section_header_clear_btn.xml b/packages/SystemUI/res/drawable/status_bar_notification_section_header_clear_btn.xml
new file mode 100644 (file)
index 0000000..15f14d8
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2019 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="40dp"
+    android:height="40dp"
+    android:viewportWidth="40"
+    android:viewportHeight="40">
+    <path
+        android:fillColor="#5F6368"
+        android:pathData="M24.6667 16.2733L23.7267 15.3333L20 19.06L16.2734 15.3333L15.3334 16.2733L19.06 20L15.3334 23.7267L16.2734 24.6667L20 20.94L23.7267 24.6667L24.6667 23.7267L20.94 20L24.6667 16.2733Z"/>
+</vector>
index 8fe80cb..2b21006 100644 (file)
             />
         <ImageView
             android:id="@+id/btn_clear_all"
-            android:visibility="gone"
             android:layout_width="@dimen/notification_section_header_height"
             android:layout_height="@dimen/notification_section_header_height"
+            android:layout_marginRight="4dp"
+            android:src="@drawable/status_bar_notification_section_header_clear_btn"
             android:contentDescription="@string/accessibility_notification_section_header_gentle_clear_all"
             />
     </LinearLayout>
index ed4a6ab..82599f0 100644 (file)
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
+
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.Intent;
@@ -42,6 +44,7 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide
 
     private SectionHeaderView mGentleHeader;
     private boolean mGentleHeaderVisible = false;
+    @Nullable private View.OnClickListener mOnClearGentleNotifsClickListener;
 
     NotificationSectionsManager(
             NotificationStackScrollLayout parent,
@@ -70,12 +73,18 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide
         mGentleHeader = (SectionHeaderView) LayoutInflater.from(context).inflate(
                 R.layout.status_bar_notification_section_header, mParent, false);
         mGentleHeader.setOnHeaderClickListener(this::onGentleHeaderClick);
+        mGentleHeader.setOnClearAllClickListener(this::onClearGentleNotifsClick);
 
         if (oldPos != -1) {
             mParent.addView(mGentleHeader, oldPos);
         }
     }
 
+    /** Listener for when the "clear all" buttton is clciked on the gentle notification header. */
+    void setOnClearGentleNotifsClickListener(View.OnClickListener listener) {
+        mOnClearGentleNotifsClickListener = listener;
+    }
+
     /** Must be called whenever the UI mode changes (i.e. when we enter night mode). */
     void onUiModeChanged() {
         mGentleHeader.onUiModeChanged();
@@ -111,6 +120,9 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide
         }
 
         adjustGentleHeaderVisibilityAndPosition(firstGentleNotifIndex);
+
+        mGentleHeader.setAreThereDismissableGentleNotifs(
+                mParent.hasActiveClearableNotifications(ROWS_GENTLE));
     }
 
     private void adjustGentleHeaderVisibilityAndPosition(int firstGentleNotifIndex) {
@@ -225,4 +237,10 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide
                 true,
                 Intent.FLAG_ACTIVITY_SINGLE_TOP);
     }
+
+    private void onClearGentleNotifsClick(View v) {
+        if (mOnClearGentleNotifsClickListener != null) {
+            mOnClearGentleNotifsClickListener.onClick(v);
+        }
+    }
 }
index 81f3eee..5bd6cab 100644 (file)
@@ -23,10 +23,13 @@ import static com.android.systemui.statusbar.notification.stack.StackStateAnimat
 import static com.android.systemui.statusbar.phone.NotificationIconAreaController.LOW_PRIORITY;
 import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.TimeAnimator;
 import android.animation.ValueAnimator;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.WallpaperManager;
@@ -143,6 +146,7 @@ import com.android.systemui.tuner.TunerService;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -525,12 +529,19 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
         }
 
         mAmbientPulseManager = ambientPulseManager;
+
         mSectionsManager =
                 new NotificationSectionsManager(
                         this,
                         activityStarter,
                         NotificationUtils.useNewInterruptionModel(context));
         mSectionsManager.inflateViews(context);
+        mSectionsManager.setOnClearGentleNotifsClickListener(v -> {
+            // Leave the shade open if there will be other notifs left over to clear
+            final boolean closeShade = !hasActiveClearableNotifications(ROWS_HIGH_PRIORITY);
+            clearNotifications(ROWS_GENTLE, closeShade);
+        });
+
         mAmbientState = new AmbientState(context, mSectionsManager);
         mRoundnessManager = notificationRoundnessManager;
         mBgColor = context.getColor(R.color.notification_shade_background_color);
@@ -672,7 +683,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
     @VisibleForTesting
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void updateFooter() {
-        boolean showDismissView = mClearAllEnabled && hasActiveClearableNotifications();
+        boolean showDismissView = mClearAllEnabled && hasActiveClearableNotifications(ROWS_ALL);
         boolean showFooterView = (showDismissView ||
                 mEntryManager.getNotificationData().getActiveNotifications().size() != 0)
                 && mStatusBarState != StatusBarState.KEYGUARD
@@ -685,14 +696,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
      * Return whether there are any clearable notifications
      */
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public boolean hasActiveClearableNotifications() {
+    public boolean hasActiveClearableNotifications(@SelectedRows int selection) {
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             View child = getChildAt(i);
             if (!(child instanceof ExpandableNotificationRow)) {
                 continue;
             }
-            if (((ExpandableNotificationRow) child).canViewBeDismissed()) {
+            final ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+            if (row.canViewBeDismissed() && matchesSelection(row, selection)) {
                 return true;
             }
         }
@@ -1695,11 +1707,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
         return mScrollingEnabled;
     }
 
-    @ShadeViewRefactor(RefactorComponent.ADAPTER)
-    private boolean canChildBeDismissed(View v) {
-        return StackScrollAlgorithm.canChildBeDismissed(v);
-    }
-
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     private boolean onKeyguard() {
         return mStatusBarState == StatusBarState.KEYGUARD;
@@ -4917,7 +4924,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
             } else {
                 child.setMinClipTopAmount(0);
             }
-            previousChildWillBeDismissed = canChildBeDismissed(child);
+            previousChildWillBeDismissed = StackScrollAlgorithm.canChildBeDismissed(child);
         }
     }
 
@@ -5473,7 +5480,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    private void clearAllNotifications() {
+    private void clearNotifications(
+            @SelectedRows int selection,
+            boolean closeShade) {
         // animate-swipe all dismissable notifications, then animate the shade closed
         int numChildren = getChildCount();
 
@@ -5485,7 +5494,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
                 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
                 boolean parentVisible = false;
                 boolean hasClipBounds = child.getClipBounds(mTmpRect);
-                if (canChildBeDismissed(child)) {
+                if (includeChildInDismissAll(row, selection)) {
                     viewsToRemove.add(row);
                     if (child.getVisibility() == View.VISIBLE
                             && (!hasClipBounds || mTmpRect.height() > 0)) {
@@ -5499,51 +5508,94 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
                 List<ExpandableNotificationRow> children = row.getNotificationChildren();
                 if (children != null) {
                     for (ExpandableNotificationRow childRow : children) {
-                        viewsToRemove.add(childRow);
-                        if (parentVisible && row.areChildrenExpanded()
-                                && canChildBeDismissed(childRow)) {
-                            hasClipBounds = childRow.getClipBounds(mTmpRect);
-                            if (childRow.getVisibility() == View.VISIBLE
-                                    && (!hasClipBounds || mTmpRect.height() > 0)) {
-                                viewsToHide.add(childRow);
+                        if (includeChildInDismissAll(row, selection)) {
+                            viewsToRemove.add(childRow);
+                            if (parentVisible && row.areChildrenExpanded()) {
+                                hasClipBounds = childRow.getClipBounds(mTmpRect);
+                                if (childRow.getVisibility() == View.VISIBLE
+                                        && (!hasClipBounds || mTmpRect.height() > 0)) {
+                                    viewsToHide.add(childRow);
+                                }
                             }
                         }
                     }
                 }
             }
         }
+
         if (viewsToRemove.isEmpty()) {
-            mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+            if (closeShade) {
+                mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+            }
             return;
         }
 
-        mShadeController.addPostCollapseAction(() -> {
-            setDismissAllInProgress(false);
+        performDismissAllAnimations(viewsToHide, closeShade, () -> {
             for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
-                if (canChildBeDismissed(rowToRemove)) {
-                    mEntryManager.removeNotification(rowToRemove.getEntry().key, null /* ranking */,
-                            NotificationListenerService.REASON_CANCEL_ALL);
+                if (StackScrollAlgorithm.canChildBeDismissed(rowToRemove)) {
+                    if (selection == ROWS_ALL) {
+                        // TODO: This is a listener method; we shouldn't be calling it. Can we just
+                        // call performRemoveNotification as below?
+                        mEntryManager.removeNotification(
+                                rowToRemove.getEntry().key,
+                                null /* ranking */,
+                                NotificationListenerService.REASON_CANCEL_ALL);
+                    } else {
+                        mEntryManager.performRemoveNotification(
+                                rowToRemove.getEntry().notification,
+                                NotificationListenerService.REASON_CANCEL_ALL);
+                    }
                 } else {
                     rowToRemove.resetTranslation();
                 }
             }
-            try {
-                mBarService.onClearAllNotifications(mLockscreenUserManager.getCurrentUserId());
-            } catch (Exception ex) {
+            if (selection == ROWS_ALL) {
+                try {
+                    mBarService.onClearAllNotifications(mLockscreenUserManager.getCurrentUserId());
+                } catch (Exception ex) {
+                }
             }
         });
+    }
 
-        performDismissAllAnimations(viewsToHide);
+    private boolean includeChildInDismissAll(
+            ExpandableNotificationRow row,
+            @SelectedRows int selection) {
+        return StackScrollAlgorithm.canChildBeDismissed(row) && matchesSelection(row, selection);
     }
 
+    /**
+     * Given a list of rows, animates them away in a staggered fashion as if they were dismissed.
+     * Doesn't actually dismiss them, though -- that must be done in the onAnimationComplete
+     * handler.
+     *
+     * @param hideAnimatedList List of rows to animated away. Should only be views that are
+     *                         currently visible, or else the stagger will look funky.
+     * @param closeShade Whether to close the shade after the stagger animation completes.
+     * @param onAnimationComplete Called after the entire animation completes (including the shade
+     *                            closing if appropriate). The rows must be dismissed for real here.
+     */
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void performDismissAllAnimations(ArrayList<View> hideAnimatedList) {
-        Runnable animationFinishAction = () -> {
-            mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+    private void performDismissAllAnimations(
+            final ArrayList<View> hideAnimatedList,
+            final boolean closeShade,
+            final Runnable onAnimationComplete) {
+
+        final Runnable onSlideAwayAnimationComplete = () -> {
+            if (closeShade) {
+                mShadeController.addPostCollapseAction(() -> {
+                    setDismissAllInProgress(false);
+                    onAnimationComplete.run();
+                });
+                mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+            } else {
+                setDismissAllInProgress(false);
+                onAnimationComplete.run();
+            }
         };
 
         if (hideAnimatedList.isEmpty()) {
-            animationFinishAction.run();
+            onSlideAwayAnimationComplete.run();
             return;
         }
 
@@ -5560,7 +5612,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
             View view = hideAnimatedList.get(i);
             Runnable endRunnable = null;
             if (i == 0) {
-                endRunnable = animationFinishAction;
+                endRunnable = onSlideAwayAnimationComplete;
             }
             dismissViewAnimated(view, endRunnable, totalDelay, ANIMATION_DURATION_SWIPE);
             currentDelay = Math.max(50, currentDelay - rowDelayDecrement);
@@ -5575,7 +5627,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
                 R.layout.status_bar_notification_footer, this, false);
         footerView.setDismissButtonClickListener(v -> {
             mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES);
-            clearAllNotifications();
+            clearNotifications(ROWS_ALL, true /* closeShade */);
         });
         footerView.setManageButtonClickListener(this::manageNotifications);
         setFooterView(footerView);
@@ -5782,6 +5834,21 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
         mSwipeHelper.resetExposedMenuView(animate, force);
     }
 
+    private static boolean matchesSelection(
+            ExpandableNotificationRow row,
+            @SelectedRows int selection) {
+        switch (selection) {
+            case ROWS_ALL:
+                return true;
+            case ROWS_HIGH_PRIORITY:
+                return row.getEntry().isHighPriority();
+            case ROWS_GENTLE:
+                return !row.getEntry().isHighPriority();
+            default:
+                throw new IllegalArgumentException("Unknown selection: " + selection);
+        }
+    }
+
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     static class AnimationEvent {
 
@@ -6266,7 +6333,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
 
                 @Override
         public boolean canChildBeDismissed(View v) {
-            return NotificationStackScrollLayout.this.canChildBeDismissed(v);
+            return StackScrollAlgorithm.canChildBeDismissed(v);
         }
 
         @Override
@@ -6473,4 +6540,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
     public ExpandHelper.Callback getExpandHelperCallback() {
         return mExpandHelperCallback;
     }
+
+    /** Enum for selecting some or all notification rows (does not included non-notif views). */
+    @Retention(SOURCE)
+    @IntDef({ROWS_ALL, ROWS_HIGH_PRIORITY, ROWS_GENTLE})
+    public @interface SelectedRows {}
+    /** All rows representing notifs. */
+    public static final int ROWS_ALL = 0;
+    /** Only rows where entry.isHighPriority() is true. */
+    public static final int ROWS_HIGH_PRIORITY = 1;
+    /** Only rows where entry.isHighPriority() is false. */
+    public static final int ROWS_GENTLE = 2;
 }
index c32c529..e2f702d 100644 (file)
@@ -60,6 +60,12 @@ public class SectionHeaderView extends ActivatableNotificationView {
         updateBackgroundColors();
         mLabelView.setTextColor(
                 getContext().getColor(R.color.notification_section_header_label_color));
+        mClearAllButton.setImageResource(
+                R.drawable.status_bar_notification_section_header_clear_btn);
+    }
+
+    void setAreThereDismissableGentleNotifs(boolean areThereDismissableGentleNotifs) {
+        mClearAllButton.setVisibility(areThereDismissableGentleNotifs ? View.VISIBLE : View.GONE);
     }
 
     @Override
@@ -79,4 +85,9 @@ public class SectionHeaderView extends ActivatableNotificationView {
     void setOnHeaderClickListener(View.OnClickListener listener) {
         mContents.setOnClickListener(listener);
     }
+
+    /** Fired when the user clicks on the "X" button on the far right of the header. */
+    void setOnClearAllClickListener(View.OnClickListener listener) {
+        mClearAllButton.setOnClickListener(listener);
+    }
 }
index 9d24e1e..168813a 100644 (file)
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone;
 
 import static com.android.systemui.SysUiServiceProvider.getComponent;
 import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
 import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
 
 import android.animation.Animator;
@@ -2992,7 +2993,7 @@ public class NotificationPanelView extends PanelView implements
     }
 
     public boolean hasActiveClearableNotifications() {
-        return mNotificationStackScroller.hasActiveClearableNotifications();
+        return mNotificationStackScroller.hasActiveClearableNotifications(ROWS_ALL);
     }
 
     @Override