blob: 1074adc3383d1d3a9cd0b41d23a5bf8c1a83612a [file] [log] [blame]
Eliot Courtney47098cb2017-10-18 17:30:30 +09001/*
2 * Copyright (C) 2017 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 */
Rohan Shah20790b82018-07-02 17:21:04 -070016package com.android.systemui.statusbar.notification.row;
Eliot Courtney47098cb2017-10-18 17:30:30 +090017
Julia Reynoldsb5867452018-02-28 16:31:35 -050018import static android.app.AppOpsManager.OP_CAMERA;
19import static android.app.AppOpsManager.OP_RECORD_AUDIO;
20import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
Gus Prevas894d9152018-11-12 13:51:40 -050021
Eliot Courtney47098cb2017-10-18 17:30:30 +090022import android.app.INotificationManager;
23import android.app.NotificationChannel;
24import android.content.Context;
25import android.content.Intent;
Julia Reynolds12ba4cf2020-01-10 16:01:38 -050026import android.content.pm.LauncherApps;
Eliot Courtney47098cb2017-10-18 17:30:30 +090027import android.content.pm.PackageManager;
Julia Reynolds12ba4cf2020-01-10 16:01:38 -050028import android.content.pm.ShortcutManager;
Julia Reynoldsf5c04872018-02-13 09:54:04 -050029import android.net.Uri;
Evan Laird47dc4542019-04-24 15:10:52 -040030import android.os.Bundle;
Dave Mankoff5fe07f72019-11-19 22:14:41 -050031import android.os.Handler;
Eliot Courtney47098cb2017-10-18 17:30:30 +090032import android.os.UserHandle;
33import android.provider.Settings;
34import android.service.notification.StatusBarNotification;
35import android.util.ArraySet;
Julia Reynolds01c9da42020-02-14 14:17:58 -050036import android.util.IconDrawableFactory;
Eliot Courtney47098cb2017-10-18 17:30:30 +090037import android.util.Log;
38import android.view.HapticFeedbackConstants;
39import android.view.View;
Eliot Courtney47098cb2017-10-18 17:30:30 +090040import android.view.accessibility.AccessibilityManager;
41
Kevina5ff1fa2018-08-21 16:35:48 -070042import com.android.internal.annotations.VisibleForTesting;
Eliot Courtney47098cb2017-10-18 17:30:30 +090043import com.android.internal.logging.MetricsLogger;
44import com.android.internal.logging.nano.MetricsProto;
Julia Reynolds01c9da42020-02-14 14:17:58 -050045import com.android.settingslib.notification.ConversationIconFactory;
Eliot Courtney47098cb2017-10-18 17:30:30 +090046import com.android.systemui.Dependency;
47import com.android.systemui.Dumpable;
Julia Reynolds01c9da42020-02-14 14:17:58 -050048import com.android.systemui.R;
Steve Elliott6771f542020-05-26 13:31:10 -040049import com.android.systemui.dagger.qualifiers.Background;
Dave Mankoff00e8a2f2019-12-18 16:59:49 -050050import com.android.systemui.dagger.qualifiers.Main;
Eliot Courtney47098cb2017-10-18 17:30:30 +090051import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
Beverly8fdb5332019-02-04 14:29:49 -050052import com.android.systemui.plugins.statusbar.StatusBarStateController;
Evan Laird31ca5472020-04-08 17:45:24 -040053import com.android.systemui.settings.CurrentUserContextTracker;
Kevina5ff1fa2018-08-21 16:35:48 -070054import com.android.systemui.statusbar.NotificationLifetimeExtender;
Rohan Shah20790b82018-07-02 17:21:04 -070055import com.android.systemui.statusbar.NotificationLockscreenUserManager;
56import com.android.systemui.statusbar.NotificationPresenter;
Jason Monk297c04e2018-08-23 17:16:59 -040057import com.android.systemui.statusbar.StatusBarState;
Evan Laird03cf3502019-05-31 16:46:48 -040058import com.android.systemui.statusbar.StatusBarStateControllerImpl;
Gus Prevas21437b32018-12-05 10:36:13 -050059import com.android.systemui.statusbar.notification.NotificationActivityStarter;
Ned Burns9512e0c2019-05-30 19:36:04 -040060import com.android.systemui.statusbar.notification.VisualStabilityManager;
Ned Burnsf81c4c42019-01-07 14:10:43 -050061import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Beverlye558f1d2020-01-07 16:28:58 -050062import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
Sergey Nikolaienkovf6ad6322020-02-10 15:46:32 +010063import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
Jason Monk297c04e2018-08-23 17:16:59 -040064import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
Rohan Shah20790b82018-07-02 17:21:04 -070065import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
Eliot Courtney47098cb2017-10-18 17:30:30 +090066import com.android.systemui.statusbar.phone.StatusBar;
Jason Monk297c04e2018-08-23 17:16:59 -040067import com.android.systemui.statusbar.policy.DeviceProvisionedController;
Eliot Courtney47098cb2017-10-18 17:30:30 +090068
69import java.io.FileDescriptor;
70import java.io.PrintWriter;
Eliot Courtney47098cb2017-10-18 17:30:30 +090071
Evan Laird31ca5472020-04-08 17:45:24 -040072import javax.inject.Provider;
73
Dave Mankoff4dd47fa2019-11-07 10:44:48 -050074import dagger.Lazy;
75
Eliot Courtney47098cb2017-10-18 17:30:30 +090076/**
77 * Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
78 * closing guts, and keeping track of the currently exposed notification guts.
79 */
Kevina5ff1fa2018-08-21 16:35:48 -070080public class NotificationGutsManager implements Dumpable, NotificationLifetimeExtender {
Eliot Courtney47098cb2017-10-18 17:30:30 +090081 private static final String TAG = "NotificationGutsManager";
82
83 // Must match constant in Settings. Used to highlight preferences when linking to Settings.
84 private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
85
86 private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
Eliot Courtney47098cb2017-10-18 17:30:30 +090087 private final Context mContext;
Ned Burns9512e0c2019-05-30 19:36:04 -040088 private final VisualStabilityManager mVisualStabilityManager;
Eliot Courtney47098cb2017-10-18 17:30:30 +090089 private final AccessibilityManager mAccessibilityManager;
Beverlye558f1d2020-01-07 16:28:58 -050090 private final HighPriorityProvider mHighPriorityProvider;
Evan Laird18bd6e62019-08-30 16:39:25 -040091 private final ChannelEditorDialogController mChannelEditorDialogController;
Eliot Courtney6c313d32017-12-14 19:57:51 +090092
93 // Dependencies:
94 private final NotificationLockscreenUserManager mLockscreenUserManager =
95 Dependency.get(NotificationLockscreenUserManager.class);
Jason Monk297c04e2018-08-23 17:16:59 -040096 private final StatusBarStateController mStatusBarStateController =
97 Dependency.get(StatusBarStateController.class);
98 private final DeviceProvisionedController mDeviceProvisionedController =
99 Dependency.get(DeviceProvisionedController.class);
Eliot Courtney09322282017-11-09 15:31:19 +0900100
Eliot Courtney47098cb2017-10-18 17:30:30 +0900101 // which notification is currently being longpress-examined by the user
102 private NotificationGuts mNotificationGutsExposed;
103 private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
Kevina5ff1fa2018-08-21 16:35:48 -0700104 private NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
Jason Monk297c04e2018-08-23 17:16:59 -0400105 private NotificationPresenter mPresenter;
Gus Prevas21437b32018-12-05 10:36:13 -0500106 private NotificationActivityStarter mNotificationActivityStarter;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900107 private NotificationListContainer mListContainer;
Jason Monk297c04e2018-08-23 17:16:59 -0400108 private CheckSaveListener mCheckSaveListener;
Eliot Courtney09322282017-11-09 15:31:19 +0900109 private OnSettingsClickListener mOnSettingsClickListener;
Kevina5ff1fa2018-08-21 16:35:48 -0700110 @VisibleForTesting
111 protected String mKeyToRemoveOnGutsClosed;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900112
Dave Mankoff4dd47fa2019-11-07 10:44:48 -0500113 private final Lazy<StatusBar> mStatusBarLazy;
Dave Mankoff5fe07f72019-11-19 22:14:41 -0500114 private final Handler mMainHandler;
Steve Elliott6771f542020-05-26 13:31:10 -0400115 private final Handler mBgHandler;
Selim Cinek56e6a322019-10-01 12:06:05 -0700116 private Runnable mOpenRunnable;
Steve Elliott46bb2a12020-03-17 11:04:09 -0400117 private final INotificationManager mNotificationManager;
118 private final LauncherApps mLauncherApps;
119 private final ShortcutManager mShortcutManager;
Evan Laird31ca5472020-04-08 17:45:24 -0400120 private final CurrentUserContextTracker mContextTracker;
121 private final Provider<PriorityOnboardingDialogController.Builder> mBuilderProvider;
Evan Laird03cf3502019-05-31 16:46:48 -0400122
Sergey Nikolaienkovf6ad6322020-02-10 15:46:32 +0100123 /**
124 * Injected constructor. See {@link NotificationsModule}.
125 */
Dave Mankoff4dd47fa2019-11-07 10:44:48 -0500126 public NotificationGutsManager(Context context, VisualStabilityManager visualStabilityManager,
Steve Elliott6771f542020-05-26 13:31:10 -0400127 Lazy<StatusBar> statusBarLazy, @Main Handler mainHandler, @Background Handler bgHandler,
Beverlye558f1d2020-01-07 16:28:58 -0500128 AccessibilityManager accessibilityManager,
Steve Elliott46bb2a12020-03-17 11:04:09 -0400129 HighPriorityProvider highPriorityProvider,
130 INotificationManager notificationManager,
131 LauncherApps launcherApps,
Evan Laird31ca5472020-04-08 17:45:24 -0400132 ShortcutManager shortcutManager,
Evan Laird18bd6e62019-08-30 16:39:25 -0400133 ChannelEditorDialogController channelEditorDialogController,
Evan Laird31ca5472020-04-08 17:45:24 -0400134 CurrentUserContextTracker contextTracker,
135 Provider<PriorityOnboardingDialogController.Builder> builderProvider) {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900136 mContext = context;
Ned Burns9512e0c2019-05-30 19:36:04 -0400137 mVisualStabilityManager = visualStabilityManager;
Dave Mankoff4dd47fa2019-11-07 10:44:48 -0500138 mStatusBarLazy = statusBarLazy;
Dave Mankoff5fe07f72019-11-19 22:14:41 -0500139 mMainHandler = mainHandler;
Steve Elliott6771f542020-05-26 13:31:10 -0400140 mBgHandler = bgHandler;
Dave Mankoff5fe07f72019-11-19 22:14:41 -0500141 mAccessibilityManager = accessibilityManager;
Beverlye558f1d2020-01-07 16:28:58 -0500142 mHighPriorityProvider = highPriorityProvider;
Steve Elliott46bb2a12020-03-17 11:04:09 -0400143 mNotificationManager = notificationManager;
144 mLauncherApps = launcherApps;
145 mShortcutManager = shortcutManager;
Evan Laird31ca5472020-04-08 17:45:24 -0400146 mContextTracker = contextTracker;
147 mBuilderProvider = builderProvider;
Evan Laird18bd6e62019-08-30 16:39:25 -0400148 mChannelEditorDialogController = channelEditorDialogController;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900149 }
150
Eliot Courtney4a96b362017-12-14 19:38:52 +0900151 public void setUpWithPresenter(NotificationPresenter presenter,
Kevina5ff1fa2018-08-21 16:35:48 -0700152 NotificationListContainer listContainer,
Jason Monk297c04e2018-08-23 17:16:59 -0400153 CheckSaveListener checkSave, OnSettingsClickListener onSettingsClick) {
Eliot Courtney09322282017-11-09 15:31:19 +0900154 mPresenter = presenter;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900155 mListContainer = listContainer;
Jason Monk297c04e2018-08-23 17:16:59 -0400156 mCheckSaveListener = checkSave;
157 mOnSettingsClickListener = onSettingsClick;
Eliot Courtney09322282017-11-09 15:31:19 +0900158 }
159
Gus Prevas21437b32018-12-05 10:36:13 -0500160 public void setNotificationActivityStarter(
161 NotificationActivityStarter notificationActivityStarter) {
162 mNotificationActivityStarter = notificationActivityStarter;
163 }
164
Ned Burnsf81c4c42019-01-07 14:10:43 -0500165 public void onDensityOrFontScaleChanged(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -0400166 setExposedGuts(entry.getGuts());
167 bindGuts(entry.getRow());
yoshiki iguchia85c2a02018-01-12 11:28:06 +0900168 }
169
Eliot Courtney47098cb2017-10-18 17:30:30 +0900170 /**
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400171 * Sends an intent to open the notification settings for a particular package and optional
Eliot Courtney47098cb2017-10-18 17:30:30 +0900172 * channel.
173 */
Evan Laird47dc4542019-04-24 15:10:52 -0400174 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
Eliot Courtney47098cb2017-10-18 17:30:30 +0900175 private void startAppNotificationSettingsActivity(String packageName, final int appUid,
Selim Cinek2627d722018-01-19 12:16:49 -0800176 final NotificationChannel channel, ExpandableNotificationRow row) {
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400177 final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
178 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
179 intent.putExtra(Settings.EXTRA_APP_UID, appUid);
Evan Laird47dc4542019-04-24 15:10:52 -0400180
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400181 if (channel != null) {
Evan Laird47dc4542019-04-24 15:10:52 -0400182 final Bundle args = new Bundle();
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400183 intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
Evan Laird47dc4542019-04-24 15:10:52 -0400184 args.putString(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
185 intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400186 }
187 mNotificationActivityStarter.startNotificationGutsIntent(intent, appUid, row);
188 }
189
190 private void startAppDetailsSettingsActivity(String packageName, final int appUid,
191 final NotificationChannel channel, ExpandableNotificationRow row) {
Julia Reynoldsf5c04872018-02-13 09:54:04 -0500192 final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
193 intent.setData(Uri.fromParts("package", packageName, null));
Eliot Courtney47098cb2017-10-18 17:30:30 +0900194 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
195 intent.putExtra(Settings.EXTRA_APP_UID, appUid);
196 if (channel != null) {
197 intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
198 }
Gus Prevas21437b32018-12-05 10:36:13 -0500199 mNotificationActivityStarter.startNotificationGutsIntent(intent, appUid, row);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900200 }
201
Julia Reynoldsb5867452018-02-28 16:31:35 -0500202 protected void startAppOpsSettingsActivity(String pkg, int uid, ArraySet<Integer> ops,
203 ExpandableNotificationRow row) {
204 if (ops.contains(OP_SYSTEM_ALERT_WINDOW)) {
205 if (ops.contains(OP_CAMERA) || ops.contains(OP_RECORD_AUDIO)) {
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400206 startAppDetailsSettingsActivity(pkg, uid, null, row);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500207 } else {
Bernardo Rufino8fefb942019-11-25 19:51:09 +0000208 Intent intent = new Intent(Settings.ACTION_MANAGE_APP_OVERLAY_PERMISSION);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500209 intent.setData(Uri.fromParts("package", pkg, null));
Gus Prevas21437b32018-12-05 10:36:13 -0500210 mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500211 }
212 } else if (ops.contains(OP_CAMERA) || ops.contains(OP_RECORD_AUDIO)) {
213 Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
214 intent.putExtra(Intent.EXTRA_PACKAGE_NAME, pkg);
Gus Prevas21437b32018-12-05 10:36:13 -0500215 mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500216 }
217 }
218
Julia Reynoldse03aa8f2020-06-02 11:21:38 -0400219 private void startConversationSettingsActivity(int uid, ExpandableNotificationRow row) {
220 final Intent intent = new Intent(Settings.ACTION_CONVERSATION_SETTINGS);
221 mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
222 }
223
Evan Lairde55c6012019-03-13 12:54:37 -0400224 private boolean bindGuts(final ExpandableNotificationRow row) {
225 row.ensureGutsInflated();
Julia Reynolds4131e782018-08-22 09:45:24 -0400226 return bindGuts(row, mGutsMenuItem);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900227 }
228
TreeHugger Robotee8e1ae2019-01-18 19:26:40 +0000229 @VisibleForTesting
230 protected boolean bindGuts(final ExpandableNotificationRow row,
Eliot Courtney47098cb2017-10-18 17:30:30 +0900231 NotificationMenuRowPlugin.MenuItem item) {
Ned Burns1c2b85a42019-11-14 15:37:03 -0500232 StatusBarNotification sbn = row.getEntry().getSbn();
Rohan Shah524cf7b2018-03-15 14:40:02 -0700233
Eliot Courtney47098cb2017-10-18 17:30:30 +0900234 row.setGutsView(item);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900235 row.setTag(sbn.getPackageName());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700236 row.getGuts().setClosedListener((NotificationGuts g) -> {
Rohan Shah56eb0912018-05-10 21:49:04 -0700237 row.onGutsClosed();
Eliot Courtney47098cb2017-10-18 17:30:30 +0900238 if (!g.willBeRemoved() && !row.isRemoved()) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900239 mListContainer.onHeightChanged(
Eliot Courtney47098cb2017-10-18 17:30:30 +0900240 row, !mPresenter.isPresenterFullyCollapsed() /* needsAnimation */);
241 }
242 if (mNotificationGutsExposed == g) {
243 mNotificationGutsExposed = null;
244 mGutsMenuItem = null;
245 }
246 String key = sbn.getKey();
247 if (key.equals(mKeyToRemoveOnGutsClosed)) {
248 mKeyToRemoveOnGutsClosed = null;
Kevina5ff1fa2018-08-21 16:35:48 -0700249 if (mNotificationLifetimeFinishedCallback != null) {
250 mNotificationLifetimeFinishedCallback.onSafeToRemove(key);
251 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900252 }
253 });
254
255 View gutsView = item.getGutsView();
Julia Reynolds4131e782018-08-22 09:45:24 -0400256 try {
257 if (gutsView instanceof NotificationSnooze) {
258 initializeSnoozeView(row, (NotificationSnooze) gutsView);
259 } else if (gutsView instanceof AppOpsInfo) {
260 initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
261 } else if (gutsView instanceof NotificationInfo) {
Gus Prevas5a70a4e2018-11-26 17:16:05 -0500262 initializeNotificationInfo(row, (NotificationInfo) gutsView);
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500263 } else if (gutsView instanceof NotificationConversationInfo) {
264 initializeConversationNotificationInfo(
265 row, (NotificationConversationInfo) gutsView);
Julia Reynolds8582df52020-04-24 18:30:59 -0400266 } else if (gutsView instanceof PartialConversationInfo) {
267 initializePartialConversationNotificationInfo(row,
268 (PartialConversationInfo) gutsView);
Julia Reynolds4131e782018-08-22 09:45:24 -0400269 }
270 return true;
271 } catch (Exception e) {
272 Log.e(TAG, "error binding guts", e);
273 return false;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900274 }
Rohan Shah524cf7b2018-03-15 14:40:02 -0700275 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900276
Rohan Shah524cf7b2018-03-15 14:40:02 -0700277 /**
278 * Sets up the {@link NotificationSnooze} inside the notification row's guts.
279 *
280 * @param row view to set up the guts for
281 * @param notificationSnoozeView view to set up/bind within {@code row}
282 */
283 private void initializeSnoozeView(
284 final ExpandableNotificationRow row,
285 NotificationSnooze notificationSnoozeView) {
286 NotificationGuts guts = row.getGuts();
Ned Burns1c2b85a42019-11-14 15:37:03 -0500287 StatusBarNotification sbn = row.getEntry().getSbn();
Rohan Shah524cf7b2018-03-15 14:40:02 -0700288
289 notificationSnoozeView.setSnoozeListener(mListContainer.getSwipeActionHelper());
290 notificationSnoozeView.setStatusBarNotification(sbn);
Ned Burns47c98f12019-09-06 17:12:07 -0400291 notificationSnoozeView.setSnoozeOptions(row.getEntry().getSnoozeCriteria());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700292 guts.setHeightChangedListener((NotificationGuts g) -> {
293 mListContainer.onHeightChanged(row, row.isShown() /* needsAnimation */);
294 });
295 }
296
297 /**
298 * Sets up the {@link AppOpsInfo} inside the notification row's guts.
299 *
300 * @param row view to set up the guts for
301 * @param appOpsInfoView view to set up/bind within {@code row}
302 */
303 private void initializeAppOpsInfo(
304 final ExpandableNotificationRow row,
305 AppOpsInfo appOpsInfoView) {
306 NotificationGuts guts = row.getGuts();
Ned Burns1c2b85a42019-11-14 15:37:03 -0500307 StatusBarNotification sbn = row.getEntry().getSbn();
Rohan Shah524cf7b2018-03-15 14:40:02 -0700308 UserHandle userHandle = sbn.getUser();
309 PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
310 userHandle.getIdentifier());
311
312 AppOpsInfo.OnSettingsClickListener onSettingsClick =
313 (View v, String pkg, int uid, ArraySet<Integer> ops) -> {
314 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_OPS_GUTS_SETTINGS);
315 guts.resetFalsingCheck();
316 startAppOpsSettingsActivity(pkg, uid, ops, row);
317 };
318 if (!row.getEntry().mActiveAppOps.isEmpty()) {
319 appOpsInfoView.bindGuts(pmUser, onSettingsClick, sbn, row.getEntry().mActiveAppOps);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500320 }
Rohan Shah524cf7b2018-03-15 14:40:02 -0700321 }
Julia Reynoldsb5867452018-02-28 16:31:35 -0500322
Rohan Shah524cf7b2018-03-15 14:40:02 -0700323 /**
324 * Sets up the {@link NotificationInfo} inside the notification row's guts.
Rohan Shah524cf7b2018-03-15 14:40:02 -0700325 * @param row view to set up the guts for
326 * @param notificationInfoView view to set up/bind within {@code row}
327 */
328 @VisibleForTesting
329 void initializeNotificationInfo(
330 final ExpandableNotificationRow row,
Gus Prevas5a70a4e2018-11-26 17:16:05 -0500331 NotificationInfo notificationInfoView) throws Exception {
Rohan Shah524cf7b2018-03-15 14:40:02 -0700332 NotificationGuts guts = row.getGuts();
Ned Burns1c2b85a42019-11-14 15:37:03 -0500333 StatusBarNotification sbn = row.getEntry().getSbn();
Rohan Shah524cf7b2018-03-15 14:40:02 -0700334 String packageName = sbn.getPackageName();
335 // Settings link is only valid for notifications that specify a non-system user
336 NotificationInfo.OnSettingsClickListener onSettingsClick = null;
337 UserHandle userHandle = sbn.getUser();
338 PackageManager pmUser = StatusBar.getPackageManagerForUser(
339 mContext, userHandle.getIdentifier());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700340 final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick =
341 (View v, Intent intent) -> {
342 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900343 guts.resetFalsingCheck();
Gus Prevas21437b32018-12-05 10:36:13 -0500344 mNotificationActivityStarter.startNotificationGutsIntent(intent, sbn.getUid(),
345 row);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900346 };
Eliot Courtney47098cb2017-10-18 17:30:30 +0900347
Rohan Shah524cf7b2018-03-15 14:40:02 -0700348 if (!userHandle.equals(UserHandle.ALL)
349 || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
350 onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
351 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
352 guts.resetFalsingCheck();
Jason Monk297c04e2018-08-23 17:16:59 -0400353 mOnSettingsClickListener.onSettingsClick(sbn.getKey());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700354 startAppNotificationSettingsActivity(packageName, appUid, channel, row);
355 };
356 }
357
Julia Reynolds4131e782018-08-22 09:45:24 -0400358 notificationInfoView.bindNotification(
359 pmUser,
Steve Elliott46bb2a12020-03-17 11:04:09 -0400360 mNotificationManager,
Ned Burns9512e0c2019-05-30 19:36:04 -0400361 mVisualStabilityManager,
Evan Laird18bd6e62019-08-30 16:39:25 -0400362 mChannelEditorDialogController,
Julia Reynolds4131e782018-08-22 09:45:24 -0400363 packageName,
Ned Burns296aec162019-09-04 17:30:59 -0400364 row.getEntry().getChannel(),
Evan Laird47dc4542019-04-24 15:10:52 -0400365 row.getUniqueChannels(),
Mady Mellor53162c12019-10-22 17:12:59 -0700366 row.getEntry(),
Julia Reynolds4131e782018-08-22 09:45:24 -0400367 onSettingsClick,
368 onAppSettingsClick,
Jason Monk297c04e2018-08-23 17:16:59 -0400369 mDeviceProvisionedController.isDeviceProvisioned(),
Julia Reynolds4131e782018-08-22 09:45:24 -0400370 row.getIsNonblockable(),
Beverlye558f1d2020-01-07 16:28:58 -0500371 mHighPriorityProvider.isHighPriority(row.getEntry()));
Rohan Shah524cf7b2018-03-15 14:40:02 -0700372 }
373
374 /**
Julia Reynolds8582df52020-04-24 18:30:59 -0400375 * Sets up the {@link PartialConversationInfo} inside the notification row's guts.
376 * @param row view to set up the guts for
377 * @param notificationInfoView view to set up/bind within {@code row}
378 */
379 @VisibleForTesting
380 void initializePartialConversationNotificationInfo(
381 final ExpandableNotificationRow row,
382 PartialConversationInfo notificationInfoView) throws Exception {
383 NotificationGuts guts = row.getGuts();
384 StatusBarNotification sbn = row.getEntry().getSbn();
385 String packageName = sbn.getPackageName();
386 // Settings link is only valid for notifications that specify a non-system user
387 NotificationInfo.OnSettingsClickListener onSettingsClick = null;
388 UserHandle userHandle = sbn.getUser();
389 PackageManager pmUser = StatusBar.getPackageManagerForUser(
390 mContext, userHandle.getIdentifier());
391
392 if (!userHandle.equals(UserHandle.ALL)
393 || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
394 onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
395 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
396 guts.resetFalsingCheck();
397 mOnSettingsClickListener.onSettingsClick(sbn.getKey());
398 startAppNotificationSettingsActivity(packageName, appUid, channel, row);
399 };
400 }
401
402 notificationInfoView.bindNotification(
403 pmUser,
404 mNotificationManager,
Evan Laird18bd6e62019-08-30 16:39:25 -0400405 mChannelEditorDialogController,
Julia Reynolds8582df52020-04-24 18:30:59 -0400406 packageName,
407 row.getEntry().getChannel(),
408 row.getUniqueChannels(),
409 row.getEntry(),
410 onSettingsClick,
411 mDeviceProvisionedController.isDeviceProvisioned(),
412 row.getIsNonblockable());
413 }
414
415 /**
416 * Sets up the {@link ConversationInfo} inside the notification row's guts.
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500417 * @param row view to set up the guts for
418 * @param notificationInfoView view to set up/bind within {@code row}
419 */
420 @VisibleForTesting
421 void initializeConversationNotificationInfo(
422 final ExpandableNotificationRow row,
423 NotificationConversationInfo notificationInfoView) throws Exception {
424 NotificationGuts guts = row.getGuts();
Evan Laird03c44452020-04-16 12:26:07 -0400425 NotificationEntry entry = row.getEntry();
426 StatusBarNotification sbn = entry.getSbn();
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500427 String packageName = sbn.getPackageName();
428 // Settings link is only valid for notifications that specify a non-system user
429 NotificationConversationInfo.OnSettingsClickListener onSettingsClick = null;
430 UserHandle userHandle = sbn.getUser();
431 PackageManager pmUser = StatusBar.getPackageManagerForUser(
432 mContext, userHandle.getIdentifier());
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500433 final NotificationConversationInfo.OnAppSettingsClickListener onAppSettingsClick =
434 (View v, Intent intent) -> {
435 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
436 guts.resetFalsingCheck();
437 mNotificationActivityStarter.startNotificationGutsIntent(intent, sbn.getUid(),
438 row);
439 };
440
441 final NotificationConversationInfo.OnSnoozeClickListener onSnoozeClickListener =
442 (View v, int hours) -> {
443 mListContainer.getSwipeActionHelper().snooze(sbn, hours);
444 };
445
Julia Reynoldse03aa8f2020-06-02 11:21:38 -0400446 final NotificationConversationInfo.OnConversationSettingsClickListener
447 onConversationSettingsListener =
448 () -> {
449 startConversationSettingsActivity(sbn.getUid(), row);
450 };
451
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500452 if (!userHandle.equals(UserHandle.ALL)
453 || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
454 onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
455 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
456 guts.resetFalsingCheck();
457 mOnSettingsClickListener.onSettingsClick(sbn.getKey());
458 startAppNotificationSettingsActivity(packageName, appUid, channel, row);
459 };
460 }
Julia Reynolds01c9da42020-02-14 14:17:58 -0500461 ConversationIconFactory iconFactoryLoader = new ConversationIconFactory(mContext,
Steve Elliott46bb2a12020-03-17 11:04:09 -0400462 mLauncherApps, pmUser, IconDrawableFactory.newInstance(mContext, false),
Julia Reynolds01c9da42020-02-14 14:17:58 -0500463 mContext.getResources().getDimensionPixelSize(
464 R.dimen.notification_guts_conversation_icon_size));
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500465
466 notificationInfoView.bindNotification(
Steve Elliott46bb2a12020-03-17 11:04:09 -0400467 mShortcutManager,
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500468 pmUser,
Steve Elliott46bb2a12020-03-17 11:04:09 -0400469 mNotificationManager,
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500470 mVisualStabilityManager,
471 packageName,
Evan Laird03c44452020-04-16 12:26:07 -0400472 entry.getChannel(),
473 entry,
474 entry.getBubbleMetadata(),
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500475 onSettingsClick,
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500476 onSnoozeClickListener,
Julia Reynolds01c9da42020-02-14 14:17:58 -0500477 iconFactoryLoader,
Evan Laird31ca5472020-04-08 17:45:24 -0400478 mContextTracker.getCurrentUserContext(),
479 mBuilderProvider,
Steve Elliott6771f542020-05-26 13:31:10 -0400480 mDeviceProvisionedController.isDeviceProvisioned(),
481 mMainHandler,
Julia Reynoldse03aa8f2020-06-02 11:21:38 -0400482 mBgHandler,
483 onConversationSettingsListener);
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500484 }
485
486 /**
Eliot Courtney47098cb2017-10-18 17:30:30 +0900487 * Closes guts or notification menus that might be visible and saves any changes.
488 *
489 * @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed.
490 * @param force true if guts should be closed regardless of state (used for snooze only).
491 * @param removeControls true if controls (e.g. info) should be closed.
492 * @param x if closed based on touch location, this is the x touch location.
493 * @param y if closed based on touch location, this is the y touch location.
494 * @param resetMenu if any notification menus that might be revealed should be closed.
495 */
496 public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls,
497 int x, int y, boolean resetMenu) {
498 if (mNotificationGutsExposed != null) {
Selim Cinek56e6a322019-10-01 12:06:05 -0700499 mNotificationGutsExposed.removeCallbacks(mOpenRunnable);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900500 mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
501 }
502 if (resetMenu) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900503 mListContainer.resetExposedMenuView(false /* animate */, true /* force */);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900504 }
505 }
506
507 /**
508 * Returns the exposed NotificationGuts or null if none are exposed.
509 */
510 public NotificationGuts getExposedGuts() {
511 return mNotificationGutsExposed;
512 }
513
514 public void setExposedGuts(NotificationGuts guts) {
515 mNotificationGutsExposed = guts;
516 }
517
Jason Monk297c04e2018-08-23 17:16:59 -0400518 public ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
519 return this::openGuts;
520 }
521
Eliot Courtney47098cb2017-10-18 17:30:30 +0900522 /**
Rohan Shah524cf7b2018-03-15 14:40:02 -0700523 * Opens guts on the given ExpandableNotificationRow {@code view}. This handles opening guts for
524 * the normal half-swipe and long-press use cases via a circular reveal. When the blocking
525 * helper needs to be shown on the row, this will skip the circular reveal.
Eliot Courtney47098cb2017-10-18 17:30:30 +0900526 *
Rohan Shah524cf7b2018-03-15 14:40:02 -0700527 * @param view ExpandableNotificationRow to open guts on
Eliot Courtney47098cb2017-10-18 17:30:30 +0900528 * @param x x coordinate of origin of circular reveal
529 * @param y y coordinate of origin of circular reveal
Rohan Shah524cf7b2018-03-15 14:40:02 -0700530 * @param menuItem MenuItem the guts should display
Eliot Courtney47098cb2017-10-18 17:30:30 +0900531 * @return true if guts was opened
532 */
Rohan Shah20790b82018-07-02 17:21:04 -0700533 public boolean openGuts(
Rohan Shah524cf7b2018-03-15 14:40:02 -0700534 View view,
535 int x,
536 int y,
537 NotificationMenuRowPlugin.MenuItem menuItem) {
Julia Reynoldsbe371072020-05-28 11:32:01 -0400538 if (menuItem.getGutsView() instanceof NotificationGuts.GutsContent) {
539 NotificationGuts.GutsContent gutsView =
540 (NotificationGuts.GutsContent) menuItem.getGutsView();
541 if (gutsView.needsFalsingProtection()) {
542 if (mStatusBarStateController instanceof StatusBarStateControllerImpl) {
543 ((StatusBarStateControllerImpl) mStatusBarStateController)
544 .setLeaveOpenOnKeyguardHide(true);
545 }
546
547 Runnable r = () -> mMainHandler.post(
548 () -> openGutsInternal(view, x, y, menuItem));
549
550 mStatusBarLazy.get().executeRunnableDismissingKeyguard(
551 r,
552 null /* cancelAction */,
553 false /* dismissShade */,
554 true /* afterKeyguardGone */,
555 true /* deferred */);
556
557 return true;
Evan Laird03cf3502019-05-31 16:46:48 -0400558 }
Evan Laird03cf3502019-05-31 16:46:48 -0400559 }
560 return openGutsInternal(view, x, y, menuItem);
561 }
562
563 @VisibleForTesting
564 boolean openGutsInternal(
565 View view,
566 int x,
567 int y,
568 NotificationMenuRowPlugin.MenuItem menuItem) {
569
Rohan Shah524cf7b2018-03-15 14:40:02 -0700570 if (!(view instanceof ExpandableNotificationRow)) {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900571 return false;
572 }
573
Rohan Shah524cf7b2018-03-15 14:40:02 -0700574 if (view.getWindowToken() == null) {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900575 Log.e(TAG, "Trying to show notification guts, but not attached to window");
576 return false;
577 }
578
Rohan Shah524cf7b2018-03-15 14:40:02 -0700579 final ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Rohan Shah524cf7b2018-03-15 14:40:02 -0700580 view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900581 if (row.areGutsExposed()) {
582 closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
583 true /* removeControls */, -1 /* x */, -1 /* y */,
584 true /* resetMenu */);
585 return false;
586 }
Julia Reynolds4131e782018-08-22 09:45:24 -0400587
Evan Lairde55c6012019-03-13 12:54:37 -0400588 row.ensureGutsInflated();
Eliot Courtney47098cb2017-10-18 17:30:30 +0900589 NotificationGuts guts = row.getGuts();
Julia Reynolds4131e782018-08-22 09:45:24 -0400590 mNotificationGutsExposed = guts;
591 if (!bindGuts(row, menuItem)) {
592 // exception occurred trying to fill in all the data, bail.
593 return false;
594 }
595
Eliot Courtney47098cb2017-10-18 17:30:30 +0900596
597 // Assume we are a status_bar_notification_row
598 if (guts == null) {
599 // This view has no guts. Examples are the more card or the dismiss all view
600 return false;
601 }
602
Eliot Courtney47098cb2017-10-18 17:30:30 +0900603 // ensure that it's laid but not visible until actually laid out
604 guts.setVisibility(View.INVISIBLE);
605 // Post to ensure the the guts are properly laid out.
Selim Cinek56e6a322019-10-01 12:06:05 -0700606 mOpenRunnable = new Runnable() {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900607 @Override
608 public void run() {
609 if (row.getWindowToken() == null) {
Rohan Shahae5fdf02018-05-09 18:59:44 -0700610 Log.e(TAG, "Trying to show notification guts in post(), but not attached to "
Eliot Courtney47098cb2017-10-18 17:30:30 +0900611 + "window");
612 return;
613 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900614 guts.setVisibility(View.VISIBLE);
yoshiki iguchia85c2a02018-01-12 11:28:06 +0900615
Eliot Courtney47098cb2017-10-18 17:30:30 +0900616 final boolean needsFalsingProtection =
Jason Monk297c04e2018-08-23 17:16:59 -0400617 (mStatusBarStateController.getState() == StatusBarState.KEYGUARD &&
Eliot Courtney47098cb2017-10-18 17:30:30 +0900618 !mAccessibilityManager.isTouchExplorationEnabled());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700619
620 guts.openControls(
621 !row.isBlockingHelperShowing(),
622 x,
623 y,
624 needsFalsingProtection,
Rohan Shah56eb0912018-05-10 21:49:04 -0700625 row::onGutsOpened);
yoshiki iguchia85c2a02018-01-12 11:28:06 +0900626
Eliot Courtney47098cb2017-10-18 17:30:30 +0900627 row.closeRemoteInput();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900628 mListContainer.onHeightChanged(row, true /* needsAnimation */);
Rohan Shah524cf7b2018-03-15 14:40:02 -0700629 mGutsMenuItem = menuItem;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900630 }
Selim Cinek56e6a322019-10-01 12:06:05 -0700631 };
632 guts.post(mOpenRunnable);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900633 return true;
634 }
635
636 @Override
Kevina5ff1fa2018-08-21 16:35:48 -0700637 public void setCallback(NotificationSafeToRemoveCallback callback) {
638 mNotificationLifetimeFinishedCallback = callback;
639 }
640
641 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500642 public boolean shouldExtendLifetime(NotificationEntry entry) {
Kevina5ff1fa2018-08-21 16:35:48 -0700643 return entry != null
644 &&(mNotificationGutsExposed != null
Evan Laird94492852018-10-25 13:43:01 -0400645 && entry.getGuts() != null
646 && mNotificationGutsExposed == entry.getGuts()
Kevina5ff1fa2018-08-21 16:35:48 -0700647 && !mNotificationGutsExposed.isLeavebehind());
648 }
649
650 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500651 public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) {
Kevina5ff1fa2018-08-21 16:35:48 -0700652 if (shouldExtend) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400653 mKeyToRemoveOnGutsClosed = entry.getKey();
Kevina5ff1fa2018-08-21 16:35:48 -0700654 if (Log.isLoggable(TAG, Log.DEBUG)) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400655 Log.d(TAG, "Keeping notification because it's showing guts. " + entry.getKey());
Kevina5ff1fa2018-08-21 16:35:48 -0700656 }
657 } else {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400658 if (mKeyToRemoveOnGutsClosed != null
659 && mKeyToRemoveOnGutsClosed.equals(entry.getKey())) {
Kevina5ff1fa2018-08-21 16:35:48 -0700660 mKeyToRemoveOnGutsClosed = null;
661 if (Log.isLoggable(TAG, Log.DEBUG)) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400662 Log.d(TAG, "Notification that was kept for guts was updated. "
663 + entry.getKey());
Kevina5ff1fa2018-08-21 16:35:48 -0700664 }
665 }
666 }
667 }
668
669 @Override
Eliot Courtney47098cb2017-10-18 17:30:30 +0900670 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Eliot Courtney09322282017-11-09 15:31:19 +0900671 pw.println("NotificationGutsManager state:");
672 pw.print(" mKeyToRemoveOnGutsClosed: ");
Eliot Courtney47098cb2017-10-18 17:30:30 +0900673 pw.println(mKeyToRemoveOnGutsClosed);
674 }
Julia Reynolds84dc96b2017-11-14 09:51:01 -0500675
676 public interface OnSettingsClickListener {
Jason Monk297c04e2018-08-23 17:16:59 -0400677 public void onSettingsClick(String key);
Julia Reynolds84dc96b2017-11-14 09:51:01 -0500678 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900679}