blob: 624fabc0a4966b09205c67bfbf438d8426c99a67 [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;
Dave Mankoff00e8a2f2019-12-18 16:59:49 -050049import com.android.systemui.dagger.qualifiers.Main;
Eliot Courtney47098cb2017-10-18 17:30:30 +090050import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
Beverly8fdb5332019-02-04 14:29:49 -050051import com.android.systemui.plugins.statusbar.StatusBarStateController;
Evan Laird31ca5472020-04-08 17:45:24 -040052import com.android.systemui.settings.CurrentUserContextTracker;
Kevina5ff1fa2018-08-21 16:35:48 -070053import com.android.systemui.statusbar.NotificationLifetimeExtender;
Rohan Shah20790b82018-07-02 17:21:04 -070054import com.android.systemui.statusbar.NotificationLockscreenUserManager;
55import com.android.systemui.statusbar.NotificationPresenter;
Jason Monk297c04e2018-08-23 17:16:59 -040056import com.android.systemui.statusbar.StatusBarState;
Evan Laird03cf3502019-05-31 16:46:48 -040057import com.android.systemui.statusbar.StatusBarStateControllerImpl;
Gus Prevas21437b32018-12-05 10:36:13 -050058import com.android.systemui.statusbar.notification.NotificationActivityStarter;
Ned Burns9512e0c2019-05-30 19:36:04 -040059import com.android.systemui.statusbar.notification.VisualStabilityManager;
Ned Burnsf81c4c42019-01-07 14:10:43 -050060import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Beverlye558f1d2020-01-07 16:28:58 -050061import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
Sergey Nikolaienkovf6ad6322020-02-10 15:46:32 +010062import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
Jason Monk297c04e2018-08-23 17:16:59 -040063import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
Rohan Shah20790b82018-07-02 17:21:04 -070064import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
Eliot Courtney47098cb2017-10-18 17:30:30 +090065import com.android.systemui.statusbar.phone.StatusBar;
Jason Monk297c04e2018-08-23 17:16:59 -040066import com.android.systemui.statusbar.policy.DeviceProvisionedController;
Eliot Courtney47098cb2017-10-18 17:30:30 +090067
68import java.io.FileDescriptor;
69import java.io.PrintWriter;
Eliot Courtney47098cb2017-10-18 17:30:30 +090070
Evan Laird31ca5472020-04-08 17:45:24 -040071import javax.inject.Provider;
72
Dave Mankoff4dd47fa2019-11-07 10:44:48 -050073import dagger.Lazy;
74
Eliot Courtney47098cb2017-10-18 17:30:30 +090075/**
76 * Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
77 * closing guts, and keeping track of the currently exposed notification guts.
78 */
Kevina5ff1fa2018-08-21 16:35:48 -070079public class NotificationGutsManager implements Dumpable, NotificationLifetimeExtender {
Eliot Courtney47098cb2017-10-18 17:30:30 +090080 private static final String TAG = "NotificationGutsManager";
81
82 // Must match constant in Settings. Used to highlight preferences when linking to Settings.
83 private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
84
85 private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
Eliot Courtney47098cb2017-10-18 17:30:30 +090086 private final Context mContext;
Ned Burns9512e0c2019-05-30 19:36:04 -040087 private final VisualStabilityManager mVisualStabilityManager;
Eliot Courtney47098cb2017-10-18 17:30:30 +090088 private final AccessibilityManager mAccessibilityManager;
Beverlye558f1d2020-01-07 16:28:58 -050089 private final HighPriorityProvider mHighPriorityProvider;
Eliot Courtney6c313d32017-12-14 19:57:51 +090090
91 // Dependencies:
92 private final NotificationLockscreenUserManager mLockscreenUserManager =
93 Dependency.get(NotificationLockscreenUserManager.class);
Jason Monk297c04e2018-08-23 17:16:59 -040094 private final StatusBarStateController mStatusBarStateController =
95 Dependency.get(StatusBarStateController.class);
96 private final DeviceProvisionedController mDeviceProvisionedController =
97 Dependency.get(DeviceProvisionedController.class);
Eliot Courtney09322282017-11-09 15:31:19 +090098
Eliot Courtney47098cb2017-10-18 17:30:30 +090099 // which notification is currently being longpress-examined by the user
100 private NotificationGuts mNotificationGutsExposed;
101 private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
Kevina5ff1fa2018-08-21 16:35:48 -0700102 private NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
Jason Monk297c04e2018-08-23 17:16:59 -0400103 private NotificationPresenter mPresenter;
Gus Prevas21437b32018-12-05 10:36:13 -0500104 private NotificationActivityStarter mNotificationActivityStarter;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900105 private NotificationListContainer mListContainer;
Jason Monk297c04e2018-08-23 17:16:59 -0400106 private CheckSaveListener mCheckSaveListener;
Eliot Courtney09322282017-11-09 15:31:19 +0900107 private OnSettingsClickListener mOnSettingsClickListener;
Kevina5ff1fa2018-08-21 16:35:48 -0700108 @VisibleForTesting
109 protected String mKeyToRemoveOnGutsClosed;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900110
Dave Mankoff4dd47fa2019-11-07 10:44:48 -0500111 private final Lazy<StatusBar> mStatusBarLazy;
Dave Mankoff5fe07f72019-11-19 22:14:41 -0500112 private final Handler mMainHandler;
Selim Cinek56e6a322019-10-01 12:06:05 -0700113 private Runnable mOpenRunnable;
Steve Elliott46bb2a12020-03-17 11:04:09 -0400114 private final INotificationManager mNotificationManager;
115 private final LauncherApps mLauncherApps;
116 private final ShortcutManager mShortcutManager;
Evan Laird31ca5472020-04-08 17:45:24 -0400117 private final CurrentUserContextTracker mContextTracker;
118 private final Provider<PriorityOnboardingDialogController.Builder> mBuilderProvider;
Evan Laird03cf3502019-05-31 16:46:48 -0400119
Sergey Nikolaienkovf6ad6322020-02-10 15:46:32 +0100120 /**
121 * Injected constructor. See {@link NotificationsModule}.
122 */
Dave Mankoff4dd47fa2019-11-07 10:44:48 -0500123 public NotificationGutsManager(Context context, VisualStabilityManager visualStabilityManager,
Dave Mankoff00e8a2f2019-12-18 16:59:49 -0500124 Lazy<StatusBar> statusBarLazy, @Main Handler mainHandler,
Beverlye558f1d2020-01-07 16:28:58 -0500125 AccessibilityManager accessibilityManager,
Steve Elliott46bb2a12020-03-17 11:04:09 -0400126 HighPriorityProvider highPriorityProvider,
127 INotificationManager notificationManager,
128 LauncherApps launcherApps,
Evan Laird31ca5472020-04-08 17:45:24 -0400129 ShortcutManager shortcutManager,
130 CurrentUserContextTracker contextTracker,
131 Provider<PriorityOnboardingDialogController.Builder> builderProvider) {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900132 mContext = context;
Ned Burns9512e0c2019-05-30 19:36:04 -0400133 mVisualStabilityManager = visualStabilityManager;
Dave Mankoff4dd47fa2019-11-07 10:44:48 -0500134 mStatusBarLazy = statusBarLazy;
Dave Mankoff5fe07f72019-11-19 22:14:41 -0500135 mMainHandler = mainHandler;
136 mAccessibilityManager = accessibilityManager;
Beverlye558f1d2020-01-07 16:28:58 -0500137 mHighPriorityProvider = highPriorityProvider;
Steve Elliott46bb2a12020-03-17 11:04:09 -0400138 mNotificationManager = notificationManager;
139 mLauncherApps = launcherApps;
140 mShortcutManager = shortcutManager;
Evan Laird31ca5472020-04-08 17:45:24 -0400141 mContextTracker = contextTracker;
142 mBuilderProvider = builderProvider;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900143 }
144
Eliot Courtney4a96b362017-12-14 19:38:52 +0900145 public void setUpWithPresenter(NotificationPresenter presenter,
Kevina5ff1fa2018-08-21 16:35:48 -0700146 NotificationListContainer listContainer,
Jason Monk297c04e2018-08-23 17:16:59 -0400147 CheckSaveListener checkSave, OnSettingsClickListener onSettingsClick) {
Eliot Courtney09322282017-11-09 15:31:19 +0900148 mPresenter = presenter;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900149 mListContainer = listContainer;
Jason Monk297c04e2018-08-23 17:16:59 -0400150 mCheckSaveListener = checkSave;
151 mOnSettingsClickListener = onSettingsClick;
Eliot Courtney09322282017-11-09 15:31:19 +0900152 }
153
Gus Prevas21437b32018-12-05 10:36:13 -0500154 public void setNotificationActivityStarter(
155 NotificationActivityStarter notificationActivityStarter) {
156 mNotificationActivityStarter = notificationActivityStarter;
157 }
158
Ned Burnsf81c4c42019-01-07 14:10:43 -0500159 public void onDensityOrFontScaleChanged(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -0400160 setExposedGuts(entry.getGuts());
161 bindGuts(entry.getRow());
yoshiki iguchia85c2a02018-01-12 11:28:06 +0900162 }
163
Eliot Courtney47098cb2017-10-18 17:30:30 +0900164 /**
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400165 * Sends an intent to open the notification settings for a particular package and optional
Eliot Courtney47098cb2017-10-18 17:30:30 +0900166 * channel.
167 */
Evan Laird47dc4542019-04-24 15:10:52 -0400168 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
Eliot Courtney47098cb2017-10-18 17:30:30 +0900169 private void startAppNotificationSettingsActivity(String packageName, final int appUid,
Selim Cinek2627d722018-01-19 12:16:49 -0800170 final NotificationChannel channel, ExpandableNotificationRow row) {
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400171 final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
172 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
173 intent.putExtra(Settings.EXTRA_APP_UID, appUid);
Evan Laird47dc4542019-04-24 15:10:52 -0400174
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400175 if (channel != null) {
Evan Laird47dc4542019-04-24 15:10:52 -0400176 final Bundle args = new Bundle();
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400177 intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
Evan Laird47dc4542019-04-24 15:10:52 -0400178 args.putString(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
179 intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400180 }
181 mNotificationActivityStarter.startNotificationGutsIntent(intent, appUid, row);
182 }
183
184 private void startAppDetailsSettingsActivity(String packageName, final int appUid,
185 final NotificationChannel channel, ExpandableNotificationRow row) {
Julia Reynoldsf5c04872018-02-13 09:54:04 -0500186 final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
187 intent.setData(Uri.fromParts("package", packageName, null));
Eliot Courtney47098cb2017-10-18 17:30:30 +0900188 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
189 intent.putExtra(Settings.EXTRA_APP_UID, appUid);
190 if (channel != null) {
191 intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
192 }
Gus Prevas21437b32018-12-05 10:36:13 -0500193 mNotificationActivityStarter.startNotificationGutsIntent(intent, appUid, row);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900194 }
195
Julia Reynoldsb5867452018-02-28 16:31:35 -0500196 protected void startAppOpsSettingsActivity(String pkg, int uid, ArraySet<Integer> ops,
197 ExpandableNotificationRow row) {
198 if (ops.contains(OP_SYSTEM_ALERT_WINDOW)) {
199 if (ops.contains(OP_CAMERA) || ops.contains(OP_RECORD_AUDIO)) {
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400200 startAppDetailsSettingsActivity(pkg, uid, null, row);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500201 } else {
Bernardo Rufino8fefb942019-11-25 19:51:09 +0000202 Intent intent = new Intent(Settings.ACTION_MANAGE_APP_OVERLAY_PERMISSION);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500203 intent.setData(Uri.fromParts("package", pkg, null));
Gus Prevas21437b32018-12-05 10:36:13 -0500204 mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500205 }
206 } else if (ops.contains(OP_CAMERA) || ops.contains(OP_RECORD_AUDIO)) {
207 Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
208 intent.putExtra(Intent.EXTRA_PACKAGE_NAME, pkg);
Gus Prevas21437b32018-12-05 10:36:13 -0500209 mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500210 }
211 }
212
Evan Lairde55c6012019-03-13 12:54:37 -0400213 private boolean bindGuts(final ExpandableNotificationRow row) {
214 row.ensureGutsInflated();
Julia Reynolds4131e782018-08-22 09:45:24 -0400215 return bindGuts(row, mGutsMenuItem);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900216 }
217
TreeHugger Robotee8e1ae2019-01-18 19:26:40 +0000218 @VisibleForTesting
219 protected boolean bindGuts(final ExpandableNotificationRow row,
Eliot Courtney47098cb2017-10-18 17:30:30 +0900220 NotificationMenuRowPlugin.MenuItem item) {
Ned Burns1c2b85a42019-11-14 15:37:03 -0500221 StatusBarNotification sbn = row.getEntry().getSbn();
Rohan Shah524cf7b2018-03-15 14:40:02 -0700222
Eliot Courtney47098cb2017-10-18 17:30:30 +0900223 row.setGutsView(item);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900224 row.setTag(sbn.getPackageName());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700225 row.getGuts().setClosedListener((NotificationGuts g) -> {
Rohan Shah56eb0912018-05-10 21:49:04 -0700226 row.onGutsClosed();
Eliot Courtney47098cb2017-10-18 17:30:30 +0900227 if (!g.willBeRemoved() && !row.isRemoved()) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900228 mListContainer.onHeightChanged(
Eliot Courtney47098cb2017-10-18 17:30:30 +0900229 row, !mPresenter.isPresenterFullyCollapsed() /* needsAnimation */);
230 }
231 if (mNotificationGutsExposed == g) {
232 mNotificationGutsExposed = null;
233 mGutsMenuItem = null;
234 }
235 String key = sbn.getKey();
236 if (key.equals(mKeyToRemoveOnGutsClosed)) {
237 mKeyToRemoveOnGutsClosed = null;
Kevina5ff1fa2018-08-21 16:35:48 -0700238 if (mNotificationLifetimeFinishedCallback != null) {
239 mNotificationLifetimeFinishedCallback.onSafeToRemove(key);
240 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900241 }
242 });
243
244 View gutsView = item.getGutsView();
Julia Reynolds4131e782018-08-22 09:45:24 -0400245 try {
246 if (gutsView instanceof NotificationSnooze) {
247 initializeSnoozeView(row, (NotificationSnooze) gutsView);
248 } else if (gutsView instanceof AppOpsInfo) {
249 initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
250 } else if (gutsView instanceof NotificationInfo) {
Gus Prevas5a70a4e2018-11-26 17:16:05 -0500251 initializeNotificationInfo(row, (NotificationInfo) gutsView);
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500252 } else if (gutsView instanceof NotificationConversationInfo) {
253 initializeConversationNotificationInfo(
254 row, (NotificationConversationInfo) gutsView);
Julia Reynolds4131e782018-08-22 09:45:24 -0400255 }
256 return true;
257 } catch (Exception e) {
258 Log.e(TAG, "error binding guts", e);
259 return false;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900260 }
Rohan Shah524cf7b2018-03-15 14:40:02 -0700261 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900262
Rohan Shah524cf7b2018-03-15 14:40:02 -0700263 /**
264 * Sets up the {@link NotificationSnooze} inside the notification row's guts.
265 *
266 * @param row view to set up the guts for
267 * @param notificationSnoozeView view to set up/bind within {@code row}
268 */
269 private void initializeSnoozeView(
270 final ExpandableNotificationRow row,
271 NotificationSnooze notificationSnoozeView) {
272 NotificationGuts guts = row.getGuts();
Ned Burns1c2b85a42019-11-14 15:37:03 -0500273 StatusBarNotification sbn = row.getEntry().getSbn();
Rohan Shah524cf7b2018-03-15 14:40:02 -0700274
275 notificationSnoozeView.setSnoozeListener(mListContainer.getSwipeActionHelper());
276 notificationSnoozeView.setStatusBarNotification(sbn);
Ned Burns47c98f12019-09-06 17:12:07 -0400277 notificationSnoozeView.setSnoozeOptions(row.getEntry().getSnoozeCriteria());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700278 guts.setHeightChangedListener((NotificationGuts g) -> {
279 mListContainer.onHeightChanged(row, row.isShown() /* needsAnimation */);
280 });
281 }
282
283 /**
284 * Sets up the {@link AppOpsInfo} inside the notification row's guts.
285 *
286 * @param row view to set up the guts for
287 * @param appOpsInfoView view to set up/bind within {@code row}
288 */
289 private void initializeAppOpsInfo(
290 final ExpandableNotificationRow row,
291 AppOpsInfo appOpsInfoView) {
292 NotificationGuts guts = row.getGuts();
Ned Burns1c2b85a42019-11-14 15:37:03 -0500293 StatusBarNotification sbn = row.getEntry().getSbn();
Rohan Shah524cf7b2018-03-15 14:40:02 -0700294 UserHandle userHandle = sbn.getUser();
295 PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
296 userHandle.getIdentifier());
297
298 AppOpsInfo.OnSettingsClickListener onSettingsClick =
299 (View v, String pkg, int uid, ArraySet<Integer> ops) -> {
300 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_OPS_GUTS_SETTINGS);
301 guts.resetFalsingCheck();
302 startAppOpsSettingsActivity(pkg, uid, ops, row);
303 };
304 if (!row.getEntry().mActiveAppOps.isEmpty()) {
305 appOpsInfoView.bindGuts(pmUser, onSettingsClick, sbn, row.getEntry().mActiveAppOps);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500306 }
Rohan Shah524cf7b2018-03-15 14:40:02 -0700307 }
Julia Reynoldsb5867452018-02-28 16:31:35 -0500308
Rohan Shah524cf7b2018-03-15 14:40:02 -0700309 /**
310 * Sets up the {@link NotificationInfo} inside the notification row's guts.
Rohan Shah524cf7b2018-03-15 14:40:02 -0700311 * @param row view to set up the guts for
312 * @param notificationInfoView view to set up/bind within {@code row}
313 */
314 @VisibleForTesting
315 void initializeNotificationInfo(
316 final ExpandableNotificationRow row,
Gus Prevas5a70a4e2018-11-26 17:16:05 -0500317 NotificationInfo notificationInfoView) throws Exception {
Rohan Shah524cf7b2018-03-15 14:40:02 -0700318 NotificationGuts guts = row.getGuts();
Ned Burns1c2b85a42019-11-14 15:37:03 -0500319 StatusBarNotification sbn = row.getEntry().getSbn();
Rohan Shah524cf7b2018-03-15 14:40:02 -0700320 String packageName = sbn.getPackageName();
321 // Settings link is only valid for notifications that specify a non-system user
322 NotificationInfo.OnSettingsClickListener onSettingsClick = null;
323 UserHandle userHandle = sbn.getUser();
324 PackageManager pmUser = StatusBar.getPackageManagerForUser(
325 mContext, userHandle.getIdentifier());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700326 final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick =
327 (View v, Intent intent) -> {
328 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900329 guts.resetFalsingCheck();
Gus Prevas21437b32018-12-05 10:36:13 -0500330 mNotificationActivityStarter.startNotificationGutsIntent(intent, sbn.getUid(),
331 row);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900332 };
Eliot Courtney47098cb2017-10-18 17:30:30 +0900333
Rohan Shah524cf7b2018-03-15 14:40:02 -0700334 if (!userHandle.equals(UserHandle.ALL)
335 || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
336 onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
337 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
338 guts.resetFalsingCheck();
Jason Monk297c04e2018-08-23 17:16:59 -0400339 mOnSettingsClickListener.onSettingsClick(sbn.getKey());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700340 startAppNotificationSettingsActivity(packageName, appUid, channel, row);
341 };
342 }
343
Julia Reynolds4131e782018-08-22 09:45:24 -0400344 notificationInfoView.bindNotification(
345 pmUser,
Steve Elliott46bb2a12020-03-17 11:04:09 -0400346 mNotificationManager,
Ned Burns9512e0c2019-05-30 19:36:04 -0400347 mVisualStabilityManager,
Julia Reynolds4131e782018-08-22 09:45:24 -0400348 packageName,
Ned Burns296aec162019-09-04 17:30:59 -0400349 row.getEntry().getChannel(),
Evan Laird47dc4542019-04-24 15:10:52 -0400350 row.getUniqueChannels(),
Mady Mellor53162c12019-10-22 17:12:59 -0700351 row.getEntry(),
Julia Reynolds4131e782018-08-22 09:45:24 -0400352 onSettingsClick,
353 onAppSettingsClick,
Jason Monk297c04e2018-08-23 17:16:59 -0400354 mDeviceProvisionedController.isDeviceProvisioned(),
Julia Reynolds4131e782018-08-22 09:45:24 -0400355 row.getIsNonblockable(),
Beverlye558f1d2020-01-07 16:28:58 -0500356 mHighPriorityProvider.isHighPriority(row.getEntry()));
Rohan Shah524cf7b2018-03-15 14:40:02 -0700357 }
358
359 /**
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500360 * Sets up the {@link NotificationConversationInfo} inside the notification row's guts.
361 * @param row view to set up the guts for
362 * @param notificationInfoView view to set up/bind within {@code row}
363 */
364 @VisibleForTesting
365 void initializeConversationNotificationInfo(
366 final ExpandableNotificationRow row,
367 NotificationConversationInfo notificationInfoView) throws Exception {
368 NotificationGuts guts = row.getGuts();
369 StatusBarNotification sbn = row.getEntry().getSbn();
370 String packageName = sbn.getPackageName();
371 // Settings link is only valid for notifications that specify a non-system user
372 NotificationConversationInfo.OnSettingsClickListener onSettingsClick = null;
373 UserHandle userHandle = sbn.getUser();
374 PackageManager pmUser = StatusBar.getPackageManagerForUser(
375 mContext, userHandle.getIdentifier());
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500376 final NotificationConversationInfo.OnAppSettingsClickListener onAppSettingsClick =
377 (View v, Intent intent) -> {
378 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
379 guts.resetFalsingCheck();
380 mNotificationActivityStarter.startNotificationGutsIntent(intent, sbn.getUid(),
381 row);
382 };
383
384 final NotificationConversationInfo.OnSnoozeClickListener onSnoozeClickListener =
385 (View v, int hours) -> {
386 mListContainer.getSwipeActionHelper().snooze(sbn, hours);
387 };
388
389 if (!userHandle.equals(UserHandle.ALL)
390 || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
391 onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
392 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
393 guts.resetFalsingCheck();
394 mOnSettingsClickListener.onSettingsClick(sbn.getKey());
395 startAppNotificationSettingsActivity(packageName, appUid, channel, row);
Julia Reynoldse6fed502020-02-07 14:41:02 -0500396 notificationInfoView.closeControls(v, false);
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500397 };
398 }
Julia Reynolds01c9da42020-02-14 14:17:58 -0500399 ConversationIconFactory iconFactoryLoader = new ConversationIconFactory(mContext,
Steve Elliott46bb2a12020-03-17 11:04:09 -0400400 mLauncherApps, pmUser, IconDrawableFactory.newInstance(mContext, false),
Julia Reynolds01c9da42020-02-14 14:17:58 -0500401 mContext.getResources().getDimensionPixelSize(
402 R.dimen.notification_guts_conversation_icon_size));
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500403
404 notificationInfoView.bindNotification(
Steve Elliott46bb2a12020-03-17 11:04:09 -0400405 mShortcutManager,
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500406 pmUser,
Steve Elliott46bb2a12020-03-17 11:04:09 -0400407 mNotificationManager,
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500408 mVisualStabilityManager,
409 packageName,
410 row.getEntry().getChannel(),
411 row.getEntry(),
412 onSettingsClick,
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500413 onSnoozeClickListener,
Julia Reynolds01c9da42020-02-14 14:17:58 -0500414 iconFactoryLoader,
Evan Laird31ca5472020-04-08 17:45:24 -0400415 mContextTracker.getCurrentUserContext(),
416 mBuilderProvider,
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500417 mDeviceProvisionedController.isDeviceProvisioned());
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500418 }
419
420 /**
Eliot Courtney47098cb2017-10-18 17:30:30 +0900421 * Closes guts or notification menus that might be visible and saves any changes.
422 *
423 * @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed.
424 * @param force true if guts should be closed regardless of state (used for snooze only).
425 * @param removeControls true if controls (e.g. info) should be closed.
426 * @param x if closed based on touch location, this is the x touch location.
427 * @param y if closed based on touch location, this is the y touch location.
428 * @param resetMenu if any notification menus that might be revealed should be closed.
429 */
430 public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls,
431 int x, int y, boolean resetMenu) {
432 if (mNotificationGutsExposed != null) {
Selim Cinek56e6a322019-10-01 12:06:05 -0700433 mNotificationGutsExposed.removeCallbacks(mOpenRunnable);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900434 mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
435 }
436 if (resetMenu) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900437 mListContainer.resetExposedMenuView(false /* animate */, true /* force */);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900438 }
439 }
440
441 /**
442 * Returns the exposed NotificationGuts or null if none are exposed.
443 */
444 public NotificationGuts getExposedGuts() {
445 return mNotificationGutsExposed;
446 }
447
448 public void setExposedGuts(NotificationGuts guts) {
449 mNotificationGutsExposed = guts;
450 }
451
Jason Monk297c04e2018-08-23 17:16:59 -0400452 public ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
453 return this::openGuts;
454 }
455
Eliot Courtney47098cb2017-10-18 17:30:30 +0900456 /**
Rohan Shah524cf7b2018-03-15 14:40:02 -0700457 * Opens guts on the given ExpandableNotificationRow {@code view}. This handles opening guts for
458 * the normal half-swipe and long-press use cases via a circular reveal. When the blocking
459 * helper needs to be shown on the row, this will skip the circular reveal.
Eliot Courtney47098cb2017-10-18 17:30:30 +0900460 *
Rohan Shah524cf7b2018-03-15 14:40:02 -0700461 * @param view ExpandableNotificationRow to open guts on
Eliot Courtney47098cb2017-10-18 17:30:30 +0900462 * @param x x coordinate of origin of circular reveal
463 * @param y y coordinate of origin of circular reveal
Rohan Shah524cf7b2018-03-15 14:40:02 -0700464 * @param menuItem MenuItem the guts should display
Eliot Courtney47098cb2017-10-18 17:30:30 +0900465 * @return true if guts was opened
466 */
Rohan Shah20790b82018-07-02 17:21:04 -0700467 public boolean openGuts(
Rohan Shah524cf7b2018-03-15 14:40:02 -0700468 View view,
469 int x,
470 int y,
471 NotificationMenuRowPlugin.MenuItem menuItem) {
Evan Laird03cf3502019-05-31 16:46:48 -0400472 if (menuItem.getGutsView() instanceof NotificationInfo) {
473 if (mStatusBarStateController instanceof StatusBarStateControllerImpl) {
474 ((StatusBarStateControllerImpl) mStatusBarStateController)
475 .setLeaveOpenOnKeyguardHide(true);
476 }
477
Dave Mankoff5fe07f72019-11-19 22:14:41 -0500478 Runnable r = () -> mMainHandler.post(
Evan Laird03cf3502019-05-31 16:46:48 -0400479 () -> openGutsInternal(view, x, y, menuItem));
480
Dave Mankoff4dd47fa2019-11-07 10:44:48 -0500481 mStatusBarLazy.get().executeRunnableDismissingKeyguard(
Evan Laird03cf3502019-05-31 16:46:48 -0400482 r,
483 null /* cancelAction */,
484 false /* dismissShade */,
485 true /* afterKeyguardGone */,
486 true /* deferred */);
487
488 return true;
489 }
490 return openGutsInternal(view, x, y, menuItem);
491 }
492
493 @VisibleForTesting
494 boolean openGutsInternal(
495 View view,
496 int x,
497 int y,
498 NotificationMenuRowPlugin.MenuItem menuItem) {
499
Rohan Shah524cf7b2018-03-15 14:40:02 -0700500 if (!(view instanceof ExpandableNotificationRow)) {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900501 return false;
502 }
503
Rohan Shah524cf7b2018-03-15 14:40:02 -0700504 if (view.getWindowToken() == null) {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900505 Log.e(TAG, "Trying to show notification guts, but not attached to window");
506 return false;
507 }
508
Rohan Shah524cf7b2018-03-15 14:40:02 -0700509 final ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Rohan Shah524cf7b2018-03-15 14:40:02 -0700510 view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900511 if (row.areGutsExposed()) {
512 closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
513 true /* removeControls */, -1 /* x */, -1 /* y */,
514 true /* resetMenu */);
515 return false;
516 }
Julia Reynolds4131e782018-08-22 09:45:24 -0400517
Evan Lairde55c6012019-03-13 12:54:37 -0400518 row.ensureGutsInflated();
Eliot Courtney47098cb2017-10-18 17:30:30 +0900519 NotificationGuts guts = row.getGuts();
Julia Reynolds4131e782018-08-22 09:45:24 -0400520 mNotificationGutsExposed = guts;
521 if (!bindGuts(row, menuItem)) {
522 // exception occurred trying to fill in all the data, bail.
523 return false;
524 }
525
Eliot Courtney47098cb2017-10-18 17:30:30 +0900526
527 // Assume we are a status_bar_notification_row
528 if (guts == null) {
529 // This view has no guts. Examples are the more card or the dismiss all view
530 return false;
531 }
532
Eliot Courtney47098cb2017-10-18 17:30:30 +0900533 // ensure that it's laid but not visible until actually laid out
534 guts.setVisibility(View.INVISIBLE);
535 // Post to ensure the the guts are properly laid out.
Selim Cinek56e6a322019-10-01 12:06:05 -0700536 mOpenRunnable = new Runnable() {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900537 @Override
538 public void run() {
539 if (row.getWindowToken() == null) {
Rohan Shahae5fdf02018-05-09 18:59:44 -0700540 Log.e(TAG, "Trying to show notification guts in post(), but not attached to "
Eliot Courtney47098cb2017-10-18 17:30:30 +0900541 + "window");
542 return;
543 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900544 guts.setVisibility(View.VISIBLE);
yoshiki iguchia85c2a02018-01-12 11:28:06 +0900545
Eliot Courtney47098cb2017-10-18 17:30:30 +0900546 final boolean needsFalsingProtection =
Jason Monk297c04e2018-08-23 17:16:59 -0400547 (mStatusBarStateController.getState() == StatusBarState.KEYGUARD &&
Eliot Courtney47098cb2017-10-18 17:30:30 +0900548 !mAccessibilityManager.isTouchExplorationEnabled());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700549
550 guts.openControls(
551 !row.isBlockingHelperShowing(),
552 x,
553 y,
554 needsFalsingProtection,
Rohan Shah56eb0912018-05-10 21:49:04 -0700555 row::onGutsOpened);
yoshiki iguchia85c2a02018-01-12 11:28:06 +0900556
Eliot Courtney47098cb2017-10-18 17:30:30 +0900557 row.closeRemoteInput();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900558 mListContainer.onHeightChanged(row, true /* needsAnimation */);
Rohan Shah524cf7b2018-03-15 14:40:02 -0700559 mGutsMenuItem = menuItem;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900560 }
Selim Cinek56e6a322019-10-01 12:06:05 -0700561 };
562 guts.post(mOpenRunnable);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900563 return true;
564 }
565
566 @Override
Kevina5ff1fa2018-08-21 16:35:48 -0700567 public void setCallback(NotificationSafeToRemoveCallback callback) {
568 mNotificationLifetimeFinishedCallback = callback;
569 }
570
571 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500572 public boolean shouldExtendLifetime(NotificationEntry entry) {
Kevina5ff1fa2018-08-21 16:35:48 -0700573 return entry != null
574 &&(mNotificationGutsExposed != null
Evan Laird94492852018-10-25 13:43:01 -0400575 && entry.getGuts() != null
576 && mNotificationGutsExposed == entry.getGuts()
Kevina5ff1fa2018-08-21 16:35:48 -0700577 && !mNotificationGutsExposed.isLeavebehind());
578 }
579
580 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500581 public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) {
Kevina5ff1fa2018-08-21 16:35:48 -0700582 if (shouldExtend) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400583 mKeyToRemoveOnGutsClosed = entry.getKey();
Kevina5ff1fa2018-08-21 16:35:48 -0700584 if (Log.isLoggable(TAG, Log.DEBUG)) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400585 Log.d(TAG, "Keeping notification because it's showing guts. " + entry.getKey());
Kevina5ff1fa2018-08-21 16:35:48 -0700586 }
587 } else {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400588 if (mKeyToRemoveOnGutsClosed != null
589 && mKeyToRemoveOnGutsClosed.equals(entry.getKey())) {
Kevina5ff1fa2018-08-21 16:35:48 -0700590 mKeyToRemoveOnGutsClosed = null;
591 if (Log.isLoggable(TAG, Log.DEBUG)) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400592 Log.d(TAG, "Notification that was kept for guts was updated. "
593 + entry.getKey());
Kevina5ff1fa2018-08-21 16:35:48 -0700594 }
595 }
596 }
597 }
598
599 @Override
Eliot Courtney47098cb2017-10-18 17:30:30 +0900600 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Eliot Courtney09322282017-11-09 15:31:19 +0900601 pw.println("NotificationGutsManager state:");
602 pw.print(" mKeyToRemoveOnGutsClosed: ");
Eliot Courtney47098cb2017-10-18 17:30:30 +0900603 pw.println(mKeyToRemoveOnGutsClosed);
604 }
Julia Reynolds84dc96b2017-11-14 09:51:01 -0500605
606 public interface OnSettingsClickListener {
Jason Monk297c04e2018-08-23 17:16:59 -0400607 public void onSettingsClick(String key);
Julia Reynolds84dc96b2017-11-14 09:51:01 -0500608 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900609}