blob: 2487d1a898a3e9d2eadf7eae5f830feab9dd67c3 [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;
Kevina5ff1fa2018-08-21 16:35:48 -070052import com.android.systemui.statusbar.NotificationLifetimeExtender;
Rohan Shah20790b82018-07-02 17:21:04 -070053import com.android.systemui.statusbar.NotificationLockscreenUserManager;
54import com.android.systemui.statusbar.NotificationPresenter;
Jason Monk297c04e2018-08-23 17:16:59 -040055import com.android.systemui.statusbar.StatusBarState;
Evan Laird03cf3502019-05-31 16:46:48 -040056import com.android.systemui.statusbar.StatusBarStateControllerImpl;
Gus Prevas21437b32018-12-05 10:36:13 -050057import com.android.systemui.statusbar.notification.NotificationActivityStarter;
Ned Burns9512e0c2019-05-30 19:36:04 -040058import com.android.systemui.statusbar.notification.VisualStabilityManager;
Ned Burnsf81c4c42019-01-07 14:10:43 -050059import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Beverlye558f1d2020-01-07 16:28:58 -050060import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
Sergey Nikolaienkovf6ad6322020-02-10 15:46:32 +010061import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
Jason Monk297c04e2018-08-23 17:16:59 -040062import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
Rohan Shah20790b82018-07-02 17:21:04 -070063import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
Eliot Courtney47098cb2017-10-18 17:30:30 +090064import com.android.systemui.statusbar.phone.StatusBar;
Jason Monk297c04e2018-08-23 17:16:59 -040065import com.android.systemui.statusbar.policy.DeviceProvisionedController;
Eliot Courtney47098cb2017-10-18 17:30:30 +090066
67import java.io.FileDescriptor;
68import java.io.PrintWriter;
Eliot Courtney47098cb2017-10-18 17:30:30 +090069
Dave Mankoff4dd47fa2019-11-07 10:44:48 -050070import dagger.Lazy;
71
Eliot Courtney47098cb2017-10-18 17:30:30 +090072/**
73 * Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
74 * closing guts, and keeping track of the currently exposed notification guts.
75 */
Kevina5ff1fa2018-08-21 16:35:48 -070076public class NotificationGutsManager implements Dumpable, NotificationLifetimeExtender {
Eliot Courtney47098cb2017-10-18 17:30:30 +090077 private static final String TAG = "NotificationGutsManager";
78
79 // Must match constant in Settings. Used to highlight preferences when linking to Settings.
80 private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
81
82 private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
Eliot Courtney47098cb2017-10-18 17:30:30 +090083 private final Context mContext;
Ned Burns9512e0c2019-05-30 19:36:04 -040084 private final VisualStabilityManager mVisualStabilityManager;
Eliot Courtney47098cb2017-10-18 17:30:30 +090085 private final AccessibilityManager mAccessibilityManager;
Beverlye558f1d2020-01-07 16:28:58 -050086 private final HighPriorityProvider mHighPriorityProvider;
Eliot Courtney6c313d32017-12-14 19:57:51 +090087
88 // Dependencies:
89 private final NotificationLockscreenUserManager mLockscreenUserManager =
90 Dependency.get(NotificationLockscreenUserManager.class);
Jason Monk297c04e2018-08-23 17:16:59 -040091 private final StatusBarStateController mStatusBarStateController =
92 Dependency.get(StatusBarStateController.class);
93 private final DeviceProvisionedController mDeviceProvisionedController =
94 Dependency.get(DeviceProvisionedController.class);
Eliot Courtney09322282017-11-09 15:31:19 +090095
Eliot Courtney47098cb2017-10-18 17:30:30 +090096 // which notification is currently being longpress-examined by the user
97 private NotificationGuts mNotificationGutsExposed;
98 private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
Kevina5ff1fa2018-08-21 16:35:48 -070099 private NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
Jason Monk297c04e2018-08-23 17:16:59 -0400100 private NotificationPresenter mPresenter;
Gus Prevas21437b32018-12-05 10:36:13 -0500101 private NotificationActivityStarter mNotificationActivityStarter;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900102 private NotificationListContainer mListContainer;
Jason Monk297c04e2018-08-23 17:16:59 -0400103 private CheckSaveListener mCheckSaveListener;
Eliot Courtney09322282017-11-09 15:31:19 +0900104 private OnSettingsClickListener mOnSettingsClickListener;
Kevina5ff1fa2018-08-21 16:35:48 -0700105 @VisibleForTesting
106 protected String mKeyToRemoveOnGutsClosed;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900107
Dave Mankoff4dd47fa2019-11-07 10:44:48 -0500108 private final Lazy<StatusBar> mStatusBarLazy;
Dave Mankoff5fe07f72019-11-19 22:14:41 -0500109 private final Handler mMainHandler;
Selim Cinek56e6a322019-10-01 12:06:05 -0700110 private Runnable mOpenRunnable;
Steve Elliott46bb2a12020-03-17 11:04:09 -0400111 private final INotificationManager mNotificationManager;
112 private final LauncherApps mLauncherApps;
113 private final ShortcutManager mShortcutManager;
Evan Laird03cf3502019-05-31 16:46:48 -0400114
Sergey Nikolaienkovf6ad6322020-02-10 15:46:32 +0100115 /**
116 * Injected constructor. See {@link NotificationsModule}.
117 */
Dave Mankoff4dd47fa2019-11-07 10:44:48 -0500118 public NotificationGutsManager(Context context, VisualStabilityManager visualStabilityManager,
Dave Mankoff00e8a2f2019-12-18 16:59:49 -0500119 Lazy<StatusBar> statusBarLazy, @Main Handler mainHandler,
Beverlye558f1d2020-01-07 16:28:58 -0500120 AccessibilityManager accessibilityManager,
Steve Elliott46bb2a12020-03-17 11:04:09 -0400121 HighPriorityProvider highPriorityProvider,
122 INotificationManager notificationManager,
123 LauncherApps launcherApps,
124 ShortcutManager shortcutManager) {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900125 mContext = context;
Ned Burns9512e0c2019-05-30 19:36:04 -0400126 mVisualStabilityManager = visualStabilityManager;
Dave Mankoff4dd47fa2019-11-07 10:44:48 -0500127 mStatusBarLazy = statusBarLazy;
Dave Mankoff5fe07f72019-11-19 22:14:41 -0500128 mMainHandler = mainHandler;
129 mAccessibilityManager = accessibilityManager;
Beverlye558f1d2020-01-07 16:28:58 -0500130 mHighPriorityProvider = highPriorityProvider;
Steve Elliott46bb2a12020-03-17 11:04:09 -0400131 mNotificationManager = notificationManager;
132 mLauncherApps = launcherApps;
133 mShortcutManager = shortcutManager;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900134 }
135
Eliot Courtney4a96b362017-12-14 19:38:52 +0900136 public void setUpWithPresenter(NotificationPresenter presenter,
Kevina5ff1fa2018-08-21 16:35:48 -0700137 NotificationListContainer listContainer,
Jason Monk297c04e2018-08-23 17:16:59 -0400138 CheckSaveListener checkSave, OnSettingsClickListener onSettingsClick) {
Eliot Courtney09322282017-11-09 15:31:19 +0900139 mPresenter = presenter;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900140 mListContainer = listContainer;
Jason Monk297c04e2018-08-23 17:16:59 -0400141 mCheckSaveListener = checkSave;
142 mOnSettingsClickListener = onSettingsClick;
Eliot Courtney09322282017-11-09 15:31:19 +0900143 }
144
Gus Prevas21437b32018-12-05 10:36:13 -0500145 public void setNotificationActivityStarter(
146 NotificationActivityStarter notificationActivityStarter) {
147 mNotificationActivityStarter = notificationActivityStarter;
148 }
149
Ned Burnsf81c4c42019-01-07 14:10:43 -0500150 public void onDensityOrFontScaleChanged(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -0400151 setExposedGuts(entry.getGuts());
152 bindGuts(entry.getRow());
yoshiki iguchia85c2a02018-01-12 11:28:06 +0900153 }
154
Eliot Courtney47098cb2017-10-18 17:30:30 +0900155 /**
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400156 * Sends an intent to open the notification settings for a particular package and optional
Eliot Courtney47098cb2017-10-18 17:30:30 +0900157 * channel.
158 */
Evan Laird47dc4542019-04-24 15:10:52 -0400159 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
Eliot Courtney47098cb2017-10-18 17:30:30 +0900160 private void startAppNotificationSettingsActivity(String packageName, final int appUid,
Selim Cinek2627d722018-01-19 12:16:49 -0800161 final NotificationChannel channel, ExpandableNotificationRow row) {
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400162 final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
163 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
164 intent.putExtra(Settings.EXTRA_APP_UID, appUid);
Evan Laird47dc4542019-04-24 15:10:52 -0400165
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400166 if (channel != null) {
Evan Laird47dc4542019-04-24 15:10:52 -0400167 final Bundle args = new Bundle();
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400168 intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
Evan Laird47dc4542019-04-24 15:10:52 -0400169 args.putString(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
170 intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400171 }
172 mNotificationActivityStarter.startNotificationGutsIntent(intent, appUid, row);
173 }
174
175 private void startAppDetailsSettingsActivity(String packageName, final int appUid,
176 final NotificationChannel channel, ExpandableNotificationRow row) {
Julia Reynoldsf5c04872018-02-13 09:54:04 -0500177 final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
178 intent.setData(Uri.fromParts("package", packageName, null));
Eliot Courtney47098cb2017-10-18 17:30:30 +0900179 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
180 intent.putExtra(Settings.EXTRA_APP_UID, appUid);
181 if (channel != null) {
182 intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
183 }
Gus Prevas21437b32018-12-05 10:36:13 -0500184 mNotificationActivityStarter.startNotificationGutsIntent(intent, appUid, row);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900185 }
186
Julia Reynoldsb5867452018-02-28 16:31:35 -0500187 protected void startAppOpsSettingsActivity(String pkg, int uid, ArraySet<Integer> ops,
188 ExpandableNotificationRow row) {
189 if (ops.contains(OP_SYSTEM_ALERT_WINDOW)) {
190 if (ops.contains(OP_CAMERA) || ops.contains(OP_RECORD_AUDIO)) {
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400191 startAppDetailsSettingsActivity(pkg, uid, null, row);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500192 } else {
Bernardo Rufino8fefb942019-11-25 19:51:09 +0000193 Intent intent = new Intent(Settings.ACTION_MANAGE_APP_OVERLAY_PERMISSION);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500194 intent.setData(Uri.fromParts("package", pkg, null));
Gus Prevas21437b32018-12-05 10:36:13 -0500195 mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500196 }
197 } else if (ops.contains(OP_CAMERA) || ops.contains(OP_RECORD_AUDIO)) {
198 Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
199 intent.putExtra(Intent.EXTRA_PACKAGE_NAME, pkg);
Gus Prevas21437b32018-12-05 10:36:13 -0500200 mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500201 }
202 }
203
Evan Lairde55c6012019-03-13 12:54:37 -0400204 private boolean bindGuts(final ExpandableNotificationRow row) {
205 row.ensureGutsInflated();
Julia Reynolds4131e782018-08-22 09:45:24 -0400206 return bindGuts(row, mGutsMenuItem);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900207 }
208
TreeHugger Robotee8e1ae2019-01-18 19:26:40 +0000209 @VisibleForTesting
210 protected boolean bindGuts(final ExpandableNotificationRow row,
Eliot Courtney47098cb2017-10-18 17:30:30 +0900211 NotificationMenuRowPlugin.MenuItem item) {
Ned Burns1c2b85a42019-11-14 15:37:03 -0500212 StatusBarNotification sbn = row.getEntry().getSbn();
Rohan Shah524cf7b2018-03-15 14:40:02 -0700213
Eliot Courtney47098cb2017-10-18 17:30:30 +0900214 row.setGutsView(item);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900215 row.setTag(sbn.getPackageName());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700216 row.getGuts().setClosedListener((NotificationGuts g) -> {
Rohan Shah56eb0912018-05-10 21:49:04 -0700217 row.onGutsClosed();
Eliot Courtney47098cb2017-10-18 17:30:30 +0900218 if (!g.willBeRemoved() && !row.isRemoved()) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900219 mListContainer.onHeightChanged(
Eliot Courtney47098cb2017-10-18 17:30:30 +0900220 row, !mPresenter.isPresenterFullyCollapsed() /* needsAnimation */);
221 }
222 if (mNotificationGutsExposed == g) {
223 mNotificationGutsExposed = null;
224 mGutsMenuItem = null;
225 }
226 String key = sbn.getKey();
227 if (key.equals(mKeyToRemoveOnGutsClosed)) {
228 mKeyToRemoveOnGutsClosed = null;
Kevina5ff1fa2018-08-21 16:35:48 -0700229 if (mNotificationLifetimeFinishedCallback != null) {
230 mNotificationLifetimeFinishedCallback.onSafeToRemove(key);
231 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900232 }
233 });
234
235 View gutsView = item.getGutsView();
Julia Reynolds4131e782018-08-22 09:45:24 -0400236 try {
237 if (gutsView instanceof NotificationSnooze) {
238 initializeSnoozeView(row, (NotificationSnooze) gutsView);
239 } else if (gutsView instanceof AppOpsInfo) {
240 initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
241 } else if (gutsView instanceof NotificationInfo) {
Gus Prevas5a70a4e2018-11-26 17:16:05 -0500242 initializeNotificationInfo(row, (NotificationInfo) gutsView);
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500243 } else if (gutsView instanceof NotificationConversationInfo) {
244 initializeConversationNotificationInfo(
245 row, (NotificationConversationInfo) gutsView);
Julia Reynolds4131e782018-08-22 09:45:24 -0400246 }
247 return true;
248 } catch (Exception e) {
249 Log.e(TAG, "error binding guts", e);
250 return false;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900251 }
Rohan Shah524cf7b2018-03-15 14:40:02 -0700252 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900253
Rohan Shah524cf7b2018-03-15 14:40:02 -0700254 /**
255 * Sets up the {@link NotificationSnooze} inside the notification row's guts.
256 *
257 * @param row view to set up the guts for
258 * @param notificationSnoozeView view to set up/bind within {@code row}
259 */
260 private void initializeSnoozeView(
261 final ExpandableNotificationRow row,
262 NotificationSnooze notificationSnoozeView) {
263 NotificationGuts guts = row.getGuts();
Ned Burns1c2b85a42019-11-14 15:37:03 -0500264 StatusBarNotification sbn = row.getEntry().getSbn();
Rohan Shah524cf7b2018-03-15 14:40:02 -0700265
266 notificationSnoozeView.setSnoozeListener(mListContainer.getSwipeActionHelper());
267 notificationSnoozeView.setStatusBarNotification(sbn);
Ned Burns47c98f12019-09-06 17:12:07 -0400268 notificationSnoozeView.setSnoozeOptions(row.getEntry().getSnoozeCriteria());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700269 guts.setHeightChangedListener((NotificationGuts g) -> {
270 mListContainer.onHeightChanged(row, row.isShown() /* needsAnimation */);
271 });
272 }
273
274 /**
275 * Sets up the {@link AppOpsInfo} inside the notification row's guts.
276 *
277 * @param row view to set up the guts for
278 * @param appOpsInfoView view to set up/bind within {@code row}
279 */
280 private void initializeAppOpsInfo(
281 final ExpandableNotificationRow row,
282 AppOpsInfo appOpsInfoView) {
283 NotificationGuts guts = row.getGuts();
Ned Burns1c2b85a42019-11-14 15:37:03 -0500284 StatusBarNotification sbn = row.getEntry().getSbn();
Rohan Shah524cf7b2018-03-15 14:40:02 -0700285 UserHandle userHandle = sbn.getUser();
286 PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
287 userHandle.getIdentifier());
288
289 AppOpsInfo.OnSettingsClickListener onSettingsClick =
290 (View v, String pkg, int uid, ArraySet<Integer> ops) -> {
291 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_OPS_GUTS_SETTINGS);
292 guts.resetFalsingCheck();
293 startAppOpsSettingsActivity(pkg, uid, ops, row);
294 };
295 if (!row.getEntry().mActiveAppOps.isEmpty()) {
296 appOpsInfoView.bindGuts(pmUser, onSettingsClick, sbn, row.getEntry().mActiveAppOps);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500297 }
Rohan Shah524cf7b2018-03-15 14:40:02 -0700298 }
Julia Reynoldsb5867452018-02-28 16:31:35 -0500299
Rohan Shah524cf7b2018-03-15 14:40:02 -0700300 /**
301 * Sets up the {@link NotificationInfo} inside the notification row's guts.
Rohan Shah524cf7b2018-03-15 14:40:02 -0700302 * @param row view to set up the guts for
303 * @param notificationInfoView view to set up/bind within {@code row}
304 */
305 @VisibleForTesting
306 void initializeNotificationInfo(
307 final ExpandableNotificationRow row,
Gus Prevas5a70a4e2018-11-26 17:16:05 -0500308 NotificationInfo notificationInfoView) throws Exception {
Rohan Shah524cf7b2018-03-15 14:40:02 -0700309 NotificationGuts guts = row.getGuts();
Ned Burns1c2b85a42019-11-14 15:37:03 -0500310 StatusBarNotification sbn = row.getEntry().getSbn();
Rohan Shah524cf7b2018-03-15 14:40:02 -0700311 String packageName = sbn.getPackageName();
312 // Settings link is only valid for notifications that specify a non-system user
313 NotificationInfo.OnSettingsClickListener onSettingsClick = null;
314 UserHandle userHandle = sbn.getUser();
315 PackageManager pmUser = StatusBar.getPackageManagerForUser(
316 mContext, userHandle.getIdentifier());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700317 final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick =
318 (View v, Intent intent) -> {
319 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900320 guts.resetFalsingCheck();
Gus Prevas21437b32018-12-05 10:36:13 -0500321 mNotificationActivityStarter.startNotificationGutsIntent(intent, sbn.getUid(),
322 row);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900323 };
Eliot Courtney47098cb2017-10-18 17:30:30 +0900324
Rohan Shah524cf7b2018-03-15 14:40:02 -0700325 if (!userHandle.equals(UserHandle.ALL)
326 || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
327 onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
328 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
329 guts.resetFalsingCheck();
Jason Monk297c04e2018-08-23 17:16:59 -0400330 mOnSettingsClickListener.onSettingsClick(sbn.getKey());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700331 startAppNotificationSettingsActivity(packageName, appUid, channel, row);
332 };
333 }
334
Julia Reynolds4131e782018-08-22 09:45:24 -0400335 notificationInfoView.bindNotification(
336 pmUser,
Steve Elliott46bb2a12020-03-17 11:04:09 -0400337 mNotificationManager,
Ned Burns9512e0c2019-05-30 19:36:04 -0400338 mVisualStabilityManager,
Julia Reynolds4131e782018-08-22 09:45:24 -0400339 packageName,
Ned Burns296aec162019-09-04 17:30:59 -0400340 row.getEntry().getChannel(),
Evan Laird47dc4542019-04-24 15:10:52 -0400341 row.getUniqueChannels(),
Mady Mellor53162c12019-10-22 17:12:59 -0700342 row.getEntry(),
Julia Reynolds4131e782018-08-22 09:45:24 -0400343 onSettingsClick,
344 onAppSettingsClick,
Jason Monk297c04e2018-08-23 17:16:59 -0400345 mDeviceProvisionedController.isDeviceProvisioned(),
Julia Reynolds4131e782018-08-22 09:45:24 -0400346 row.getIsNonblockable(),
Beverlye558f1d2020-01-07 16:28:58 -0500347 mHighPriorityProvider.isHighPriority(row.getEntry()));
Rohan Shah524cf7b2018-03-15 14:40:02 -0700348 }
349
350 /**
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500351 * Sets up the {@link NotificationConversationInfo} inside the notification row's guts.
352 * @param row view to set up the guts for
353 * @param notificationInfoView view to set up/bind within {@code row}
354 */
355 @VisibleForTesting
356 void initializeConversationNotificationInfo(
357 final ExpandableNotificationRow row,
358 NotificationConversationInfo notificationInfoView) throws Exception {
359 NotificationGuts guts = row.getGuts();
360 StatusBarNotification sbn = row.getEntry().getSbn();
361 String packageName = sbn.getPackageName();
362 // Settings link is only valid for notifications that specify a non-system user
363 NotificationConversationInfo.OnSettingsClickListener onSettingsClick = null;
364 UserHandle userHandle = sbn.getUser();
365 PackageManager pmUser = StatusBar.getPackageManagerForUser(
366 mContext, userHandle.getIdentifier());
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500367 final NotificationConversationInfo.OnAppSettingsClickListener onAppSettingsClick =
368 (View v, Intent intent) -> {
369 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
370 guts.resetFalsingCheck();
371 mNotificationActivityStarter.startNotificationGutsIntent(intent, sbn.getUid(),
372 row);
373 };
374
375 final NotificationConversationInfo.OnSnoozeClickListener onSnoozeClickListener =
376 (View v, int hours) -> {
377 mListContainer.getSwipeActionHelper().snooze(sbn, hours);
378 };
379
380 if (!userHandle.equals(UserHandle.ALL)
381 || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
382 onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
383 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
384 guts.resetFalsingCheck();
385 mOnSettingsClickListener.onSettingsClick(sbn.getKey());
386 startAppNotificationSettingsActivity(packageName, appUid, channel, row);
Julia Reynoldse6fed502020-02-07 14:41:02 -0500387 notificationInfoView.closeControls(v, false);
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500388 };
389 }
Julia Reynolds01c9da42020-02-14 14:17:58 -0500390 ConversationIconFactory iconFactoryLoader = new ConversationIconFactory(mContext,
Steve Elliott46bb2a12020-03-17 11:04:09 -0400391 mLauncherApps, pmUser, IconDrawableFactory.newInstance(mContext, false),
Julia Reynolds01c9da42020-02-14 14:17:58 -0500392 mContext.getResources().getDimensionPixelSize(
393 R.dimen.notification_guts_conversation_icon_size));
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500394
395 notificationInfoView.bindNotification(
Steve Elliott46bb2a12020-03-17 11:04:09 -0400396 mShortcutManager,
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500397 pmUser,
Steve Elliott46bb2a12020-03-17 11:04:09 -0400398 mNotificationManager,
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500399 mVisualStabilityManager,
400 packageName,
401 row.getEntry().getChannel(),
402 row.getEntry(),
403 onSettingsClick,
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500404 onSnoozeClickListener,
Julia Reynolds01c9da42020-02-14 14:17:58 -0500405 iconFactoryLoader,
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500406 mDeviceProvisionedController.isDeviceProvisioned());
Julia Reynolds12ba4cf2020-01-10 16:01:38 -0500407 }
408
409 /**
Eliot Courtney47098cb2017-10-18 17:30:30 +0900410 * Closes guts or notification menus that might be visible and saves any changes.
411 *
412 * @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed.
413 * @param force true if guts should be closed regardless of state (used for snooze only).
414 * @param removeControls true if controls (e.g. info) should be closed.
415 * @param x if closed based on touch location, this is the x touch location.
416 * @param y if closed based on touch location, this is the y touch location.
417 * @param resetMenu if any notification menus that might be revealed should be closed.
418 */
419 public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls,
420 int x, int y, boolean resetMenu) {
421 if (mNotificationGutsExposed != null) {
Selim Cinek56e6a322019-10-01 12:06:05 -0700422 mNotificationGutsExposed.removeCallbacks(mOpenRunnable);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900423 mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
424 }
425 if (resetMenu) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900426 mListContainer.resetExposedMenuView(false /* animate */, true /* force */);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900427 }
428 }
429
430 /**
431 * Returns the exposed NotificationGuts or null if none are exposed.
432 */
433 public NotificationGuts getExposedGuts() {
434 return mNotificationGutsExposed;
435 }
436
437 public void setExposedGuts(NotificationGuts guts) {
438 mNotificationGutsExposed = guts;
439 }
440
Jason Monk297c04e2018-08-23 17:16:59 -0400441 public ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
442 return this::openGuts;
443 }
444
Eliot Courtney47098cb2017-10-18 17:30:30 +0900445 /**
Rohan Shah524cf7b2018-03-15 14:40:02 -0700446 * Opens guts on the given ExpandableNotificationRow {@code view}. This handles opening guts for
447 * the normal half-swipe and long-press use cases via a circular reveal. When the blocking
448 * helper needs to be shown on the row, this will skip the circular reveal.
Eliot Courtney47098cb2017-10-18 17:30:30 +0900449 *
Rohan Shah524cf7b2018-03-15 14:40:02 -0700450 * @param view ExpandableNotificationRow to open guts on
Eliot Courtney47098cb2017-10-18 17:30:30 +0900451 * @param x x coordinate of origin of circular reveal
452 * @param y y coordinate of origin of circular reveal
Rohan Shah524cf7b2018-03-15 14:40:02 -0700453 * @param menuItem MenuItem the guts should display
Eliot Courtney47098cb2017-10-18 17:30:30 +0900454 * @return true if guts was opened
455 */
Rohan Shah20790b82018-07-02 17:21:04 -0700456 public boolean openGuts(
Rohan Shah524cf7b2018-03-15 14:40:02 -0700457 View view,
458 int x,
459 int y,
460 NotificationMenuRowPlugin.MenuItem menuItem) {
Evan Laird03cf3502019-05-31 16:46:48 -0400461 if (menuItem.getGutsView() instanceof NotificationInfo) {
462 if (mStatusBarStateController instanceof StatusBarStateControllerImpl) {
463 ((StatusBarStateControllerImpl) mStatusBarStateController)
464 .setLeaveOpenOnKeyguardHide(true);
465 }
466
Dave Mankoff5fe07f72019-11-19 22:14:41 -0500467 Runnable r = () -> mMainHandler.post(
Evan Laird03cf3502019-05-31 16:46:48 -0400468 () -> openGutsInternal(view, x, y, menuItem));
469
Dave Mankoff4dd47fa2019-11-07 10:44:48 -0500470 mStatusBarLazy.get().executeRunnableDismissingKeyguard(
Evan Laird03cf3502019-05-31 16:46:48 -0400471 r,
472 null /* cancelAction */,
473 false /* dismissShade */,
474 true /* afterKeyguardGone */,
475 true /* deferred */);
476
477 return true;
478 }
479 return openGutsInternal(view, x, y, menuItem);
480 }
481
482 @VisibleForTesting
483 boolean openGutsInternal(
484 View view,
485 int x,
486 int y,
487 NotificationMenuRowPlugin.MenuItem menuItem) {
488
Rohan Shah524cf7b2018-03-15 14:40:02 -0700489 if (!(view instanceof ExpandableNotificationRow)) {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900490 return false;
491 }
492
Rohan Shah524cf7b2018-03-15 14:40:02 -0700493 if (view.getWindowToken() == null) {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900494 Log.e(TAG, "Trying to show notification guts, but not attached to window");
495 return false;
496 }
497
Rohan Shah524cf7b2018-03-15 14:40:02 -0700498 final ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Rohan Shah524cf7b2018-03-15 14:40:02 -0700499 view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900500 if (row.areGutsExposed()) {
501 closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
502 true /* removeControls */, -1 /* x */, -1 /* y */,
503 true /* resetMenu */);
504 return false;
505 }
Julia Reynolds4131e782018-08-22 09:45:24 -0400506
Evan Lairde55c6012019-03-13 12:54:37 -0400507 row.ensureGutsInflated();
Eliot Courtney47098cb2017-10-18 17:30:30 +0900508 NotificationGuts guts = row.getGuts();
Julia Reynolds4131e782018-08-22 09:45:24 -0400509 mNotificationGutsExposed = guts;
510 if (!bindGuts(row, menuItem)) {
511 // exception occurred trying to fill in all the data, bail.
512 return false;
513 }
514
Eliot Courtney47098cb2017-10-18 17:30:30 +0900515
516 // Assume we are a status_bar_notification_row
517 if (guts == null) {
518 // This view has no guts. Examples are the more card or the dismiss all view
519 return false;
520 }
521
Eliot Courtney47098cb2017-10-18 17:30:30 +0900522 // ensure that it's laid but not visible until actually laid out
523 guts.setVisibility(View.INVISIBLE);
524 // Post to ensure the the guts are properly laid out.
Selim Cinek56e6a322019-10-01 12:06:05 -0700525 mOpenRunnable = new Runnable() {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900526 @Override
527 public void run() {
528 if (row.getWindowToken() == null) {
Rohan Shahae5fdf02018-05-09 18:59:44 -0700529 Log.e(TAG, "Trying to show notification guts in post(), but not attached to "
Eliot Courtney47098cb2017-10-18 17:30:30 +0900530 + "window");
531 return;
532 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900533 guts.setVisibility(View.VISIBLE);
yoshiki iguchia85c2a02018-01-12 11:28:06 +0900534
Eliot Courtney47098cb2017-10-18 17:30:30 +0900535 final boolean needsFalsingProtection =
Jason Monk297c04e2018-08-23 17:16:59 -0400536 (mStatusBarStateController.getState() == StatusBarState.KEYGUARD &&
Eliot Courtney47098cb2017-10-18 17:30:30 +0900537 !mAccessibilityManager.isTouchExplorationEnabled());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700538
539 guts.openControls(
540 !row.isBlockingHelperShowing(),
541 x,
542 y,
543 needsFalsingProtection,
Rohan Shah56eb0912018-05-10 21:49:04 -0700544 row::onGutsOpened);
yoshiki iguchia85c2a02018-01-12 11:28:06 +0900545
Eliot Courtney47098cb2017-10-18 17:30:30 +0900546 row.closeRemoteInput();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900547 mListContainer.onHeightChanged(row, true /* needsAnimation */);
Rohan Shah524cf7b2018-03-15 14:40:02 -0700548 mGutsMenuItem = menuItem;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900549 }
Selim Cinek56e6a322019-10-01 12:06:05 -0700550 };
551 guts.post(mOpenRunnable);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900552 return true;
553 }
554
555 @Override
Kevina5ff1fa2018-08-21 16:35:48 -0700556 public void setCallback(NotificationSafeToRemoveCallback callback) {
557 mNotificationLifetimeFinishedCallback = callback;
558 }
559
560 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500561 public boolean shouldExtendLifetime(NotificationEntry entry) {
Kevina5ff1fa2018-08-21 16:35:48 -0700562 return entry != null
563 &&(mNotificationGutsExposed != null
Evan Laird94492852018-10-25 13:43:01 -0400564 && entry.getGuts() != null
565 && mNotificationGutsExposed == entry.getGuts()
Kevina5ff1fa2018-08-21 16:35:48 -0700566 && !mNotificationGutsExposed.isLeavebehind());
567 }
568
569 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500570 public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) {
Kevina5ff1fa2018-08-21 16:35:48 -0700571 if (shouldExtend) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400572 mKeyToRemoveOnGutsClosed = entry.getKey();
Kevina5ff1fa2018-08-21 16:35:48 -0700573 if (Log.isLoggable(TAG, Log.DEBUG)) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400574 Log.d(TAG, "Keeping notification because it's showing guts. " + entry.getKey());
Kevina5ff1fa2018-08-21 16:35:48 -0700575 }
576 } else {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400577 if (mKeyToRemoveOnGutsClosed != null
578 && mKeyToRemoveOnGutsClosed.equals(entry.getKey())) {
Kevina5ff1fa2018-08-21 16:35:48 -0700579 mKeyToRemoveOnGutsClosed = null;
580 if (Log.isLoggable(TAG, Log.DEBUG)) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400581 Log.d(TAG, "Notification that was kept for guts was updated. "
582 + entry.getKey());
Kevina5ff1fa2018-08-21 16:35:48 -0700583 }
584 }
585 }
586 }
587
588 @Override
Eliot Courtney47098cb2017-10-18 17:30:30 +0900589 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Eliot Courtney09322282017-11-09 15:31:19 +0900590 pw.println("NotificationGutsManager state:");
591 pw.print(" mKeyToRemoveOnGutsClosed: ");
Eliot Courtney47098cb2017-10-18 17:30:30 +0900592 pw.println(mKeyToRemoveOnGutsClosed);
593 }
Julia Reynolds84dc96b2017-11-14 09:51:01 -0500594
595 public interface OnSettingsClickListener {
Jason Monk297c04e2018-08-23 17:16:59 -0400596 public void onSettingsClick(String key);
Julia Reynolds84dc96b2017-11-14 09:51:01 -0500597 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900598}