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 NotificationBlockingHelperManager(Context context)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 */ perhapsShowBlockingHelper( ExpandableNotificationRow row, NotificationMenuRowPlugin menuRow)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 */ dismissCurrentBlockingHelper()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 */ setNotificationShadeExpanded(float expandedHeight)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 */ isNonblockablePackage(String packageName)147 public boolean isNonblockablePackage(String packageName) { 148 return mNonBlockablePkgs.contains(packageName); 149 } 150 151 @VisibleForTesting isBlockingHelperRowNull()152 boolean isBlockingHelperRowNull() { 153 return mBlockingHelperRow == null; 154 } 155 156 @VisibleForTesting setBlockingHelperRowForTest(ExpandableNotificationRow blockingHelperRowForTest)157 void setBlockingHelperRowForTest(ExpandableNotificationRow blockingHelperRowForTest) { 158 mBlockingHelperRow = blockingHelperRowForTest; 159 } 160 } 161