blob: 8f7671a5dd96db577239e7ffd851c5a23ffac221 [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;
26import android.content.pm.PackageManager;
Julia Reynoldsf5c04872018-02-13 09:54:04 -050027import android.net.Uri;
Evan Laird47dc4542019-04-24 15:10:52 -040028import android.os.Bundle;
Eliot Courtney47098cb2017-10-18 17:30:30 +090029import android.os.ServiceManager;
30import android.os.UserHandle;
31import android.provider.Settings;
32import android.service.notification.StatusBarNotification;
33import android.util.ArraySet;
34import android.util.Log;
35import android.view.HapticFeedbackConstants;
36import android.view.View;
Eliot Courtney47098cb2017-10-18 17:30:30 +090037import android.view.accessibility.AccessibilityManager;
38
Kevina5ff1fa2018-08-21 16:35:48 -070039import com.android.internal.annotations.VisibleForTesting;
Eliot Courtney47098cb2017-10-18 17:30:30 +090040import com.android.internal.logging.MetricsLogger;
41import com.android.internal.logging.nano.MetricsProto;
42import com.android.systemui.Dependency;
43import com.android.systemui.Dumpable;
Evan Laird03cf3502019-05-31 16:46:48 -040044import com.android.systemui.SysUiServiceProvider;
Eliot Courtney47098cb2017-10-18 17:30:30 +090045import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
Beverly8fdb5332019-02-04 14:29:49 -050046import com.android.systemui.plugins.statusbar.StatusBarStateController;
Kevina5ff1fa2018-08-21 16:35:48 -070047import com.android.systemui.statusbar.NotificationLifetimeExtender;
Rohan Shah20790b82018-07-02 17:21:04 -070048import com.android.systemui.statusbar.NotificationLockscreenUserManager;
49import com.android.systemui.statusbar.NotificationPresenter;
Jason Monk297c04e2018-08-23 17:16:59 -040050import com.android.systemui.statusbar.StatusBarState;
Evan Laird03cf3502019-05-31 16:46:48 -040051import com.android.systemui.statusbar.StatusBarStateControllerImpl;
Gus Prevas21437b32018-12-05 10:36:13 -050052import com.android.systemui.statusbar.notification.NotificationActivityStarter;
Ned Burns9512e0c2019-05-30 19:36:04 -040053import com.android.systemui.statusbar.notification.VisualStabilityManager;
Ned Burnsf81c4c42019-01-07 14:10:43 -050054import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Jason Monk297c04e2018-08-23 17:16:59 -040055import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
Rohan Shah20790b82018-07-02 17:21:04 -070056import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
Eliot Courtney47098cb2017-10-18 17:30:30 +090057import com.android.systemui.statusbar.phone.StatusBar;
Jason Monk297c04e2018-08-23 17:16:59 -040058import com.android.systemui.statusbar.policy.DeviceProvisionedController;
Eliot Courtney47098cb2017-10-18 17:30:30 +090059
60import java.io.FileDescriptor;
61import java.io.PrintWriter;
Eliot Courtney47098cb2017-10-18 17:30:30 +090062
Jason Monk27d01a622018-12-10 15:57:09 -050063import javax.inject.Inject;
64import javax.inject.Singleton;
65
Eliot Courtney47098cb2017-10-18 17:30:30 +090066/**
67 * Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
68 * closing guts, and keeping track of the currently exposed notification guts.
69 */
Jason Monk27d01a622018-12-10 15:57:09 -050070@Singleton
Kevina5ff1fa2018-08-21 16:35:48 -070071public class NotificationGutsManager implements Dumpable, NotificationLifetimeExtender {
Eliot Courtney47098cb2017-10-18 17:30:30 +090072 private static final String TAG = "NotificationGutsManager";
73
74 // Must match constant in Settings. Used to highlight preferences when linking to Settings.
75 private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
76
77 private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
Eliot Courtney47098cb2017-10-18 17:30:30 +090078 private final Context mContext;
Ned Burns9512e0c2019-05-30 19:36:04 -040079 private final VisualStabilityManager mVisualStabilityManager;
Eliot Courtney47098cb2017-10-18 17:30:30 +090080 private final AccessibilityManager mAccessibilityManager;
Eliot Courtney6c313d32017-12-14 19:57:51 +090081
82 // Dependencies:
83 private final NotificationLockscreenUserManager mLockscreenUserManager =
84 Dependency.get(NotificationLockscreenUserManager.class);
Jason Monk297c04e2018-08-23 17:16:59 -040085 private final StatusBarStateController mStatusBarStateController =
86 Dependency.get(StatusBarStateController.class);
87 private final DeviceProvisionedController mDeviceProvisionedController =
88 Dependency.get(DeviceProvisionedController.class);
Eliot Courtney09322282017-11-09 15:31:19 +090089
Eliot Courtney47098cb2017-10-18 17:30:30 +090090 // which notification is currently being longpress-examined by the user
91 private NotificationGuts mNotificationGutsExposed;
92 private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
Kevina5ff1fa2018-08-21 16:35:48 -070093 private NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
Jason Monk297c04e2018-08-23 17:16:59 -040094 private NotificationPresenter mPresenter;
Gus Prevas21437b32018-12-05 10:36:13 -050095 private NotificationActivityStarter mNotificationActivityStarter;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090096 private NotificationListContainer mListContainer;
Jason Monk297c04e2018-08-23 17:16:59 -040097 private CheckSaveListener mCheckSaveListener;
Eliot Courtney09322282017-11-09 15:31:19 +090098 private OnSettingsClickListener mOnSettingsClickListener;
Kevina5ff1fa2018-08-21 16:35:48 -070099 @VisibleForTesting
100 protected String mKeyToRemoveOnGutsClosed;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900101
Evan Laird03cf3502019-05-31 16:46:48 -0400102 private StatusBar mStatusBar;
103
Jason Monk27d01a622018-12-10 15:57:09 -0500104 @Inject
Ned Burns9512e0c2019-05-30 19:36:04 -0400105 public NotificationGutsManager(
106 Context context,
107 VisualStabilityManager visualStabilityManager) {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900108 mContext = context;
Ned Burns9512e0c2019-05-30 19:36:04 -0400109 mVisualStabilityManager = visualStabilityManager;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900110 mAccessibilityManager = (AccessibilityManager)
111 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
112 }
113
Eliot Courtney4a96b362017-12-14 19:38:52 +0900114 public void setUpWithPresenter(NotificationPresenter presenter,
Kevina5ff1fa2018-08-21 16:35:48 -0700115 NotificationListContainer listContainer,
Jason Monk297c04e2018-08-23 17:16:59 -0400116 CheckSaveListener checkSave, OnSettingsClickListener onSettingsClick) {
Eliot Courtney09322282017-11-09 15:31:19 +0900117 mPresenter = presenter;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900118 mListContainer = listContainer;
Jason Monk297c04e2018-08-23 17:16:59 -0400119 mCheckSaveListener = checkSave;
120 mOnSettingsClickListener = onSettingsClick;
Evan Laird03cf3502019-05-31 16:46:48 -0400121 mStatusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
Eliot Courtney09322282017-11-09 15:31:19 +0900122 }
123
Gus Prevas21437b32018-12-05 10:36:13 -0500124 public void setNotificationActivityStarter(
125 NotificationActivityStarter notificationActivityStarter) {
126 mNotificationActivityStarter = notificationActivityStarter;
127 }
128
Ned Burnsf81c4c42019-01-07 14:10:43 -0500129 public void onDensityOrFontScaleChanged(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -0400130 setExposedGuts(entry.getGuts());
131 bindGuts(entry.getRow());
yoshiki iguchia85c2a02018-01-12 11:28:06 +0900132 }
133
Eliot Courtney47098cb2017-10-18 17:30:30 +0900134 /**
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400135 * Sends an intent to open the notification settings for a particular package and optional
Eliot Courtney47098cb2017-10-18 17:30:30 +0900136 * channel.
137 */
Evan Laird47dc4542019-04-24 15:10:52 -0400138 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
Eliot Courtney47098cb2017-10-18 17:30:30 +0900139 private void startAppNotificationSettingsActivity(String packageName, final int appUid,
Selim Cinek2627d722018-01-19 12:16:49 -0800140 final NotificationChannel channel, ExpandableNotificationRow row) {
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400141 final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
142 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
143 intent.putExtra(Settings.EXTRA_APP_UID, appUid);
Evan Laird47dc4542019-04-24 15:10:52 -0400144
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400145 if (channel != null) {
Evan Laird47dc4542019-04-24 15:10:52 -0400146 final Bundle args = new Bundle();
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400147 intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
Evan Laird47dc4542019-04-24 15:10:52 -0400148 args.putString(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
149 intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400150 }
151 mNotificationActivityStarter.startNotificationGutsIntent(intent, appUid, row);
152 }
153
154 private void startAppDetailsSettingsActivity(String packageName, final int appUid,
155 final NotificationChannel channel, ExpandableNotificationRow row) {
Julia Reynoldsf5c04872018-02-13 09:54:04 -0500156 final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
157 intent.setData(Uri.fromParts("package", packageName, null));
Eliot Courtney47098cb2017-10-18 17:30:30 +0900158 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
159 intent.putExtra(Settings.EXTRA_APP_UID, appUid);
160 if (channel != null) {
161 intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
162 }
Gus Prevas21437b32018-12-05 10:36:13 -0500163 mNotificationActivityStarter.startNotificationGutsIntent(intent, appUid, row);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900164 }
165
Julia Reynoldsb5867452018-02-28 16:31:35 -0500166 protected void startAppOpsSettingsActivity(String pkg, int uid, ArraySet<Integer> ops,
167 ExpandableNotificationRow row) {
168 if (ops.contains(OP_SYSTEM_ALERT_WINDOW)) {
169 if (ops.contains(OP_CAMERA) || ops.contains(OP_RECORD_AUDIO)) {
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400170 startAppDetailsSettingsActivity(pkg, uid, null, row);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500171 } else {
172 Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
173 intent.setData(Uri.fromParts("package", pkg, null));
Gus Prevas21437b32018-12-05 10:36:13 -0500174 mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500175 }
176 } else if (ops.contains(OP_CAMERA) || ops.contains(OP_RECORD_AUDIO)) {
177 Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
178 intent.putExtra(Intent.EXTRA_PACKAGE_NAME, pkg);
Gus Prevas21437b32018-12-05 10:36:13 -0500179 mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500180 }
181 }
182
Evan Lairde55c6012019-03-13 12:54:37 -0400183 private boolean bindGuts(final ExpandableNotificationRow row) {
184 row.ensureGutsInflated();
Julia Reynolds4131e782018-08-22 09:45:24 -0400185 return bindGuts(row, mGutsMenuItem);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900186 }
187
TreeHugger Robotee8e1ae2019-01-18 19:26:40 +0000188 @VisibleForTesting
189 protected boolean bindGuts(final ExpandableNotificationRow row,
Eliot Courtney47098cb2017-10-18 17:30:30 +0900190 NotificationMenuRowPlugin.MenuItem item) {
Rohan Shah524cf7b2018-03-15 14:40:02 -0700191 StatusBarNotification sbn = row.getStatusBarNotification();
192
Eliot Courtney47098cb2017-10-18 17:30:30 +0900193 row.setGutsView(item);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900194 row.setTag(sbn.getPackageName());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700195 row.getGuts().setClosedListener((NotificationGuts g) -> {
Rohan Shah56eb0912018-05-10 21:49:04 -0700196 row.onGutsClosed();
Eliot Courtney47098cb2017-10-18 17:30:30 +0900197 if (!g.willBeRemoved() && !row.isRemoved()) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900198 mListContainer.onHeightChanged(
Eliot Courtney47098cb2017-10-18 17:30:30 +0900199 row, !mPresenter.isPresenterFullyCollapsed() /* needsAnimation */);
200 }
201 if (mNotificationGutsExposed == g) {
202 mNotificationGutsExposed = null;
203 mGutsMenuItem = null;
204 }
205 String key = sbn.getKey();
206 if (key.equals(mKeyToRemoveOnGutsClosed)) {
207 mKeyToRemoveOnGutsClosed = null;
Kevina5ff1fa2018-08-21 16:35:48 -0700208 if (mNotificationLifetimeFinishedCallback != null) {
209 mNotificationLifetimeFinishedCallback.onSafeToRemove(key);
210 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900211 }
212 });
213
214 View gutsView = item.getGutsView();
Julia Reynolds4131e782018-08-22 09:45:24 -0400215 try {
216 if (gutsView instanceof NotificationSnooze) {
217 initializeSnoozeView(row, (NotificationSnooze) gutsView);
218 } else if (gutsView instanceof AppOpsInfo) {
219 initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
220 } else if (gutsView instanceof NotificationInfo) {
Gus Prevas5a70a4e2018-11-26 17:16:05 -0500221 initializeNotificationInfo(row, (NotificationInfo) gutsView);
Julia Reynolds4131e782018-08-22 09:45:24 -0400222 }
223 return true;
224 } catch (Exception e) {
225 Log.e(TAG, "error binding guts", e);
226 return false;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900227 }
Rohan Shah524cf7b2018-03-15 14:40:02 -0700228 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900229
Rohan Shah524cf7b2018-03-15 14:40:02 -0700230 /**
231 * Sets up the {@link NotificationSnooze} inside the notification row's guts.
232 *
233 * @param row view to set up the guts for
234 * @param notificationSnoozeView view to set up/bind within {@code row}
235 */
236 private void initializeSnoozeView(
237 final ExpandableNotificationRow row,
238 NotificationSnooze notificationSnoozeView) {
239 NotificationGuts guts = row.getGuts();
240 StatusBarNotification sbn = row.getStatusBarNotification();
241
242 notificationSnoozeView.setSnoozeListener(mListContainer.getSwipeActionHelper());
243 notificationSnoozeView.setStatusBarNotification(sbn);
244 notificationSnoozeView.setSnoozeOptions(row.getEntry().snoozeCriteria);
245 guts.setHeightChangedListener((NotificationGuts g) -> {
246 mListContainer.onHeightChanged(row, row.isShown() /* needsAnimation */);
247 });
248 }
249
250 /**
251 * Sets up the {@link AppOpsInfo} inside the notification row's guts.
252 *
253 * @param row view to set up the guts for
254 * @param appOpsInfoView view to set up/bind within {@code row}
255 */
256 private void initializeAppOpsInfo(
257 final ExpandableNotificationRow row,
258 AppOpsInfo appOpsInfoView) {
259 NotificationGuts guts = row.getGuts();
260 StatusBarNotification sbn = row.getStatusBarNotification();
261 UserHandle userHandle = sbn.getUser();
262 PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
263 userHandle.getIdentifier());
264
265 AppOpsInfo.OnSettingsClickListener onSettingsClick =
266 (View v, String pkg, int uid, ArraySet<Integer> ops) -> {
267 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_OPS_GUTS_SETTINGS);
268 guts.resetFalsingCheck();
269 startAppOpsSettingsActivity(pkg, uid, ops, row);
270 };
271 if (!row.getEntry().mActiveAppOps.isEmpty()) {
272 appOpsInfoView.bindGuts(pmUser, onSettingsClick, sbn, row.getEntry().mActiveAppOps);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500273 }
Rohan Shah524cf7b2018-03-15 14:40:02 -0700274 }
Julia Reynoldsb5867452018-02-28 16:31:35 -0500275
Rohan Shah524cf7b2018-03-15 14:40:02 -0700276 /**
277 * Sets up the {@link NotificationInfo} inside the notification row's guts.
Rohan Shah524cf7b2018-03-15 14:40:02 -0700278 * @param row view to set up the guts for
279 * @param notificationInfoView view to set up/bind within {@code row}
280 */
281 @VisibleForTesting
282 void initializeNotificationInfo(
283 final ExpandableNotificationRow row,
Gus Prevas5a70a4e2018-11-26 17:16:05 -0500284 NotificationInfo notificationInfoView) throws Exception {
Rohan Shah524cf7b2018-03-15 14:40:02 -0700285 NotificationGuts guts = row.getGuts();
286 StatusBarNotification sbn = row.getStatusBarNotification();
287 String packageName = sbn.getPackageName();
288 // Settings link is only valid for notifications that specify a non-system user
289 NotificationInfo.OnSettingsClickListener onSettingsClick = null;
290 UserHandle userHandle = sbn.getUser();
291 PackageManager pmUser = StatusBar.getPackageManagerForUser(
292 mContext, userHandle.getIdentifier());
293 INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
294 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
295 final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick =
296 (View v, Intent intent) -> {
297 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900298 guts.resetFalsingCheck();
Gus Prevas21437b32018-12-05 10:36:13 -0500299 mNotificationActivityStarter.startNotificationGutsIntent(intent, sbn.getUid(),
300 row);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900301 };
Rohan Shah524cf7b2018-03-15 14:40:02 -0700302 boolean isForBlockingHelper = row.isBlockingHelperShowing();
Eliot Courtney47098cb2017-10-18 17:30:30 +0900303
Rohan Shah524cf7b2018-03-15 14:40:02 -0700304 if (!userHandle.equals(UserHandle.ALL)
305 || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
306 onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
307 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
308 guts.resetFalsingCheck();
Jason Monk297c04e2018-08-23 17:16:59 -0400309 mOnSettingsClickListener.onSettingsClick(sbn.getKey());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700310 startAppNotificationSettingsActivity(packageName, appUid, channel, row);
311 };
312 }
313
Julia Reynolds4131e782018-08-22 09:45:24 -0400314 notificationInfoView.bindNotification(
315 pmUser,
316 iNotificationManager,
Ned Burns9512e0c2019-05-30 19:36:04 -0400317 mVisualStabilityManager,
Julia Reynolds4131e782018-08-22 09:45:24 -0400318 packageName,
319 row.getEntry().channel,
Evan Laird47dc4542019-04-24 15:10:52 -0400320 row.getUniqueChannels(),
Julia Reynolds4131e782018-08-22 09:45:24 -0400321 sbn,
322 mCheckSaveListener,
323 onSettingsClick,
324 onAppSettingsClick,
Jason Monk297c04e2018-08-23 17:16:59 -0400325 mDeviceProvisionedController.isDeviceProvisioned(),
Julia Reynolds4131e782018-08-22 09:45:24 -0400326 row.getIsNonblockable(),
327 isForBlockingHelper,
Gus Prevascaed15c2019-01-18 14:19:51 -0500328 row.getEntry().importance,
329 row.getEntry().isHighPriority());
Julia Reynolds4131e782018-08-22 09:45:24 -0400330
Rohan Shah524cf7b2018-03-15 14:40:02 -0700331 }
332
333 /**
Eliot Courtney47098cb2017-10-18 17:30:30 +0900334 * Closes guts or notification menus that might be visible and saves any changes.
335 *
336 * @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed.
337 * @param force true if guts should be closed regardless of state (used for snooze only).
338 * @param removeControls true if controls (e.g. info) should be closed.
339 * @param x if closed based on touch location, this is the x touch location.
340 * @param y if closed based on touch location, this is the y touch location.
341 * @param resetMenu if any notification menus that might be revealed should be closed.
342 */
343 public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls,
344 int x, int y, boolean resetMenu) {
345 if (mNotificationGutsExposed != null) {
346 mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
347 }
348 if (resetMenu) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900349 mListContainer.resetExposedMenuView(false /* animate */, true /* force */);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900350 }
351 }
352
353 /**
354 * Returns the exposed NotificationGuts or null if none are exposed.
355 */
356 public NotificationGuts getExposedGuts() {
357 return mNotificationGutsExposed;
358 }
359
360 public void setExposedGuts(NotificationGuts guts) {
361 mNotificationGutsExposed = guts;
362 }
363
Jason Monk297c04e2018-08-23 17:16:59 -0400364 public ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
365 return this::openGuts;
366 }
367
Eliot Courtney47098cb2017-10-18 17:30:30 +0900368 /**
Rohan Shah524cf7b2018-03-15 14:40:02 -0700369 * Opens guts on the given ExpandableNotificationRow {@code view}. This handles opening guts for
370 * the normal half-swipe and long-press use cases via a circular reveal. When the blocking
371 * helper needs to be shown on the row, this will skip the circular reveal.
Eliot Courtney47098cb2017-10-18 17:30:30 +0900372 *
Rohan Shah524cf7b2018-03-15 14:40:02 -0700373 * @param view ExpandableNotificationRow to open guts on
Eliot Courtney47098cb2017-10-18 17:30:30 +0900374 * @param x x coordinate of origin of circular reveal
375 * @param y y coordinate of origin of circular reveal
Rohan Shah524cf7b2018-03-15 14:40:02 -0700376 * @param menuItem MenuItem the guts should display
Eliot Courtney47098cb2017-10-18 17:30:30 +0900377 * @return true if guts was opened
378 */
Rohan Shah20790b82018-07-02 17:21:04 -0700379 public boolean openGuts(
Rohan Shah524cf7b2018-03-15 14:40:02 -0700380 View view,
381 int x,
382 int y,
383 NotificationMenuRowPlugin.MenuItem menuItem) {
Evan Laird03cf3502019-05-31 16:46:48 -0400384 if (menuItem.getGutsView() instanceof NotificationInfo) {
385 if (mStatusBarStateController instanceof StatusBarStateControllerImpl) {
386 ((StatusBarStateControllerImpl) mStatusBarStateController)
387 .setLeaveOpenOnKeyguardHide(true);
388 }
389
390 Runnable r = () -> Dependency.get(Dependency.MAIN_HANDLER).post(
391 () -> openGutsInternal(view, x, y, menuItem));
392
393 mStatusBar.executeRunnableDismissingKeyguard(
394 r,
395 null /* cancelAction */,
396 false /* dismissShade */,
397 true /* afterKeyguardGone */,
398 true /* deferred */);
399
400 return true;
401 }
402 return openGutsInternal(view, x, y, menuItem);
403 }
404
405 @VisibleForTesting
406 boolean openGutsInternal(
407 View view,
408 int x,
409 int y,
410 NotificationMenuRowPlugin.MenuItem menuItem) {
411
Rohan Shah524cf7b2018-03-15 14:40:02 -0700412 if (!(view instanceof ExpandableNotificationRow)) {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900413 return false;
414 }
415
Rohan Shah524cf7b2018-03-15 14:40:02 -0700416 if (view.getWindowToken() == null) {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900417 Log.e(TAG, "Trying to show notification guts, but not attached to window");
418 return false;
419 }
420
Rohan Shah524cf7b2018-03-15 14:40:02 -0700421 final ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Rohan Shah524cf7b2018-03-15 14:40:02 -0700422 view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900423 if (row.areGutsExposed()) {
424 closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
425 true /* removeControls */, -1 /* x */, -1 /* y */,
426 true /* resetMenu */);
427 return false;
428 }
Julia Reynolds4131e782018-08-22 09:45:24 -0400429
Evan Lairde55c6012019-03-13 12:54:37 -0400430 row.ensureGutsInflated();
Eliot Courtney47098cb2017-10-18 17:30:30 +0900431 NotificationGuts guts = row.getGuts();
Julia Reynolds4131e782018-08-22 09:45:24 -0400432 mNotificationGutsExposed = guts;
433 if (!bindGuts(row, menuItem)) {
434 // exception occurred trying to fill in all the data, bail.
435 return false;
436 }
437
Eliot Courtney47098cb2017-10-18 17:30:30 +0900438
439 // Assume we are a status_bar_notification_row
440 if (guts == null) {
441 // This view has no guts. Examples are the more card or the dismiss all view
442 return false;
443 }
444
Eliot Courtney47098cb2017-10-18 17:30:30 +0900445 // ensure that it's laid but not visible until actually laid out
446 guts.setVisibility(View.INVISIBLE);
447 // Post to ensure the the guts are properly laid out.
448 guts.post(new Runnable() {
449 @Override
450 public void run() {
451 if (row.getWindowToken() == null) {
Rohan Shahae5fdf02018-05-09 18:59:44 -0700452 Log.e(TAG, "Trying to show notification guts in post(), but not attached to "
Eliot Courtney47098cb2017-10-18 17:30:30 +0900453 + "window");
454 return;
455 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900456 guts.setVisibility(View.VISIBLE);
yoshiki iguchia85c2a02018-01-12 11:28:06 +0900457
Eliot Courtney47098cb2017-10-18 17:30:30 +0900458 final boolean needsFalsingProtection =
Jason Monk297c04e2018-08-23 17:16:59 -0400459 (mStatusBarStateController.getState() == StatusBarState.KEYGUARD &&
Eliot Courtney47098cb2017-10-18 17:30:30 +0900460 !mAccessibilityManager.isTouchExplorationEnabled());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700461
462 guts.openControls(
463 !row.isBlockingHelperShowing(),
464 x,
465 y,
466 needsFalsingProtection,
Rohan Shah56eb0912018-05-10 21:49:04 -0700467 row::onGutsOpened);
yoshiki iguchia85c2a02018-01-12 11:28:06 +0900468
Eliot Courtney47098cb2017-10-18 17:30:30 +0900469 row.closeRemoteInput();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900470 mListContainer.onHeightChanged(row, true /* needsAnimation */);
Rohan Shah524cf7b2018-03-15 14:40:02 -0700471 mGutsMenuItem = menuItem;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900472 }
473 });
474 return true;
475 }
476
477 @Override
Kevina5ff1fa2018-08-21 16:35:48 -0700478 public void setCallback(NotificationSafeToRemoveCallback callback) {
479 mNotificationLifetimeFinishedCallback = callback;
480 }
481
482 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500483 public boolean shouldExtendLifetime(NotificationEntry entry) {
Kevina5ff1fa2018-08-21 16:35:48 -0700484 return entry != null
485 &&(mNotificationGutsExposed != null
Evan Laird94492852018-10-25 13:43:01 -0400486 && entry.getGuts() != null
487 && mNotificationGutsExposed == entry.getGuts()
Kevina5ff1fa2018-08-21 16:35:48 -0700488 && !mNotificationGutsExposed.isLeavebehind());
489 }
490
491 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500492 public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) {
Kevina5ff1fa2018-08-21 16:35:48 -0700493 if (shouldExtend) {
494 mKeyToRemoveOnGutsClosed = entry.key;
495 if (Log.isLoggable(TAG, Log.DEBUG)) {
496 Log.d(TAG, "Keeping notification because it's showing guts. " + entry.key);
497 }
498 } else {
499 if (mKeyToRemoveOnGutsClosed != null && mKeyToRemoveOnGutsClosed.equals(entry.key)) {
500 mKeyToRemoveOnGutsClosed = null;
501 if (Log.isLoggable(TAG, Log.DEBUG)) {
502 Log.d(TAG, "Notification that was kept for guts was updated. " + entry.key);
503 }
504 }
505 }
506 }
507
508 @Override
Eliot Courtney47098cb2017-10-18 17:30:30 +0900509 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Eliot Courtney09322282017-11-09 15:31:19 +0900510 pw.println("NotificationGutsManager state:");
511 pw.print(" mKeyToRemoveOnGutsClosed: ");
Eliot Courtney47098cb2017-10-18 17:30:30 +0900512 pw.println(mKeyToRemoveOnGutsClosed);
513 }
Julia Reynolds84dc96b2017-11-14 09:51:01 -0500514
515 public interface OnSettingsClickListener {
Jason Monk297c04e2018-08-23 17:16:59 -0400516 public void onSettingsClick(String key);
Julia Reynolds84dc96b2017-11-14 09:51:01 -0500517 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900518}