blob: cb1384cacec8d28d711a83e9925694cd1517c3a7 [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 Prevas5a70a4e2018-11-26 17:16:05 -050021import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
Gus Prevas894d9152018-11-12 13:51:40 -050022
Eliot Courtney47098cb2017-10-18 17:30:30 +090023import android.app.INotificationManager;
24import android.app.NotificationChannel;
25import android.content.Context;
26import android.content.Intent;
27import android.content.pm.PackageManager;
TreeHugger Robotee8e1ae2019-01-18 19:26:40 +000028import android.metrics.LogMaker;
Julia Reynoldsf5c04872018-02-13 09:54:04 -050029import android.net.Uri;
Eliot Courtney47098cb2017-10-18 17:30:30 +090030import android.os.ServiceManager;
31import android.os.UserHandle;
32import android.provider.Settings;
33import android.service.notification.StatusBarNotification;
34import android.util.ArraySet;
35import android.util.Log;
36import android.view.HapticFeedbackConstants;
37import android.view.View;
Eliot Courtney47098cb2017-10-18 17:30:30 +090038import android.view.accessibility.AccessibilityManager;
39
Kevina5ff1fa2018-08-21 16:35:48 -070040import com.android.internal.annotations.VisibleForTesting;
Eliot Courtney47098cb2017-10-18 17:30:30 +090041import com.android.internal.logging.MetricsLogger;
42import com.android.internal.logging.nano.MetricsProto;
43import com.android.systemui.Dependency;
44import com.android.systemui.Dumpable;
Eliot Courtney47098cb2017-10-18 17:30:30 +090045import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
Kevina5ff1fa2018-08-21 16:35:48 -070046import com.android.systemui.statusbar.NotificationLifetimeExtender;
Rohan Shah20790b82018-07-02 17:21:04 -070047import com.android.systemui.statusbar.NotificationLockscreenUserManager;
48import com.android.systemui.statusbar.NotificationPresenter;
Jason Monk297c04e2018-08-23 17:16:59 -040049import com.android.systemui.statusbar.StatusBarState;
50import com.android.systemui.statusbar.StatusBarStateController;
Gus Prevas21437b32018-12-05 10:36:13 -050051import com.android.systemui.statusbar.notification.NotificationActivityStarter;
Ned Burnsf81c4c42019-01-07 14:10:43 -050052import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Jason Monk297c04e2018-08-23 17:16:59 -040053import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
Rohan Shah20790b82018-07-02 17:21:04 -070054import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
Eliot Courtney47098cb2017-10-18 17:30:30 +090055import com.android.systemui.statusbar.phone.StatusBar;
Jason Monk297c04e2018-08-23 17:16:59 -040056import com.android.systemui.statusbar.policy.DeviceProvisionedController;
Eliot Courtney47098cb2017-10-18 17:30:30 +090057
58import java.io.FileDescriptor;
59import java.io.PrintWriter;
Eliot Courtney47098cb2017-10-18 17:30:30 +090060
Jason Monk27d01a622018-12-10 15:57:09 -050061import javax.inject.Inject;
62import javax.inject.Singleton;
63
Eliot Courtney47098cb2017-10-18 17:30:30 +090064/**
65 * Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
66 * closing guts, and keeping track of the currently exposed notification guts.
67 */
Jason Monk27d01a622018-12-10 15:57:09 -050068@Singleton
Kevina5ff1fa2018-08-21 16:35:48 -070069public class NotificationGutsManager implements Dumpable, NotificationLifetimeExtender {
Eliot Courtney47098cb2017-10-18 17:30:30 +090070 private static final String TAG = "NotificationGutsManager";
71
72 // Must match constant in Settings. Used to highlight preferences when linking to Settings.
73 private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
74
75 private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
Eliot Courtney47098cb2017-10-18 17:30:30 +090076 private final Context mContext;
77 private final AccessibilityManager mAccessibilityManager;
Eliot Courtney6c313d32017-12-14 19:57:51 +090078
79 // Dependencies:
80 private final NotificationLockscreenUserManager mLockscreenUserManager =
81 Dependency.get(NotificationLockscreenUserManager.class);
Jason Monk297c04e2018-08-23 17:16:59 -040082 private final StatusBarStateController mStatusBarStateController =
83 Dependency.get(StatusBarStateController.class);
84 private final DeviceProvisionedController mDeviceProvisionedController =
85 Dependency.get(DeviceProvisionedController.class);
Eliot Courtney09322282017-11-09 15:31:19 +090086
Eliot Courtney47098cb2017-10-18 17:30:30 +090087 // which notification is currently being longpress-examined by the user
88 private NotificationGuts mNotificationGutsExposed;
89 private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
Kevina5ff1fa2018-08-21 16:35:48 -070090 private NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
Jason Monk297c04e2018-08-23 17:16:59 -040091 private NotificationPresenter mPresenter;
Gus Prevas21437b32018-12-05 10:36:13 -050092 private NotificationActivityStarter mNotificationActivityStarter;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090093 private NotificationListContainer mListContainer;
Jason Monk297c04e2018-08-23 17:16:59 -040094 private CheckSaveListener mCheckSaveListener;
Eliot Courtney09322282017-11-09 15:31:19 +090095 private OnSettingsClickListener mOnSettingsClickListener;
Kevina5ff1fa2018-08-21 16:35:48 -070096 @VisibleForTesting
97 protected String mKeyToRemoveOnGutsClosed;
Eliot Courtney47098cb2017-10-18 17:30:30 +090098
Jason Monk27d01a622018-12-10 15:57:09 -050099 @Inject
Eliot Courtney6c313d32017-12-14 19:57:51 +0900100 public NotificationGutsManager(Context context) {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900101 mContext = context;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900102 mAccessibilityManager = (AccessibilityManager)
103 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
104 }
105
Eliot Courtney4a96b362017-12-14 19:38:52 +0900106 public void setUpWithPresenter(NotificationPresenter presenter,
Kevina5ff1fa2018-08-21 16:35:48 -0700107 NotificationListContainer listContainer,
Jason Monk297c04e2018-08-23 17:16:59 -0400108 CheckSaveListener checkSave, OnSettingsClickListener onSettingsClick) {
Eliot Courtney09322282017-11-09 15:31:19 +0900109 mPresenter = presenter;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900110 mListContainer = listContainer;
Jason Monk297c04e2018-08-23 17:16:59 -0400111 mCheckSaveListener = checkSave;
112 mOnSettingsClickListener = onSettingsClick;
Eliot Courtney09322282017-11-09 15:31:19 +0900113 }
114
Gus Prevas21437b32018-12-05 10:36:13 -0500115 public void setNotificationActivityStarter(
116 NotificationActivityStarter notificationActivityStarter) {
117 mNotificationActivityStarter = notificationActivityStarter;
118 }
119
Ned Burnsf81c4c42019-01-07 14:10:43 -0500120 public void onDensityOrFontScaleChanged(NotificationEntry entry) {
Evan Laird94492852018-10-25 13:43:01 -0400121 setExposedGuts(entry.getGuts());
122 bindGuts(entry.getRow());
yoshiki iguchia85c2a02018-01-12 11:28:06 +0900123 }
124
Eliot Courtney47098cb2017-10-18 17:30:30 +0900125 /**
Julia Reynoldsf5c04872018-02-13 09:54:04 -0500126 * Sends an intent to open the app settings for a particular package and optional
Eliot Courtney47098cb2017-10-18 17:30:30 +0900127 * channel.
128 */
129 private void startAppNotificationSettingsActivity(String packageName, final int appUid,
Selim Cinek2627d722018-01-19 12:16:49 -0800130 final NotificationChannel channel, ExpandableNotificationRow row) {
Julia Reynoldsf5c04872018-02-13 09:54:04 -0500131 final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
132 intent.setData(Uri.fromParts("package", packageName, null));
Eliot Courtney47098cb2017-10-18 17:30:30 +0900133 intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
134 intent.putExtra(Settings.EXTRA_APP_UID, appUid);
135 if (channel != null) {
136 intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
137 }
Gus Prevas21437b32018-12-05 10:36:13 -0500138 mNotificationActivityStarter.startNotificationGutsIntent(intent, appUid, row);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900139 }
140
Julia Reynoldsb5867452018-02-28 16:31:35 -0500141 protected void startAppOpsSettingsActivity(String pkg, int uid, ArraySet<Integer> ops,
142 ExpandableNotificationRow row) {
143 if (ops.contains(OP_SYSTEM_ALERT_WINDOW)) {
144 if (ops.contains(OP_CAMERA) || ops.contains(OP_RECORD_AUDIO)) {
145 startAppNotificationSettingsActivity(pkg, uid, null, row);
146 } else {
147 Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
148 intent.setData(Uri.fromParts("package", pkg, null));
Gus Prevas21437b32018-12-05 10:36:13 -0500149 mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500150 }
151 } else if (ops.contains(OP_CAMERA) || ops.contains(OP_RECORD_AUDIO)) {
152 Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
153 intent.putExtra(Intent.EXTRA_PACKAGE_NAME, pkg);
Gus Prevas21437b32018-12-05 10:36:13 -0500154 mNotificationActivityStarter.startNotificationGutsIntent(intent, uid, row);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500155 }
156 }
157
Julia Reynolds4131e782018-08-22 09:45:24 -0400158 public boolean bindGuts(final ExpandableNotificationRow row) {
159 row.inflateGuts();
160 return bindGuts(row, mGutsMenuItem);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900161 }
162
TreeHugger Robotee8e1ae2019-01-18 19:26:40 +0000163 @VisibleForTesting
164 protected boolean bindGuts(final ExpandableNotificationRow row,
Eliot Courtney47098cb2017-10-18 17:30:30 +0900165 NotificationMenuRowPlugin.MenuItem item) {
Rohan Shah524cf7b2018-03-15 14:40:02 -0700166 StatusBarNotification sbn = row.getStatusBarNotification();
167
Eliot Courtney47098cb2017-10-18 17:30:30 +0900168 row.setGutsView(item);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900169 row.setTag(sbn.getPackageName());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700170 row.getGuts().setClosedListener((NotificationGuts g) -> {
Rohan Shah56eb0912018-05-10 21:49:04 -0700171 row.onGutsClosed();
Eliot Courtney47098cb2017-10-18 17:30:30 +0900172 if (!g.willBeRemoved() && !row.isRemoved()) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900173 mListContainer.onHeightChanged(
Eliot Courtney47098cb2017-10-18 17:30:30 +0900174 row, !mPresenter.isPresenterFullyCollapsed() /* needsAnimation */);
175 }
176 if (mNotificationGutsExposed == g) {
177 mNotificationGutsExposed = null;
178 mGutsMenuItem = null;
179 }
180 String key = sbn.getKey();
181 if (key.equals(mKeyToRemoveOnGutsClosed)) {
182 mKeyToRemoveOnGutsClosed = null;
Kevina5ff1fa2018-08-21 16:35:48 -0700183 if (mNotificationLifetimeFinishedCallback != null) {
184 mNotificationLifetimeFinishedCallback.onSafeToRemove(key);
185 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900186 }
187 });
188
189 View gutsView = item.getGutsView();
Julia Reynolds4131e782018-08-22 09:45:24 -0400190 try {
191 if (gutsView instanceof NotificationSnooze) {
192 initializeSnoozeView(row, (NotificationSnooze) gutsView);
193 } else if (gutsView instanceof AppOpsInfo) {
194 initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
195 } else if (gutsView instanceof NotificationInfo) {
Gus Prevas5a70a4e2018-11-26 17:16:05 -0500196 initializeNotificationInfo(row, (NotificationInfo) gutsView);
Julia Reynolds4131e782018-08-22 09:45:24 -0400197 }
198 return true;
199 } catch (Exception e) {
200 Log.e(TAG, "error binding guts", e);
201 return false;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900202 }
Rohan Shah524cf7b2018-03-15 14:40:02 -0700203 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900204
Rohan Shah524cf7b2018-03-15 14:40:02 -0700205 /**
206 * Sets up the {@link NotificationSnooze} inside the notification row's guts.
207 *
208 * @param row view to set up the guts for
209 * @param notificationSnoozeView view to set up/bind within {@code row}
210 */
211 private void initializeSnoozeView(
212 final ExpandableNotificationRow row,
213 NotificationSnooze notificationSnoozeView) {
214 NotificationGuts guts = row.getGuts();
215 StatusBarNotification sbn = row.getStatusBarNotification();
216
217 notificationSnoozeView.setSnoozeListener(mListContainer.getSwipeActionHelper());
218 notificationSnoozeView.setStatusBarNotification(sbn);
219 notificationSnoozeView.setSnoozeOptions(row.getEntry().snoozeCriteria);
220 guts.setHeightChangedListener((NotificationGuts g) -> {
221 mListContainer.onHeightChanged(row, row.isShown() /* needsAnimation */);
222 });
223 }
224
225 /**
226 * Sets up the {@link AppOpsInfo} inside the notification row's guts.
227 *
228 * @param row view to set up the guts for
229 * @param appOpsInfoView view to set up/bind within {@code row}
230 */
231 private void initializeAppOpsInfo(
232 final ExpandableNotificationRow row,
233 AppOpsInfo appOpsInfoView) {
234 NotificationGuts guts = row.getGuts();
235 StatusBarNotification sbn = row.getStatusBarNotification();
236 UserHandle userHandle = sbn.getUser();
237 PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
238 userHandle.getIdentifier());
239
240 AppOpsInfo.OnSettingsClickListener onSettingsClick =
241 (View v, String pkg, int uid, ArraySet<Integer> ops) -> {
242 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_OPS_GUTS_SETTINGS);
243 guts.resetFalsingCheck();
244 startAppOpsSettingsActivity(pkg, uid, ops, row);
245 };
246 if (!row.getEntry().mActiveAppOps.isEmpty()) {
247 appOpsInfoView.bindGuts(pmUser, onSettingsClick, sbn, row.getEntry().mActiveAppOps);
Julia Reynoldsb5867452018-02-28 16:31:35 -0500248 }
Rohan Shah524cf7b2018-03-15 14:40:02 -0700249 }
Julia Reynoldsb5867452018-02-28 16:31:35 -0500250
Rohan Shah524cf7b2018-03-15 14:40:02 -0700251 /**
252 * Sets up the {@link NotificationInfo} inside the notification row's guts.
Rohan Shah524cf7b2018-03-15 14:40:02 -0700253 * @param row view to set up the guts for
254 * @param notificationInfoView view to set up/bind within {@code row}
255 */
256 @VisibleForTesting
257 void initializeNotificationInfo(
258 final ExpandableNotificationRow row,
Gus Prevas5a70a4e2018-11-26 17:16:05 -0500259 NotificationInfo notificationInfoView) throws Exception {
Rohan Shah524cf7b2018-03-15 14:40:02 -0700260 NotificationGuts guts = row.getGuts();
261 StatusBarNotification sbn = row.getStatusBarNotification();
262 String packageName = sbn.getPackageName();
263 // Settings link is only valid for notifications that specify a non-system user
264 NotificationInfo.OnSettingsClickListener onSettingsClick = null;
265 UserHandle userHandle = sbn.getUser();
266 PackageManager pmUser = StatusBar.getPackageManagerForUser(
267 mContext, userHandle.getIdentifier());
268 INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
269 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
270 final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick =
271 (View v, Intent intent) -> {
272 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900273 guts.resetFalsingCheck();
Gus Prevas21437b32018-12-05 10:36:13 -0500274 mNotificationActivityStarter.startNotificationGutsIntent(intent, sbn.getUid(),
275 row);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900276 };
Rohan Shah524cf7b2018-03-15 14:40:02 -0700277 boolean isForBlockingHelper = row.isBlockingHelperShowing();
Eliot Courtney47098cb2017-10-18 17:30:30 +0900278
Rohan Shah524cf7b2018-03-15 14:40:02 -0700279 if (!userHandle.equals(UserHandle.ALL)
280 || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
281 onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
282 mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
283 guts.resetFalsingCheck();
Jason Monk297c04e2018-08-23 17:16:59 -0400284 mOnSettingsClickListener.onSettingsClick(sbn.getKey());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700285 startAppNotificationSettingsActivity(packageName, appUid, channel, row);
286 };
287 }
288
Julia Reynolds4131e782018-08-22 09:45:24 -0400289 notificationInfoView.bindNotification(
290 pmUser,
291 iNotificationManager,
292 packageName,
293 row.getEntry().channel,
294 row.getNumUniqueChannels(),
295 sbn,
296 mCheckSaveListener,
297 onSettingsClick,
298 onAppSettingsClick,
Jason Monk297c04e2018-08-23 17:16:59 -0400299 mDeviceProvisionedController.isDeviceProvisioned(),
Julia Reynolds4131e782018-08-22 09:45:24 -0400300 row.getIsNonblockable(),
301 isForBlockingHelper,
Gus Prevas9abc5062018-10-31 16:11:04 -0400302 row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE,
Gus Prevascaed15c2019-01-18 14:19:51 -0500303 row.getEntry().importance,
304 row.getEntry().isHighPriority());
Julia Reynolds4131e782018-08-22 09:45:24 -0400305
Rohan Shah524cf7b2018-03-15 14:40:02 -0700306 }
307
308 /**
Eliot Courtney47098cb2017-10-18 17:30:30 +0900309 * Closes guts or notification menus that might be visible and saves any changes.
310 *
311 * @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed.
312 * @param force true if guts should be closed regardless of state (used for snooze only).
313 * @param removeControls true if controls (e.g. info) should be closed.
314 * @param x if closed based on touch location, this is the x touch location.
315 * @param y if closed based on touch location, this is the y touch location.
316 * @param resetMenu if any notification menus that might be revealed should be closed.
317 */
318 public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls,
319 int x, int y, boolean resetMenu) {
320 if (mNotificationGutsExposed != null) {
321 mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
322 }
323 if (resetMenu) {
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900324 mListContainer.resetExposedMenuView(false /* animate */, true /* force */);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900325 }
326 }
327
328 /**
329 * Returns the exposed NotificationGuts or null if none are exposed.
330 */
331 public NotificationGuts getExposedGuts() {
332 return mNotificationGutsExposed;
333 }
334
335 public void setExposedGuts(NotificationGuts guts) {
336 mNotificationGutsExposed = guts;
337 }
338
Jason Monk297c04e2018-08-23 17:16:59 -0400339 public ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
340 return this::openGuts;
341 }
342
Eliot Courtney47098cb2017-10-18 17:30:30 +0900343 /**
Rohan Shah524cf7b2018-03-15 14:40:02 -0700344 * Opens guts on the given ExpandableNotificationRow {@code view}. This handles opening guts for
345 * the normal half-swipe and long-press use cases via a circular reveal. When the blocking
346 * helper needs to be shown on the row, this will skip the circular reveal.
Eliot Courtney47098cb2017-10-18 17:30:30 +0900347 *
Rohan Shah524cf7b2018-03-15 14:40:02 -0700348 * @param view ExpandableNotificationRow to open guts on
Eliot Courtney47098cb2017-10-18 17:30:30 +0900349 * @param x x coordinate of origin of circular reveal
350 * @param y y coordinate of origin of circular reveal
Rohan Shah524cf7b2018-03-15 14:40:02 -0700351 * @param menuItem MenuItem the guts should display
Eliot Courtney47098cb2017-10-18 17:30:30 +0900352 * @return true if guts was opened
353 */
Rohan Shah20790b82018-07-02 17:21:04 -0700354 public boolean openGuts(
Rohan Shah524cf7b2018-03-15 14:40:02 -0700355 View view,
356 int x,
357 int y,
358 NotificationMenuRowPlugin.MenuItem menuItem) {
359 if (!(view instanceof ExpandableNotificationRow)) {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900360 return false;
361 }
362
Rohan Shah524cf7b2018-03-15 14:40:02 -0700363 if (view.getWindowToken() == null) {
Eliot Courtney47098cb2017-10-18 17:30:30 +0900364 Log.e(TAG, "Trying to show notification guts, but not attached to window");
365 return false;
366 }
367
Rohan Shah524cf7b2018-03-15 14:40:02 -0700368 final ExpandableNotificationRow row = (ExpandableNotificationRow) view;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900369 if (row.isDark()) {
370 return false;
371 }
Rohan Shah524cf7b2018-03-15 14:40:02 -0700372 view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
Eliot Courtney47098cb2017-10-18 17:30:30 +0900373 if (row.areGutsExposed()) {
374 closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
375 true /* removeControls */, -1 /* x */, -1 /* y */,
376 true /* resetMenu */);
377 return false;
378 }
Julia Reynolds4131e782018-08-22 09:45:24 -0400379
380 row.inflateGuts();
Eliot Courtney47098cb2017-10-18 17:30:30 +0900381 NotificationGuts guts = row.getGuts();
Julia Reynolds4131e782018-08-22 09:45:24 -0400382 mNotificationGutsExposed = guts;
383 if (!bindGuts(row, menuItem)) {
384 // exception occurred trying to fill in all the data, bail.
385 return false;
386 }
387
Eliot Courtney47098cb2017-10-18 17:30:30 +0900388
389 // Assume we are a status_bar_notification_row
390 if (guts == null) {
391 // This view has no guts. Examples are the more card or the dismiss all view
392 return false;
393 }
394
TreeHugger Robotee8e1ae2019-01-18 19:26:40 +0000395 LogMaker logMaker = (row.getStatusBarNotification() == null)
396 ? new LogMaker(MetricsProto.MetricsEvent.ACTION_NOTE_CONTROLS)
397 : row.getStatusBarNotification().getLogMaker();
398 mMetricsLogger.write(logMaker.setCategory(MetricsProto.MetricsEvent.ACTION_NOTE_CONTROLS)
399 .setType(MetricsProto.MetricsEvent.TYPE_ACTION));
Eliot Courtney47098cb2017-10-18 17:30:30 +0900400
401 // ensure that it's laid but not visible until actually laid out
402 guts.setVisibility(View.INVISIBLE);
403 // Post to ensure the the guts are properly laid out.
404 guts.post(new Runnable() {
405 @Override
406 public void run() {
407 if (row.getWindowToken() == null) {
Rohan Shahae5fdf02018-05-09 18:59:44 -0700408 Log.e(TAG, "Trying to show notification guts in post(), but not attached to "
Eliot Courtney47098cb2017-10-18 17:30:30 +0900409 + "window");
410 return;
411 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900412 guts.setVisibility(View.VISIBLE);
yoshiki iguchia85c2a02018-01-12 11:28:06 +0900413
Eliot Courtney47098cb2017-10-18 17:30:30 +0900414 final boolean needsFalsingProtection =
Jason Monk297c04e2018-08-23 17:16:59 -0400415 (mStatusBarStateController.getState() == StatusBarState.KEYGUARD &&
Eliot Courtney47098cb2017-10-18 17:30:30 +0900416 !mAccessibilityManager.isTouchExplorationEnabled());
Rohan Shah524cf7b2018-03-15 14:40:02 -0700417
418 guts.openControls(
419 !row.isBlockingHelperShowing(),
420 x,
421 y,
422 needsFalsingProtection,
Rohan Shah56eb0912018-05-10 21:49:04 -0700423 row::onGutsOpened);
yoshiki iguchia85c2a02018-01-12 11:28:06 +0900424
Eliot Courtney47098cb2017-10-18 17:30:30 +0900425 row.closeRemoteInput();
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900426 mListContainer.onHeightChanged(row, true /* needsAnimation */);
Rohan Shah524cf7b2018-03-15 14:40:02 -0700427 mGutsMenuItem = menuItem;
Eliot Courtney47098cb2017-10-18 17:30:30 +0900428 }
429 });
430 return true;
431 }
432
433 @Override
Kevina5ff1fa2018-08-21 16:35:48 -0700434 public void setCallback(NotificationSafeToRemoveCallback callback) {
435 mNotificationLifetimeFinishedCallback = callback;
436 }
437
438 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500439 public boolean shouldExtendLifetime(NotificationEntry entry) {
Kevina5ff1fa2018-08-21 16:35:48 -0700440 return entry != null
441 &&(mNotificationGutsExposed != null
Evan Laird94492852018-10-25 13:43:01 -0400442 && entry.getGuts() != null
443 && mNotificationGutsExposed == entry.getGuts()
Kevina5ff1fa2018-08-21 16:35:48 -0700444 && !mNotificationGutsExposed.isLeavebehind());
445 }
446
447 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500448 public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) {
Kevina5ff1fa2018-08-21 16:35:48 -0700449 if (shouldExtend) {
450 mKeyToRemoveOnGutsClosed = entry.key;
451 if (Log.isLoggable(TAG, Log.DEBUG)) {
452 Log.d(TAG, "Keeping notification because it's showing guts. " + entry.key);
453 }
454 } else {
455 if (mKeyToRemoveOnGutsClosed != null && mKeyToRemoveOnGutsClosed.equals(entry.key)) {
456 mKeyToRemoveOnGutsClosed = null;
457 if (Log.isLoggable(TAG, Log.DEBUG)) {
458 Log.d(TAG, "Notification that was kept for guts was updated. " + entry.key);
459 }
460 }
461 }
462 }
463
464 @Override
Eliot Courtney47098cb2017-10-18 17:30:30 +0900465 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Eliot Courtney09322282017-11-09 15:31:19 +0900466 pw.println("NotificationGutsManager state:");
467 pw.print(" mKeyToRemoveOnGutsClosed: ");
Eliot Courtney47098cb2017-10-18 17:30:30 +0900468 pw.println(mKeyToRemoveOnGutsClosed);
469 }
Julia Reynolds84dc96b2017-11-14 09:51:01 -0500470
471 public interface OnSettingsClickListener {
Jason Monk297c04e2018-08-23 17:16:59 -0400472 public void onSettingsClick(String key);
Julia Reynolds84dc96b2017-11-14 09:51:01 -0500473 }
Eliot Courtney47098cb2017-10-18 17:30:30 +0900474}