Home | History | Annotate | Download | only in statusbar
      1 /*
      2  * Copyright (C) 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package com.android.systemui.statusbar;
     18 
     19 import android.content.Context;
     20 import android.content.pm.PackageManager;
     21 import android.os.UserHandle;
     22 import android.service.notification.StatusBarNotification;
     23 import android.support.annotation.VisibleForTesting;
     24 import android.util.Log;
     25 
     26 import com.android.internal.logging.MetricsLogger;
     27 import com.android.systemui.Dependency;
     28 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
     29 import com.android.systemui.statusbar.notification.NotificationCounters;
     30 import com.android.systemui.statusbar.phone.StatusBar;
     31 
     32 import java.util.Collections;
     33 import java.util.HashSet;
     34 import java.util.Set;
     35 
     36 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
     37 
     38 /**
     39  * Manager for the notification blocking helper - tracks and helps create the blocking helper
     40  * affordance.
     41  */
     42 public class NotificationBlockingHelperManager {
     43     /** Enables debug logging and always makes the blocking helper show up after a dismiss. */
     44     private static final boolean DEBUG = false;
     45     private static final String TAG = "BlockingHelper";
     46 
     47     private final Context mContext;
     48     /** Row that the blocking helper will be shown in (via {@link NotificationGuts}. */
     49     private ExpandableNotificationRow mBlockingHelperRow;
     50     private Set<String> mNonBlockablePkgs;
     51 
     52     /**
     53      * Whether the notification shade/stack is expanded - used to determine blocking helper
     54      * eligibility.
     55      */
     56     private boolean mIsShadeExpanded;
     57 
     58     public NotificationBlockingHelperManager(Context context) {
     59         mContext = context;
     60         mNonBlockablePkgs = new HashSet<>();
     61         Collections.addAll(mNonBlockablePkgs, mContext.getResources().getStringArray(
     62                 com.android.internal.R.array.config_nonBlockableNotificationPackages));
     63     }
     64 
     65     /**
     66      * Potentially shows the blocking helper, represented via the {@link NotificationInfo} menu
     67      * item, in the current row if user sentiment is negative.
     68      *
     69      * @param row row to render the blocking helper in
     70      * @param menuRow menu used to generate the {@link NotificationInfo} view that houses the
     71      *                blocking helper UI
     72      * @return whether we're showing a blocking helper in the given notification row
     73      */
     74     boolean perhapsShowBlockingHelper(
     75             ExpandableNotificationRow row, NotificationMenuRowPlugin menuRow) {
     76         // We only show the blocking helper if:
     77         // - User sentiment is negative (DEBUG flag can bypass)
     78         // - The notification shade is fully expanded (guarantees we're not touching a HUN).
     79         // - The row is blockable (i.e. not non-blockable)
     80         // - The dismissed row is a valid group (>1 or 0 children) or the only child in the group
     81         if ((row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE || DEBUG)
     82                 && mIsShadeExpanded
     83                 && !row.getIsNonblockable()
     84                 && (!row.isChildInGroup() || row.isOnlyChildInGroup())) {
     85             // Dismiss any current blocking helper before continuing forward (only one can be shown
     86             // at a given time).
     87             dismissCurrentBlockingHelper();
     88 
     89             if (DEBUG) {
     90                 Log.d(TAG, "Manager.perhapsShowBlockingHelper: Showing new blocking helper");
     91             }
     92             NotificationGutsManager manager = Dependency.get(NotificationGutsManager.class);
     93 
     94             // Enable blocking helper on the row before moving forward so everything in the guts is
     95             // correctly prepped.
     96             mBlockingHelperRow = row;
     97             mBlockingHelperRow.setBlockingHelperShowing(true);
     98 
     99             // We don't care about the touch origin (x, y) since we're opening guts without any
    100             // explicit user interaction.
    101             manager.openGuts(mBlockingHelperRow, 0, 0, menuRow.getLongpressMenuItem(mContext));
    102 
    103             Dependency.get(MetricsLogger.class)
    104                     .count(NotificationCounters.BLOCKING_HELPER_SHOWN, 1);
    105             return true;
    106         }
    107         return false;
    108     }
    109 
    110     /**
    111      * Dismiss the currently showing blocking helper, if any, through a notification update.
    112      *
    113      * @return whether the blocking helper was dismissed
    114      */
    115     boolean dismissCurrentBlockingHelper() {
    116         if (!isBlockingHelperRowNull()) {
    117             if (DEBUG) {
    118                 Log.d(TAG, "Manager.dismissCurrentBlockingHelper: Dismissing current helper");
    119             }
    120             if (!mBlockingHelperRow.isBlockingHelperShowing()) {
    121                 Log.e(TAG, "Manager.dismissCurrentBlockingHelper: "
    122                         + "Non-null row is not showing a blocking helper");
    123             }
    124 
    125             mBlockingHelperRow.setBlockingHelperShowing(false);
    126             if (mBlockingHelperRow.isAttachedToWindow()) {
    127                 Dependency.get(NotificationEntryManager.class).updateNotifications();
    128             }
    129             mBlockingHelperRow = null;
    130             return true;
    131         }
    132         return false;
    133     }
    134 
    135     /**
    136      * Update the expansion status of the notification shade/stack.
    137      *
    138      * @param expandedHeight how much the shade is expanded ({code 0} indicating it's collapsed)
    139      */
    140     public void setNotificationShadeExpanded(float expandedHeight) {
    141         mIsShadeExpanded = expandedHeight > 0.0f;
    142     }
    143 
    144     /**
    145      * Returns whether the given package name is in the list of non-blockable packages.
    146      */
    147     public boolean isNonblockablePackage(String packageName) {
    148         return mNonBlockablePkgs.contains(packageName);
    149     }
    150 
    151     @VisibleForTesting
    152     boolean isBlockingHelperRowNull() {
    153         return mBlockingHelperRow == null;
    154     }
    155 
    156     @VisibleForTesting
    157     void setBlockingHelperRowForTest(ExpandableNotificationRow blockingHelperRowForTest) {
    158         mBlockingHelperRow = blockingHelperRowForTest;
    159     }
    160 }
    161