blob: 719ec3215d4274ff4eaed5e5753b1817397df016 [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;
Selim Cinek300dcfb2019-10-01 12:06:05 -0700103 private Runnable mOpenRunnable;
Evan Laird03cf3502019-05-31 16:46:48 -0400104
Jason Monk27d01a622018-12-10 15:57:09 -0500105 @Inject
Ned Burns9512e0c2019-05-30 19:36:04 -0400106 public NotificationGutsManager(
107 Context context,
108 VisualStabilityManager visualStabilityManager) {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900109 mContext = context;
Ned Burns9512e0c2019-05-30 19:36:04 -0400110 mVisualStabilityManager = visualStabilityManager;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900111 mAccessibilityManager = (AccessibilityManager)
112 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
113 }
114
Eliot Courtney4a96b362017-12-14 19:38:52 +0900115 public void setUpWithPresenter(NotificationPresenter presenter,
Kevina5ff1fa2018-08-21 16:35:48 -0700116 NotificationListContainer listContainer,
Jason Monk297c04e2018-08-23 17:16:59 -0400117 CheckSaveListener checkSave, OnSettingsClickListener onSettingsClick) {
Eliot Courtney09322282017-11-09 15:31:19 +0900118 mPresenter = presenter;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900119 mListContainer = listContainer;
Jason Monk297c04e2018-08-23 17:16:59 -0400120 mCheckSaveListener = checkSave;
121 mOnSettingsClickListener = onSettingsClick;
Evan Laird03cf3502019-05-31 16:46:48 -0400122 mStatusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
Eliot Courtney09322282017-11-09 15:31:19 +0900123 }
124
Gus Prevas21437b32018-12-05 10:36:13 -0500125 public void setNotificationActivityStarter(
126 NotificationActivityStarter notificationActivityStarter) {
127 mNotificationActivityStarter = notificationActivityStarter;
128 }
129
Ned Burnsf81c4c42019-01-07 14:10:43 -0500130 public void onDensityOrFontScaleChanged(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -0400131 setExposedGuts(entry.getGuts());
132 bindGuts(entry.getRow());
yoshiki iguchia85c2a02018-01-12 11:28:06 +0900133 }
134
Eliot Courtney47098cb2017-10-18 17:30:30 +0900135 /**
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400136 * Sends an intent to open the notification settings for a particular package and optional
Eliot Courtney47098cb2017-10-18 17:30:30 +0900137 * channel.
138 */
Evan Laird47dc4542019-04-24 15:10:52 -0400139 public static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
Eliot Courtney47098cb2017-10-18 17:30:30 +0900140 private void startAppNotificationSettingsActivity(String packageName, final int appUid,
Selim Cinek2627d722018-01-19 12:16:49 -0800141 final NotificationChannel channel, ExpandableNotificationRow row) {
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400142 final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
143 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
144 intent.putExtra(Settings.EXTRA_APP_UID, appUid);
Evan Laird47dc4542019-04-24 15:10:52 -0400145
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400146 if (channel != null) {
Evan Laird47dc4542019-04-24 15:10:52 -0400147 final Bundle args = new Bundle();
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400148 intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
Evan Laird47dc4542019-04-24 15:10:52 -0400149 args.putString(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
150 intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400151 }
152 mNotificationActivityStarter.startNotificationGutsIntent(intent, appUid, row);
153 }
154
155 private void startAppDetailsSettingsActivity(String packageName, final int appUid,
156 final NotificationChannel channel, ExpandableNotificationRow row) {
Julia Reynoldsf5c04872018-02-13 09:54:04 -0500157 final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
158 intent.setData(Uri.fromParts("package", packageName, null));
Eliot Courtney47098cb2017-10-18 17:30:30 +0900159 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
160 intent.putExtra(Settings.EXTRA_APP_UID, appUid);
161 if (channel != null) {
162 intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
163 }
Gus Prevas21437b32018-12-05 10:36:13 -0500164 mNotificationActivityStarter.startNotificationGutsIntent(intent, appUid, row);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900165 }
166
Julia Reynoldsb5867452018-02-28 16:31:35 -0500167 protected void startAppOpsSettingsActivity(String pkg, int uid, ArraySet<Integer> ops,
168 ExpandableNotificationRow row) {
169 if (ops.contains(OP_SYSTEM_ALERT_WINDOW)) {
170 if (ops.contains(OP_CAMERA) || ops.contains(OP_RECORD_AUDIO)) {
Nadia Benbernou4a99e932019-03-13 14:47:47 -0400171 startAppDetailsSettingsActivity(pkg, uid, null, row);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500172 } else {
173 Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
174 intent.setData(Uri.fromParts("package", pkg, null));
Gus Prevas21437b32018-12-05 10:36:13 -0500175 mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500176 }
177 } else if (ops.contains(OP_CAMERA) || ops.contains(OP_RECORD_AUDIO)) {
178 Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
179 intent.putExtra(Intent.EXTRA_PACKAGE_NAME, pkg);
Gus Prevas21437b32018-12-05 10:36:13 -0500180 mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500181 }
182 }
183
Evan Lairde55c6012019-03-13 12:54:37 -0400184 private boolean bindGuts(final ExpandableNotificationRow row) {
185 row.ensureGutsInflated();
Julia Reynolds4131e782018-08-22 09:45:24 -0400186 return bindGuts(row, mGutsMenuItem);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900187 }
188
TreeHugger Robotee8e1ae2019-01-18 19:26:40 +0000189 @VisibleForTesting
190 protected boolean bindGuts(final ExpandableNotificationRow row,
Eliot Courtney47098cb2017-10-18 17:30:30 +0900191 NotificationMenuRowPlugin.MenuItem item) {
Rohan Shah524cf7b2018-03-15 14:40:02 -0700192 StatusBarNotification sbn = row.getStatusBarNotification();
193
Eliot Courtney47098cb2017-10-18 17:30:30 +0900194 row.setGutsView(item);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900195 row.setTag(sbn.getPackageName());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700196 row.getGuts().setClosedListener((NotificationGuts g) -> {
Rohan Shah56eb0912018-05-10 21:49:04 -0700197 row.onGutsClosed();
Eliot Courtney47098cb2017-10-18 17:30:30 +0900198 if (!g.willBeRemoved() && !row.isRemoved()) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900199 mListContainer.onHeightChanged(
Eliot Courtney47098cb2017-10-18 17:30:30 +0900200 row, !mPresenter.isPresenterFullyCollapsed() /* needsAnimation */);
201 }
202 if (mNotificationGutsExposed == g) {
203 mNotificationGutsExposed = null;
204 mGutsMenuItem = null;
205 }
206 String key = sbn.getKey();
207 if (key.equals(mKeyToRemoveOnGutsClosed)) {
208 mKeyToRemoveOnGutsClosed = null;
Kevina5ff1fa2018-08-21 16:35:48 -0700209 if (mNotificationLifetimeFinishedCallback != null) {
210 mNotificationLifetimeFinishedCallback.onSafeToRemove(key);
211 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900212 }
213 });
214
215 View gutsView = item.getGutsView();
Julia Reynolds4131e782018-08-22 09:45:24 -0400216 try {
217 if (gutsView instanceof NotificationSnooze) {
218 initializeSnoozeView(row, (NotificationSnooze) gutsView);
219 } else if (gutsView instanceof AppOpsInfo) {
220 initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
221 } else if (gutsView instanceof NotificationInfo) {
Gus Prevas5a70a4e2018-11-26 17:16:05 -0500222 initializeNotificationInfo(row, (NotificationInfo) gutsView);
Julia Reynolds4131e782018-08-22 09:45:24 -0400223 }
224 return true;
225 } catch (Exception e) {
226 Log.e(TAG, "error binding guts", e);
227 return false;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900228 }
Rohan Shah524cf7b2018-03-15 14:40:02 -0700229 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900230
Rohan Shah524cf7b2018-03-15 14:40:02 -0700231 /**
232 * Sets up the {@link NotificationSnooze} inside the notification row's guts.
233 *
234 * @param row view to set up the guts for
235 * @param notificationSnoozeView view to set up/bind within {@code row}
236 */
237 private void initializeSnoozeView(
238 final ExpandableNotificationRow row,
239 NotificationSnooze notificationSnoozeView) {
240 NotificationGuts guts = row.getGuts();
241 StatusBarNotification sbn = row.getStatusBarNotification();
242
243 notificationSnoozeView.setSnoozeListener(mListContainer.getSwipeActionHelper());
244 notificationSnoozeView.setStatusBarNotification(sbn);
245 notificationSnoozeView.setSnoozeOptions(row.getEntry().snoozeCriteria);
246 guts.setHeightChangedListener((NotificationGuts g) -> {
247 mListContainer.onHeightChanged(row, row.isShown() /* needsAnimation */);
248 });
249 }
250
251 /**
252 * Sets up the {@link AppOpsInfo} inside the notification row's guts.
253 *
254 * @param row view to set up the guts for
255 * @param appOpsInfoView view to set up/bind within {@code row}
256 */
257 private void initializeAppOpsInfo(
258 final ExpandableNotificationRow row,
259 AppOpsInfo appOpsInfoView) {
260 NotificationGuts guts = row.getGuts();
261 StatusBarNotification sbn = row.getStatusBarNotification();
262 UserHandle userHandle = sbn.getUser();
263 PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
264 userHandle.getIdentifier());
265
266 AppOpsInfo.OnSettingsClickListener onSettingsClick =
267 (View v, String pkg, int uid, ArraySet<Integer> ops) -> {
268 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_OPS_GUTS_SETTINGS);
269 guts.resetFalsingCheck();
270 startAppOpsSettingsActivity(pkg, uid, ops, row);
271 };
272 if (!row.getEntry().mActiveAppOps.isEmpty()) {
273 appOpsInfoView.bindGuts(pmUser, onSettingsClick, sbn, row.getEntry().mActiveAppOps);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500274 }
Rohan Shah524cf7b2018-03-15 14:40:02 -0700275 }
Julia Reynoldsb5867452018-02-28 16:31:35 -0500276
Rohan Shah524cf7b2018-03-15 14:40:02 -0700277 /**
278 * Sets up the {@link NotificationInfo} inside the notification row's guts.
Rohan Shah524cf7b2018-03-15 14:40:02 -0700279 * @param row view to set up the guts for
280 * @param notificationInfoView view to set up/bind within {@code row}
281 */
282 @VisibleForTesting
283 void initializeNotificationInfo(
284 final ExpandableNotificationRow row,
Gus Prevas5a70a4e2018-11-26 17:16:05 -0500285 NotificationInfo notificationInfoView) throws Exception {
Rohan Shah524cf7b2018-03-15 14:40:02 -0700286 NotificationGuts guts = row.getGuts();
287 StatusBarNotification sbn = row.getStatusBarNotification();
288 String packageName = sbn.getPackageName();
289 // Settings link is only valid for notifications that specify a non-system user
290 NotificationInfo.OnSettingsClickListener onSettingsClick = null;
291 UserHandle userHandle = sbn.getUser();
292 PackageManager pmUser = StatusBar.getPackageManagerForUser(
293 mContext, userHandle.getIdentifier());
294 INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
295 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
296 final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick =
297 (View v, Intent intent) -> {
298 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900299 guts.resetFalsingCheck();
Gus Prevas21437b32018-12-05 10:36:13 -0500300 mNotificationActivityStarter.startNotificationGutsIntent(intent, sbn.getUid(),
301 row);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900302 };
Rohan Shah524cf7b2018-03-15 14:40:02 -0700303 boolean isForBlockingHelper = row.isBlockingHelperShowing();
Eliot Courtney47098cb2017-10-18 17:30:30 +0900304
Rohan Shah524cf7b2018-03-15 14:40:02 -0700305 if (!userHandle.equals(UserHandle.ALL)
306 || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
307 onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
308 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
309 guts.resetFalsingCheck();
Jason Monk297c04e2018-08-23 17:16:59 -0400310 mOnSettingsClickListener.onSettingsClick(sbn.getKey());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700311 startAppNotificationSettingsActivity(packageName, appUid, channel, row);
312 };
313 }
314
Julia Reynolds4131e782018-08-22 09:45:24 -0400315 notificationInfoView.bindNotification(
316 pmUser,
317 iNotificationManager,
Ned Burns9512e0c2019-05-30 19:36:04 -0400318 mVisualStabilityManager,
Julia Reynolds4131e782018-08-22 09:45:24 -0400319 packageName,
320 row.getEntry().channel,
Evan Laird47dc4542019-04-24 15:10:52 -0400321 row.getUniqueChannels(),
Julia Reynolds4131e782018-08-22 09:45:24 -0400322 sbn,
323 mCheckSaveListener,
324 onSettingsClick,
325 onAppSettingsClick,
Jason Monk297c04e2018-08-23 17:16:59 -0400326 mDeviceProvisionedController.isDeviceProvisioned(),
Julia Reynolds4131e782018-08-22 09:45:24 -0400327 row.getIsNonblockable(),
328 isForBlockingHelper,
Gus Prevascaed15c2019-01-18 14:19:51 -0500329 row.getEntry().importance,
330 row.getEntry().isHighPriority());
Julia Reynolds4131e782018-08-22 09:45:24 -0400331
Rohan Shah524cf7b2018-03-15 14:40:02 -0700332 }
333
334 /**
Eliot Courtney47098cb2017-10-18 17:30:30 +0900335 * Closes guts or notification menus that might be visible and saves any changes.
336 *
337 * @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed.
338 * @param force true if guts should be closed regardless of state (used for snooze only).
339 * @param removeControls true if controls (e.g. info) should be closed.
340 * @param x if closed based on touch location, this is the x touch location.
341 * @param y if closed based on touch location, this is the y touch location.
342 * @param resetMenu if any notification menus that might be revealed should be closed.
343 */
344 public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls,
345 int x, int y, boolean resetMenu) {
346 if (mNotificationGutsExposed != null) {
Selim Cinek300dcfb2019-10-01 12:06:05 -0700347 mNotificationGutsExposed.removeCallbacks(mOpenRunnable);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900348 mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
349 }
350 if (resetMenu) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900351 mListContainer.resetExposedMenuView(false /* animate */, true /* force */);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900352 }
353 }
354
355 /**
356 * Returns the exposed NotificationGuts or null if none are exposed.
357 */
358 public NotificationGuts getExposedGuts() {
359 return mNotificationGutsExposed;
360 }
361
362 public void setExposedGuts(NotificationGuts guts) {
363 mNotificationGutsExposed = guts;
364 }
365
Jason Monk297c04e2018-08-23 17:16:59 -0400366 public ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
367 return this::openGuts;
368 }
369
Eliot Courtney47098cb2017-10-18 17:30:30 +0900370 /**
Rohan Shah524cf7b2018-03-15 14:40:02 -0700371 * Opens guts on the given ExpandableNotificationRow {@code view}. This handles opening guts for
372 * the normal half-swipe and long-press use cases via a circular reveal. When the blocking
373 * helper needs to be shown on the row, this will skip the circular reveal.
Eliot Courtney47098cb2017-10-18 17:30:30 +0900374 *
Rohan Shah524cf7b2018-03-15 14:40:02 -0700375 * @param view ExpandableNotificationRow to open guts on
Eliot Courtney47098cb2017-10-18 17:30:30 +0900376 * @param x x coordinate of origin of circular reveal
377 * @param y y coordinate of origin of circular reveal
Rohan Shah524cf7b2018-03-15 14:40:02 -0700378 * @param menuItem MenuItem the guts should display
Eliot Courtney47098cb2017-10-18 17:30:30 +0900379 * @return true if guts was opened
380 */
Rohan Shah20790b82018-07-02 17:21:04 -0700381 public boolean openGuts(
Rohan Shah524cf7b2018-03-15 14:40:02 -0700382 View view,
383 int x,
384 int y,
385 NotificationMenuRowPlugin.MenuItem menuItem) {
Evan Laird03cf3502019-05-31 16:46:48 -0400386 if (menuItem.getGutsView() instanceof NotificationInfo) {
387 if (mStatusBarStateController instanceof StatusBarStateControllerImpl) {
388 ((StatusBarStateControllerImpl) mStatusBarStateController)
389 .setLeaveOpenOnKeyguardHide(true);
390 }
391
392 Runnable r = () -> Dependency.get(Dependency.MAIN_HANDLER).post(
393 () -> openGutsInternal(view, x, y, menuItem));
394
395 mStatusBar.executeRunnableDismissingKeyguard(
396 r,
397 null /* cancelAction */,
398 false /* dismissShade */,
399 true /* afterKeyguardGone */,
400 true /* deferred */);
401
402 return true;
403 }
404 return openGutsInternal(view, x, y, menuItem);
405 }
406
407 @VisibleForTesting
408 boolean openGutsInternal(
409 View view,
410 int x,
411 int y,
412 NotificationMenuRowPlugin.MenuItem menuItem) {
413
Rohan Shah524cf7b2018-03-15 14:40:02 -0700414 if (!(view instanceof ExpandableNotificationRow)) {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900415 return false;
416 }
417
Rohan Shah524cf7b2018-03-15 14:40:02 -0700418 if (view.getWindowToken() == null) {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900419 Log.e(TAG, "Trying to show notification guts, but not attached to window");
420 return false;
421 }
422
Rohan Shah524cf7b2018-03-15 14:40:02 -0700423 final ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Rohan Shah524cf7b2018-03-15 14:40:02 -0700424 view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900425 if (row.areGutsExposed()) {
426 closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
427 true /* removeControls */, -1 /* x */, -1 /* y */,
428 true /* resetMenu */);
429 return false;
430 }
Julia Reynolds4131e782018-08-22 09:45:24 -0400431
Evan Lairde55c6012019-03-13 12:54:37 -0400432 row.ensureGutsInflated();
Eliot Courtney47098cb2017-10-18 17:30:30 +0900433 NotificationGuts guts = row.getGuts();
Julia Reynolds4131e782018-08-22 09:45:24 -0400434 mNotificationGutsExposed = guts;
435 if (!bindGuts(row, menuItem)) {
436 // exception occurred trying to fill in all the data, bail.
437 return false;
438 }
439
Eliot Courtney47098cb2017-10-18 17:30:30 +0900440
441 // Assume we are a status_bar_notification_row
442 if (guts == null) {
443 // This view has no guts. Examples are the more card or the dismiss all view
444 return false;
445 }
446
Eliot Courtney47098cb2017-10-18 17:30:30 +0900447 // ensure that it's laid but not visible until actually laid out
448 guts.setVisibility(View.INVISIBLE);
449 // Post to ensure the the guts are properly laid out.
Selim Cinek300dcfb2019-10-01 12:06:05 -0700450 mOpenRunnable = new Runnable() {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900451 @Override
452 public void run() {
453 if (row.getWindowToken() == null) {
Rohan Shahae5fdf02018-05-09 18:59:44 -0700454 Log.e(TAG, "Trying to show notification guts in post(), but not attached to "
Eliot Courtney47098cb2017-10-18 17:30:30 +0900455 + "window");
456 return;
457 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900458 guts.setVisibility(View.VISIBLE);
yoshiki iguchia85c2a02018-01-12 11:28:06 +0900459
Eliot Courtney47098cb2017-10-18 17:30:30 +0900460 final boolean needsFalsingProtection =
Jason Monk297c04e2018-08-23 17:16:59 -0400461 (mStatusBarStateController.getState() == StatusBarState.KEYGUARD &&
Eliot Courtney47098cb2017-10-18 17:30:30 +0900462 !mAccessibilityManager.isTouchExplorationEnabled());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700463
464 guts.openControls(
465 !row.isBlockingHelperShowing(),
466 x,
467 y,
468 needsFalsingProtection,
Rohan Shah56eb0912018-05-10 21:49:04 -0700469 row::onGutsOpened);
yoshiki iguchia85c2a02018-01-12 11:28:06 +0900470
Eliot Courtney47098cb2017-10-18 17:30:30 +0900471 row.closeRemoteInput();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900472 mListContainer.onHeightChanged(row, true /* needsAnimation */);
Rohan Shah524cf7b2018-03-15 14:40:02 -0700473 mGutsMenuItem = menuItem;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900474 }
Selim Cinek300dcfb2019-10-01 12:06:05 -0700475 };
476 guts.post(mOpenRunnable);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900477 return true;
478 }
479
480 @Override
Kevina5ff1fa2018-08-21 16:35:48 -0700481 public void setCallback(NotificationSafeToRemoveCallback callback) {
482 mNotificationLifetimeFinishedCallback = callback;
483 }
484
485 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500486 public boolean shouldExtendLifetime(NotificationEntry entry) {
Kevina5ff1fa2018-08-21 16:35:48 -0700487 return entry != null
488 &&(mNotificationGutsExposed != null
Evan Laird94492852018-10-25 13:43:01 -0400489 && entry.getGuts() != null
490 && mNotificationGutsExposed == entry.getGuts()
Kevina5ff1fa2018-08-21 16:35:48 -0700491 && !mNotificationGutsExposed.isLeavebehind());
492 }
493
494 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500495 public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) {
Kevina5ff1fa2018-08-21 16:35:48 -0700496 if (shouldExtend) {
497 mKeyToRemoveOnGutsClosed = entry.key;
498 if (Log.isLoggable(TAG, Log.DEBUG)) {
499 Log.d(TAG, "Keeping notification because it's showing guts. " + entry.key);
500 }
501 } else {
502 if (mKeyToRemoveOnGutsClosed != null && mKeyToRemoveOnGutsClosed.equals(entry.key)) {
503 mKeyToRemoveOnGutsClosed = null;
504 if (Log.isLoggable(TAG, Log.DEBUG)) {
505 Log.d(TAG, "Notification that was kept for guts was updated. " + entry.key);
506 }
507 }
508 }
509 }
510
511 @Override
Eliot Courtney47098cb2017-10-18 17:30:30 +0900512 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Eliot Courtney09322282017-11-09 15:31:19 +0900513 pw.println("NotificationGutsManager state:");
514 pw.print(" mKeyToRemoveOnGutsClosed: ");
Eliot Courtney47098cb2017-10-18 17:30:30 +0900515 pw.println(mKeyToRemoveOnGutsClosed);
516 }
Julia Reynolds84dc96b2017-11-14 09:51:01 -0500517
518 public interface OnSettingsClickListener {
Jason Monk297c04e2018-08-23 17:16:59 -0400519 public void onSettingsClick(String key);
Julia Reynolds84dc96b2017-11-14 09:51:01 -0500520 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900521}