blob: 626701c6cb349cd63bee19e8c610e9ebc0bdb9e7 [file] [log] [blame]
Mady Mellor87d79452017-01-10 11:52:52 -08001/*
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 */
16
Rohan Shah20790b82018-07-02 17:21:04 -070017package com.android.systemui.statusbar.notification.row;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -050018
Gus Prevas9abc5062018-10-31 16:11:04 -040019import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
Gus Prevas9abc5062018-10-31 16:11:04 -040020import static android.app.NotificationManager.IMPORTANCE_LOW;
Julia Reynolds8728d002019-04-19 15:46:13 -040021import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
Julia Reynolds3aedded2017-03-31 14:42:09 -040022
Ned Burns369f1472019-05-22 21:07:26 -040023import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN;
24
25import static java.lang.annotation.RetentionPolicy.SOURCE;
26
Julia Reynolds437cdb12018-01-03 12:27:24 -050027import android.animation.Animator;
28import android.animation.AnimatorListenerAdapter;
29import android.animation.AnimatorSet;
30import android.animation.ObjectAnimator;
Gus Prevas9abc5062018-10-31 16:11:04 -040031import android.annotation.IntDef;
Rohan Shahca0447e2018-03-30 15:18:27 -070032import android.annotation.Nullable;
Mady Mellor87d79452017-01-10 11:52:52 -080033import android.app.INotificationManager;
Julia Reynolds3aedded2017-03-31 14:42:09 -040034import android.app.Notification;
Mady Mellor87d79452017-01-10 11:52:52 -080035import android.app.NotificationChannel;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -050036import android.app.NotificationChannelGroup;
Mady Mellor87d79452017-01-10 11:52:52 -080037import android.content.Context;
Julia Reynolds3aedded2017-03-31 14:42:09 -040038import android.content.Intent;
39import android.content.pm.ActivityInfo;
Mady Mellor87d79452017-01-10 11:52:52 -080040import android.content.pm.ApplicationInfo;
Mady Mellor87d79452017-01-10 11:52:52 -080041import android.content.pm.PackageManager;
Julia Reynolds3aedded2017-03-31 14:42:09 -040042import android.content.pm.ResolveInfo;
Mady Mellor87d79452017-01-10 11:52:52 -080043import android.graphics.drawable.Drawable;
William Brockman75cf66a2018-12-21 13:25:01 -050044import android.metrics.LogMaker;
Rohan Shahca0447e2018-03-30 15:18:27 -070045import android.os.Handler;
Mady Mellor87d79452017-01-10 11:52:52 -080046import android.os.RemoteException;
Julia Reynolds3aedded2017-03-31 14:42:09 -040047import android.service.notification.StatusBarNotification;
48import android.text.TextUtils;
Ned Burns369f1472019-05-22 21:07:26 -040049import android.transition.ChangeBounds;
50import android.transition.Fade;
Julia Reynolds4d1dd792019-04-29 11:48:29 -040051import android.transition.TransitionManager;
Ned Burns369f1472019-05-22 21:07:26 -040052import android.transition.TransitionSet;
Mady Mellor87d79452017-01-10 11:52:52 -080053import android.util.AttributeSet;
Rohan Shahca0447e2018-03-30 15:18:27 -070054import android.util.Log;
Mady Mellor87d79452017-01-10 11:52:52 -080055import android.view.View;
Julia Reynoldse0341482018-03-08 14:42:50 -050056import android.view.ViewGroup;
Geoffrey Pitschd94e7882017-04-06 09:52:11 -040057import android.view.accessibility.AccessibilityEvent;
Mady Mellor87d79452017-01-10 11:52:52 -080058import android.widget.ImageView;
59import android.widget.LinearLayout;
Mady Mellor87d79452017-01-10 11:52:52 -080060import android.widget.TextView;
61
Rohan Shah524cf7b2018-03-15 14:40:02 -070062import com.android.internal.annotations.VisibleForTesting;
Mady Mellor87d79452017-01-10 11:52:52 -080063import com.android.internal.logging.MetricsLogger;
64import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Rohan Shah524cf7b2018-03-15 14:40:02 -070065import com.android.systemui.Dependency;
Julia Reynolds437cdb12018-01-03 12:27:24 -050066import com.android.systemui.Interpolators;
Mady Mellor87d79452017-01-10 11:52:52 -080067import com.android.systemui.R;
Rohan Shah20790b82018-07-02 17:21:04 -070068import com.android.systemui.statusbar.notification.logging.NotificationCounters;
Mady Mellor87d79452017-01-10 11:52:52 -080069
Ned Burns369f1472019-05-22 21:07:26 -040070import java.lang.annotation.Retention;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050071import java.util.List;
Evan Laird47dc4542019-04-24 15:10:52 -040072import java.util.Set;
Mady Mellor87d79452017-01-10 11:52:52 -080073
74/**
Rohan Shahda5dcdd2018-04-27 17:21:50 -070075 * The guts of a notification revealed when performing a long press. This also houses the blocking
76 * helper affordance that allows a user to keep/stop notifications after swiping one away.
Mady Mellor87d79452017-01-10 11:52:52 -080077 */
Mady Mellor95d743c2017-01-10 12:05:27 -080078public class NotificationInfo extends LinearLayout implements NotificationGuts.GutsContent {
Mady Mellor87d79452017-01-10 11:52:52 -080079 private static final String TAG = "InfoGuts";
80
Gus Prevas894d9152018-11-12 13:51:40 -050081 @IntDef(prefix = { "ACTION_" }, value = {
82 ACTION_NONE,
83 ACTION_UNDO,
84 ACTION_TOGGLE_SILENT,
85 ACTION_BLOCK,
Gus Prevas9abc5062018-10-31 16:11:04 -040086 })
Gus Prevas894d9152018-11-12 13:51:40 -050087 public @interface NotificationInfoAction {
88 }
Gus Prevas9abc5062018-10-31 16:11:04 -040089
Gus Prevas894d9152018-11-12 13:51:40 -050090 public static final int ACTION_NONE = 0;
Nadia Benbernoub66516c2019-03-06 11:01:53 -050091 static final int ACTION_UNDO = 1;
Julia Reynolds52a27372019-04-03 11:00:17 -040092 // standard controls
Nadia Benbernoub66516c2019-03-06 11:01:53 -050093 static final int ACTION_TOGGLE_SILENT = 2;
Julia Reynolds52a27372019-04-03 11:00:17 -040094 // unused
Nadia Benbernoub66516c2019-03-06 11:01:53 -050095 static final int ACTION_BLOCK = 3;
Julia Reynolds52a27372019-04-03 11:00:17 -040096 // blocking helper
Nadia Benbernoub66516c2019-03-06 11:01:53 -050097 static final int ACTION_DELIVER_SILENTLY = 4;
Julia Reynolds52a27372019-04-03 11:00:17 -040098 // standard controls
Nadia Benbernoub66516c2019-03-06 11:01:53 -050099 private static final int ACTION_ALERT = 5;
Gus Prevas9abc5062018-10-31 16:11:04 -0400100
Ned Burns369f1472019-05-22 21:07:26 -0400101 private TextView mPriorityDescriptionView;
102 private TextView mSilentDescriptionView;
Dan Sandler0ed02292019-05-14 19:18:35 -0400103
Mady Mellor87d79452017-01-10 11:52:52 -0800104 private INotificationManager mINotificationManager;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500105 private PackageManager mPm;
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700106 private MetricsLogger mMetricsLogger;
Evan Laird47dc4542019-04-24 15:10:52 -0400107 private ChannelEditorDialogController mChannelEditorDialogController;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500108
Rohan Shahca0447e2018-03-30 15:18:27 -0700109 private String mPackageName;
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400110 private String mAppName;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500111 private int mAppUid;
Julia Reynolds268647a2018-10-25 16:54:27 -0400112 private String mDelegatePkg;
Rohan Shahca0447e2018-03-30 15:18:27 -0700113 private int mNumUniqueChannelsInRow;
Evan Laird47dc4542019-04-24 15:10:52 -0400114 private Set<NotificationChannel> mUniqueChannelsInRow;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500115 private NotificationChannel mSingleNotificationChannel;
Gus Prevas9abc5062018-10-31 16:11:04 -0400116 private int mStartingChannelImportance;
Gus Prevascaed15c2019-01-18 14:19:51 -0500117 private boolean mWasShownHighPriority;
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400118 private boolean mPressedApply;
119
Gus Prevascaed15c2019-01-18 14:19:51 -0500120 /**
121 * The last importance level chosen by the user. Null if the user has not chosen an importance
122 * level; non-null once the user takes an action which indicates an explicit preference.
123 */
124 @Nullable private Integer mChosenImportance;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500125 private boolean mIsSingleDefaultChannel;
Rohan Shah63411fc2018-03-28 19:05:52 -0700126 private boolean mIsNonblockable;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500127 private StatusBarNotification mSbn;
128 private AnimatorSet mExpandAnimation;
Julia Reynolds35765d82018-08-17 11:39:19 -0400129 private boolean mIsDeviceProvisioned;
Mady Mellor87d79452017-01-10 11:52:52 -0800130
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400131 private CheckSaveListener mCheckSaveListener;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500132 private OnSettingsClickListener mOnSettingsClickListener;
Julia Reynolds3aedded2017-03-31 14:42:09 -0400133 private OnAppSettingsClickListener mAppSettingsClickListener;
Mady Mellor95d743c2017-01-10 12:05:27 -0800134 private NotificationGuts mGutsContainer;
Evan Laird47dc4542019-04-24 15:10:52 -0400135 private Drawable mPkgIcon;
Rohan Shah524cf7b2018-03-15 14:40:02 -0700136
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700137 /** Whether this view is being shown as part of the blocking helper. */
Rohan Shah524cf7b2018-03-15 14:40:02 -0700138 private boolean mIsForBlockingHelper;
Mady Mellor87d79452017-01-10 11:52:52 -0800139
Rohan Shahdd588c72018-05-09 20:32:15 -0700140 /**
141 * String that describes how the user exit or quit out of this view, also used as a counter tag.
142 */
143 private String mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700144
Julia Reynolds52a27372019-04-03 11:00:17 -0400145 // used by standard ui
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500146 private OnClickListener mOnAlert = v -> {
147 mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
148 mChosenImportance = IMPORTANCE_DEFAULT;
Ned Burns369f1472019-05-22 21:07:26 -0400149 applyAlertingBehavior(BEHAVIOR_ALERTING, true /* userTriggered */);
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500150 };
151
Julia Reynolds52a27372019-04-03 11:00:17 -0400152 // used by standard ui
153 private OnClickListener mOnSilent = v -> {
154 mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY;
155 mChosenImportance = IMPORTANCE_LOW;
Ned Burns369f1472019-05-22 21:07:26 -0400156 applyAlertingBehavior(BEHAVIOR_SILENT, true /* userTriggered */);
Julia Reynolds52a27372019-04-03 11:00:17 -0400157 };
158
159 // used by standard ui
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500160 private OnClickListener mOnDismissSettings = v -> {
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400161 mPressedApply = true;
Evan Laird7956fe82019-05-30 17:14:57 -0400162 closeControls(v, true);
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500163 };
164
Julia Reynolds52a27372019-04-03 11:00:17 -0400165 // used by blocking helper
166 private OnClickListener mOnKeepShowing = v -> {
167 mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
Evan Laird7956fe82019-05-30 17:14:57 -0400168 closeControls(v, true);
Julia Reynolds52a27372019-04-03 11:00:17 -0400169 mMetricsLogger.write(getLogMaker().setCategory(
170 MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
171 .setType(MetricsEvent.TYPE_ACTION)
172 .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_STAY_SILENT));
173 };
174
175 // used by blocking helper
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500176 private OnClickListener mOnDeliverSilently = v -> {
177 handleSaveImportance(
178 ACTION_DELIVER_SILENTLY, MetricsEvent.BLOCKING_HELPER_CLICK_STAY_SILENT);
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500179 };
180
181 private void handleSaveImportance(int action, int metricsSubtype) {
Gus Prevas533836a2018-09-24 17:15:32 -0400182 Runnable saveImportance = () -> {
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500183 saveImportanceAndExitReason(action);
Will Brockmana399f022019-01-30 10:59:36 -0500184 if (mIsForBlockingHelper) {
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500185 swapContent(action, true /* animate */);
Will Brockmana399f022019-01-30 10:59:36 -0500186 mMetricsLogger.write(getLogMaker()
187 .setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
188 .setType(MetricsEvent.TYPE_ACTION)
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500189 .setSubtype(metricsSubtype));
Will Brockmana399f022019-01-30 10:59:36 -0500190 }
Gus Prevas533836a2018-09-24 17:15:32 -0400191 };
192 if (mCheckSaveListener != null) {
193 mCheckSaveListener.checkSave(saveImportance, mSbn);
194 } else {
195 saveImportance.run();
196 }
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500197 }
Julia Reynolds437cdb12018-01-03 12:27:24 -0500198
199 private OnClickListener mOnUndo = v -> {
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700200 // Reset exit counter that we'll log and record an undo event separately (not an exit event)
Rohan Shahdd588c72018-05-09 20:32:15 -0700201 mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
Will Brockmana399f022019-01-30 10:59:36 -0500202 if (mIsForBlockingHelper) {
203 logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_UNDO);
204 mMetricsLogger.write(getLogMaker().setCategory(
205 MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
206 .setType(MetricsEvent.TYPE_DISMISS)
207 .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_UNDO));
208 } else {
Julia Reynolds52a27372019-04-03 11:00:17 -0400209 // TODO: this can't happen?
Will Brockmana399f022019-01-30 10:59:36 -0500210 mMetricsLogger.write(importanceChangeLogMaker().setType(MetricsEvent.TYPE_DISMISS));
211 }
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500212 saveImportanceAndExitReason(ACTION_UNDO);
Gus Prevas894d9152018-11-12 13:51:40 -0500213 swapContent(ACTION_UNDO, true /* animate */);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500214 };
215
Mady Mellor87d79452017-01-10 11:52:52 -0800216 public NotificationInfo(Context context, AttributeSet attrs) {
217 super(context, attrs);
218 }
219
Ned Burns369f1472019-05-22 21:07:26 -0400220 @Override
221 protected void onFinishInflate() {
222 super.onFinishInflate();
223
224 mPriorityDescriptionView = findViewById(R.id.alert_summary);
225 mSilentDescriptionView = findViewById(R.id.silence_summary);
226 }
227
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400228 // Specify a CheckSaveListener to override when/if the user's changes are committed.
229 public interface CheckSaveListener {
230 // Invoked when importance has changed and the NotificationInfo wants to try to save it.
231 // Listener should run saveImportance unless the change should be canceled.
Eliot Courtney47098cb2017-10-18 17:30:30 +0900232 void checkSave(Runnable saveImportance, StatusBarNotification sbn);
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400233 }
234
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500235 public interface OnSettingsClickListener {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500236 void onClick(View v, NotificationChannel channel, int appUid);
Mady Mellor87d79452017-01-10 11:52:52 -0800237 }
238
Julia Reynolds3aedded2017-03-31 14:42:09 -0400239 public interface OnAppSettingsClickListener {
240 void onClick(View v, Intent intent);
241 }
242
Rohan Shah524cf7b2018-03-15 14:40:02 -0700243 @VisibleForTesting
244 void bindNotification(
245 final PackageManager pm,
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500246 final INotificationManager iNotificationManager,
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500247 final String pkg,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500248 final NotificationChannel notificationChannel,
Evan Laird47dc4542019-04-24 15:10:52 -0400249 final Set<NotificationChannel> uniqueChannelsInRow,
Julia Reynolds3aedded2017-03-31 14:42:09 -0400250 final StatusBarNotification sbn,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500251 final CheckSaveListener checkSaveListener,
252 final OnSettingsClickListener onSettingsClick,
253 final OnAppSettingsClickListener onAppSettingsClick,
Julia Reynolds35765d82018-08-17 11:39:19 -0400254 boolean isDeviceProvisioned,
Gus Prevas9abc5062018-10-31 16:11:04 -0400255 boolean isNonblockable,
Gus Prevascaed15c2019-01-18 14:19:51 -0500256 int importance,
257 boolean wasShownHighPriority)
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500258 throws RemoteException {
Rohan Shahca0447e2018-03-30 15:18:27 -0700259 bindNotification(pm, iNotificationManager, pkg, notificationChannel,
Evan Laird47dc4542019-04-24 15:10:52 -0400260 uniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
Julia Reynolds35765d82018-08-17 11:39:19 -0400261 onAppSettingsClick, isDeviceProvisioned, isNonblockable,
Julia Reynolds52a27372019-04-03 11:00:17 -0400262 false /* isBlockingHelper */,
Gus Prevascaed15c2019-01-18 14:19:51 -0500263 importance, wasShownHighPriority);
Julia Reynolds0ef7d842018-01-24 17:50:31 -0500264 }
265
Rohan Shah524cf7b2018-03-15 14:40:02 -0700266 public void bindNotification(
267 PackageManager pm,
268 INotificationManager iNotificationManager,
269 String pkg,
270 NotificationChannel notificationChannel,
Evan Laird47dc4542019-04-24 15:10:52 -0400271 Set<NotificationChannel> uniqueChannelsInRow,
Rohan Shah524cf7b2018-03-15 14:40:02 -0700272 StatusBarNotification sbn,
273 CheckSaveListener checkSaveListener,
274 OnSettingsClickListener onSettingsClick,
275 OnAppSettingsClickListener onAppSettingsClick,
Julia Reynolds35765d82018-08-17 11:39:19 -0400276 boolean isDeviceProvisioned,
Rohan Shah63411fc2018-03-28 19:05:52 -0700277 boolean isNonblockable,
Rohan Shah524cf7b2018-03-15 14:40:02 -0700278 boolean isForBlockingHelper,
Gus Prevascaed15c2019-01-18 14:19:51 -0500279 int importance,
280 boolean wasShownHighPriority)
Rohan Shah524cf7b2018-03-15 14:40:02 -0700281 throws RemoteException {
Mady Mellor87d79452017-01-10 11:52:52 -0800282 mINotificationManager = iNotificationManager;
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700283 mMetricsLogger = Dependency.get(MetricsLogger.class);
Evan Laird47dc4542019-04-24 15:10:52 -0400284 mChannelEditorDialogController = Dependency.get(ChannelEditorDialogController.class);
Rohan Shahca0447e2018-03-30 15:18:27 -0700285 mPackageName = pkg;
Evan Laird47dc4542019-04-24 15:10:52 -0400286 mUniqueChannelsInRow = uniqueChannelsInRow;
287 mNumUniqueChannelsInRow = uniqueChannelsInRow.size();
Julia Reynolds3aedded2017-03-31 14:42:09 -0400288 mSbn = sbn;
289 mPm = pm;
290 mAppSettingsClickListener = onAppSettingsClick;
Rohan Shahca0447e2018-03-30 15:18:27 -0700291 mAppName = mPackageName;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500292 mCheckSaveListener = checkSaveListener;
293 mOnSettingsClickListener = onSettingsClick;
294 mSingleNotificationChannel = notificationChannel;
Gus Prevascaed15c2019-01-18 14:19:51 -0500295 mStartingChannelImportance = mSingleNotificationChannel.getImportance();
296 mWasShownHighPriority = wasShownHighPriority;
Rohan Shah63411fc2018-03-28 19:05:52 -0700297 mIsNonblockable = isNonblockable;
Rohan Shah524cf7b2018-03-15 14:40:02 -0700298 mIsForBlockingHelper = isForBlockingHelper;
Rohan Shahdbd64e72018-03-28 14:46:50 -0700299 mAppUid = mSbn.getUid();
Julia Reynolds268647a2018-10-25 16:54:27 -0400300 mDelegatePkg = mSbn.getOpPkg();
Julia Reynolds35765d82018-08-17 11:39:19 -0400301 mIsDeviceProvisioned = isDeviceProvisioned;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500302
Julia Reynolds437cdb12018-01-03 12:27:24 -0500303 int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400304 pkg, mAppUid, false /* includeDeleted */);
Rohan Shahca0447e2018-03-30 15:18:27 -0700305 if (mNumUniqueChannelsInRow == 0) {
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400306 throw new IllegalArgumentException("bindNotification requires at least one channel");
307 } else {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500308 // Special behavior for the Default channel if no other channels have been defined.
Rohan Shahca0447e2018-03-30 15:18:27 -0700309 mIsSingleDefaultChannel = mNumUniqueChannelsInRow == 1
Rohan Shahdbd64e72018-03-28 14:46:50 -0700310 && mSingleNotificationChannel.getId().equals(
311 NotificationChannel.DEFAULT_CHANNEL_ID)
312 && numTotalChannels == 1;
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400313 }
314
Julia Reynolds437cdb12018-01-03 12:27:24 -0500315 bindHeader();
Julia Reynolds52a27372019-04-03 11:00:17 -0400316 bindChannelDetails();
317
318 if (mIsForBlockingHelper) {
319 bindBlockingHelper();
320 } else {
321 bindInlineControls();
322 }
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200323
Will Brockmana399f022019-01-30 10:59:36 -0500324 mMetricsLogger.write(notificationControlsLogMaker());
Julia Reynolds437cdb12018-01-03 12:27:24 -0500325 }
326
Julia Reynolds52a27372019-04-03 11:00:17 -0400327 private void bindBlockingHelper() {
328 findViewById(R.id.inline_controls).setVisibility(GONE);
329 findViewById(R.id.blocking_helper).setVisibility(VISIBLE);
330
331 findViewById(R.id.undo).setOnClickListener(mOnUndo);
332
333 View turnOffButton = findViewById(R.id.blocking_helper_turn_off_notifications);
334 turnOffButton.setOnClickListener(getSettingsOnClickListener());
335 turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() ? VISIBLE : GONE);
336
337 TextView keepShowing = findViewById(R.id.keep_showing);
338 keepShowing.setOnClickListener(mOnKeepShowing);
339
340 View deliverSilently = findViewById(R.id.deliver_silently);
341 deliverSilently.setOnClickListener(mOnDeliverSilently);
342 }
343
344 private void bindInlineControls() {
345 findViewById(R.id.inline_controls).setVisibility(VISIBLE);
346 findViewById(R.id.blocking_helper).setVisibility(GONE);
347
348 if (mIsNonblockable) {
349 findViewById(R.id.non_configurable_text).setVisibility(VISIBLE);
350 findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE);
351 findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
Julia Reynolds8728d002019-04-19 15:46:13 -0400352 ((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button);
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400353 findViewById(R.id.turn_off_notifications).setVisibility(GONE);
Julia Reynolds52a27372019-04-03 11:00:17 -0400354 } else if (mNumUniqueChannelsInRow > 1) {
355 findViewById(R.id.non_configurable_text).setVisibility(GONE);
356 findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
357 findViewById(R.id.non_configurable_multichannel_text).setVisibility(VISIBLE);
358 } else {
359 findViewById(R.id.non_configurable_text).setVisibility(GONE);
360 findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE);
361 findViewById(R.id.interruptiveness_settings).setVisibility(VISIBLE);
362 }
363
364 View turnOffButton = findViewById(R.id.turn_off_notifications);
Evan Laird47dc4542019-04-24 15:10:52 -0400365 turnOffButton.setOnClickListener(getTurnOffNotificationsClickListener());
Julia Reynolds52a27372019-04-03 11:00:17 -0400366 turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() && !mIsNonblockable
367 ? VISIBLE : GONE);
368
369 View done = findViewById(R.id.done);
370 done.setOnClickListener(mOnDismissSettings);
371
372
Julia Reynolds8728d002019-04-19 15:46:13 -0400373 View silent = findViewById(R.id.silence);
374 View alert = findViewById(R.id.alert);
Julia Reynolds52a27372019-04-03 11:00:17 -0400375 silent.setOnClickListener(mOnSilent);
376 alert.setOnClickListener(mOnAlert);
377
Ned Burns369f1472019-05-22 21:07:26 -0400378 applyAlertingBehavior(
379 mWasShownHighPriority ? BEHAVIOR_ALERTING : BEHAVIOR_SILENT,
380 false /* userTriggered */);
Julia Reynolds52a27372019-04-03 11:00:17 -0400381 }
382
383 private void bindHeader() {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500384 // Package name
Evan Laird47dc4542019-04-24 15:10:52 -0400385 mPkgIcon = null;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500386 ApplicationInfo info;
387 try {
Rohan Shahca0447e2018-03-30 15:18:27 -0700388 info = mPm.getApplicationInfo(
389 mPackageName,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500390 PackageManager.MATCH_UNINSTALLED_PACKAGES
391 | PackageManager.MATCH_DISABLED_COMPONENTS
392 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
393 | PackageManager.MATCH_DIRECT_BOOT_AWARE);
394 if (info != null) {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500395 mAppName = String.valueOf(mPm.getApplicationLabel(info));
Evan Laird47dc4542019-04-24 15:10:52 -0400396 mPkgIcon = mPm.getApplicationIcon(info);
Geoffrey Pitschee8c81e2017-03-23 11:38:56 -0400397 }
Julia Reynolds437cdb12018-01-03 12:27:24 -0500398 } catch (PackageManager.NameNotFoundException e) {
399 // app is gone, just show package name and generic icon
Evan Laird47dc4542019-04-24 15:10:52 -0400400 mPkgIcon = mPm.getDefaultActivityIcon();
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500401 }
Evan Laird47dc4542019-04-24 15:10:52 -0400402 ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(mPkgIcon);
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400403 ((TextView) findViewById(R.id.pkgname)).setText(mAppName);
Mady Mellor87d79452017-01-10 11:52:52 -0800404
Julia Reynolds268647a2018-10-25 16:54:27 -0400405 // Delegate
406 bindDelegate();
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000407
Julia Reynolds52a27372019-04-03 11:00:17 -0400408 // Set up app settings link (i.e. Customize)
409 View settingsLinkView = findViewById(R.id.app_settings);
410 Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName,
411 mSingleNotificationChannel,
412 mSbn.getId(), mSbn.getTag());
413 if (settingsIntent != null
414 && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
415 settingsLinkView.setVisibility(VISIBLE);
416 settingsLinkView.setOnClickListener((View view) -> {
417 mAppSettingsClickListener.onClick(view, settingsIntent);
418 });
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000419 } else {
Julia Reynolds52a27372019-04-03 11:00:17 -0400420 settingsLinkView.setVisibility(View.GONE);
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000421 }
Julia Reynolds52a27372019-04-03 11:00:17 -0400422
423 // System Settings button.
424 final View settingsButton = findViewById(R.id.info);
425 settingsButton.setOnClickListener(getSettingsOnClickListener());
426 settingsButton.setVisibility(settingsButton.hasOnClickListeners() ? VISIBLE : GONE);
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000427 }
428
Julia Reynolds52a27372019-04-03 11:00:17 -0400429 private OnClickListener getSettingsOnClickListener() {
430 if (mAppUid >= 0 && mOnSettingsClickListener != null && mIsDeviceProvisioned) {
431 final int appUidF = mAppUid;
432 return ((View view) -> {
433 logBlockingHelperCounter(
434 NotificationCounters.BLOCKING_HELPER_NOTIF_SETTINGS);
435 mOnSettingsClickListener.onClick(view,
436 mNumUniqueChannelsInRow > 1 ? null : mSingleNotificationChannel,
437 appUidF);
438 });
439 }
440 return null;
441 }
442
Evan Laird47dc4542019-04-24 15:10:52 -0400443 private OnClickListener getTurnOffNotificationsClickListener() {
444 return ((View view) -> {
445 if (mChannelEditorDialogController != null) {
446 mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid,
447 mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener);
Evan Laird7956fe82019-05-30 17:14:57 -0400448 mChannelEditorDialogController.setOnFinishListener(
449 () -> closeControls(this, false));
Evan Laird47dc4542019-04-24 15:10:52 -0400450 mChannelEditorDialogController.show();
451 }
452 });
453 }
454
Julia Reynolds52a27372019-04-03 11:00:17 -0400455 private void bindChannelDetails() throws RemoteException {
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000456 bindName();
Julia Reynolds268647a2018-10-25 16:54:27 -0400457 bindGroup();
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000458 }
459
460 private void bindName() {
461 final TextView channelName = findViewById(R.id.channel_name);
462 if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) {
463 channelName.setVisibility(View.GONE);
464 } else {
465 channelName.setText(mSingleNotificationChannel.getName());
466 }
Julia Reynoldsac98aea2018-10-25 16:54:27 -0400467 }
468
Julia Reynolds268647a2018-10-25 16:54:27 -0400469 private void bindDelegate() {
470 TextView delegateView = findViewById(R.id.delegate_name);
471 TextView dividerView = findViewById(R.id.pkg_divider);
472
473 CharSequence delegatePkg = null;
474 if (!TextUtils.equals(mPackageName, mDelegatePkg)) {
475 // this notification was posted by a delegate!
Julia Reynolds268647a2018-10-25 16:54:27 -0400476 delegateView.setVisibility(View.VISIBLE);
477 dividerView.setVisibility(View.VISIBLE);
478 } else {
479 delegateView.setVisibility(View.GONE);
480 dividerView.setVisibility(View.GONE);
481 }
482 }
483
484 private void bindGroup() throws RemoteException {
485 // Set group information if this channel has an associated group.
486 CharSequence groupName = null;
487 if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
488 final NotificationChannelGroup notificationChannelGroup =
489 mINotificationManager.getNotificationChannelGroupForPackage(
490 mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
491 if (notificationChannelGroup != null) {
492 groupName = notificationChannelGroup.getName();
493 }
494 }
495 TextView groupNameView = findViewById(R.id.group_name);
Julia Reynolds268647a2018-10-25 16:54:27 -0400496 if (groupName != null) {
497 groupNameView.setText(groupName);
498 groupNameView.setVisibility(View.VISIBLE);
Julia Reynolds268647a2018-10-25 16:54:27 -0400499 } else {
500 groupNameView.setVisibility(View.GONE);
Julia Reynolds268647a2018-10-25 16:54:27 -0400501 }
502 }
503
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500504
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700505 @VisibleForTesting
506 void logBlockingHelperCounter(String counterTag) {
507 if (mIsForBlockingHelper) {
508 mMetricsLogger.count(counterTag, 1);
509 }
510 }
511
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500512 private void saveImportance() {
Gus Prevas894d9152018-11-12 13:51:40 -0500513 if (!mIsNonblockable
514 || mExitReason != NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS) {
Gus Prevascaed15c2019-01-18 14:19:51 -0500515 if (mChosenImportance == null) {
516 mChosenImportance = mStartingChannelImportance;
517 }
Gus Prevas533836a2018-09-24 17:15:32 -0400518 updateImportance();
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500519 }
Rohan Shahca0447e2018-03-30 15:18:27 -0700520 }
521
522 /**
523 * Commits the updated importance values on the background thread.
524 */
525 private void updateImportance() {
Gus Prevascaed15c2019-01-18 14:19:51 -0500526 if (mChosenImportance != null) {
527 mMetricsLogger.write(importanceChangeLogMaker());
Rohan Shahca0447e2018-03-30 15:18:27 -0700528
Julia Reynolds8728d002019-04-19 15:46:13 -0400529 int newImportance = mChosenImportance;
530 if (mStartingChannelImportance != IMPORTANCE_UNSPECIFIED) {
531 if ((mWasShownHighPriority && mChosenImportance >= IMPORTANCE_DEFAULT)
532 || (!mWasShownHighPriority && mChosenImportance < IMPORTANCE_DEFAULT)) {
533 newImportance = mStartingChannelImportance;
534 }
535 }
536
Gus Prevascaed15c2019-01-18 14:19:51 -0500537 Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
538 bgHandler.post(
539 new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
540 mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
Julia Reynolds8728d002019-04-19 15:46:13 -0400541 mStartingChannelImportance, newImportance));
Gus Prevascaed15c2019-01-18 14:19:51 -0500542 }
Mady Mellor87d79452017-01-10 11:52:52 -0800543 }
544
Ned Burns369f1472019-05-22 21:07:26 -0400545 private void applyAlertingBehavior(@AlertingBehavior int behavior, boolean userTriggered) {
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400546 if (userTriggered) {
Ned Burns369f1472019-05-22 21:07:26 -0400547 TransitionSet transition = new TransitionSet();
548 transition.setOrdering(TransitionSet.ORDERING_TOGETHER);
549 transition.addTransition(new Fade(Fade.OUT))
550 .addTransition(new ChangeBounds())
551 .addTransition(
552 new Fade(Fade.IN)
553 .setStartDelay(150)
554 .setDuration(200)
555 .setInterpolator(FAST_OUT_SLOW_IN));
556 transition.setDuration(350);
557 transition.setInterpolator(FAST_OUT_SLOW_IN);
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400558 TransitionManager.beginDelayedTransition(this, transition);
559 }
Ned Burns369f1472019-05-22 21:07:26 -0400560
561 View alert = findViewById(R.id.alert);
562 View silence = findViewById(R.id.silence);
563
564 switch (behavior) {
565 case BEHAVIOR_ALERTING:
566 alert.setSelected(true);
567 silence.setSelected(false);
568 mPriorityDescriptionView.setVisibility(VISIBLE);
569 mSilentDescriptionView.setVisibility(GONE);
570 break;
571 case BEHAVIOR_SILENT:
572 alert.setSelected(false);
573 silence.setSelected(true);
574 mSilentDescriptionView.setVisibility(VISIBLE);
575 mPriorityDescriptionView.setVisibility(GONE);
576 break;
577 default:
578 throw new IllegalArgumentException("Unrecognized alerting behavior: " + behavior);
Julia Reynolds8728d002019-04-19 15:46:13 -0400579 }
Ned Burns369f1472019-05-22 21:07:26 -0400580
581 boolean isAChange = mWasShownHighPriority != (behavior == BEHAVIOR_ALERTING);
582 TextView done = findViewById(R.id.done);
583 done.setText(isAChange ? R.string.inline_ok_button : R.string.inline_done_button);
Julia Reynolds8728d002019-04-19 15:46:13 -0400584 }
585
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500586 private void saveImportanceAndExitReason(@NotificationInfoAction int action) {
587 switch (action) {
588 case ACTION_UNDO:
589 mChosenImportance = mStartingChannelImportance;
590 break;
591 case ACTION_DELIVER_SILENTLY:
592 mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY;
Julia Reynolds8728d002019-04-19 15:46:13 -0400593 mChosenImportance = mWasShownHighPriority
594 ? IMPORTANCE_LOW : mStartingChannelImportance;
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500595 break;
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500596 default:
597 throw new IllegalArgumentException();
598 }
599 }
600
Julia Reynolds52a27372019-04-03 11:00:17 -0400601 // only used for blocking helper
Gus Prevas894d9152018-11-12 13:51:40 -0500602 private void swapContent(@NotificationInfoAction int action, boolean animate) {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500603 if (mExpandAnimation != null) {
604 mExpandAnimation.cancel();
605 }
Mady Mellor87d79452017-01-10 11:52:52 -0800606
Julia Reynolds52a27372019-04-03 11:00:17 -0400607 View blockingHelper = findViewById(R.id.blocking_helper);
Julia Reynoldse0341482018-03-08 14:42:50 -0500608 ViewGroup confirmation = findViewById(R.id.confirmation);
609 TextView confirmationText = findViewById(R.id.confirmation_text);
Julia Reynoldse0341482018-03-08 14:42:50 -0500610
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500611 saveImportanceAndExitReason(action);
612
Gus Prevas9abc5062018-10-31 16:11:04 -0400613 switch (action) {
Gus Prevas894d9152018-11-12 13:51:40 -0500614 case ACTION_UNDO:
Gus Prevas9abc5062018-10-31 16:11:04 -0400615 break;
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500616 case ACTION_DELIVER_SILENTLY:
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500617 confirmationText.setText(R.string.notification_channel_silenced);
618 break;
Gus Prevas9abc5062018-10-31 16:11:04 -0400619 default:
620 throw new IllegalArgumentException();
Julia Reynolds437cdb12018-01-03 12:27:24 -0500621 }
622
Gus Prevas894d9152018-11-12 13:51:40 -0500623 boolean isUndo = action == ACTION_UNDO;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500624
Julia Reynolds52a27372019-04-03 11:00:17 -0400625 blockingHelper.setVisibility(isUndo ? VISIBLE : GONE);
626 findViewById(R.id.channel_info).setVisibility(isUndo ? VISIBLE : GONE);
627 findViewById(R.id.header).setVisibility(isUndo ? VISIBLE : GONE);
Gus Prevas9abc5062018-10-31 16:11:04 -0400628 confirmation.setVisibility(isUndo ? GONE : VISIBLE);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500629
Gus Prevas894d9152018-11-12 13:51:40 -0500630 if (animate) {
Julia Reynolds52a27372019-04-03 11:00:17 -0400631 ObjectAnimator promptAnim = ObjectAnimator.ofFloat(blockingHelper, View.ALPHA,
632 blockingHelper.getAlpha(), isUndo ? 1f : 0f);
Gus Prevas894d9152018-11-12 13:51:40 -0500633 promptAnim.setInterpolator(isUndo ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
634 ObjectAnimator confirmAnim = ObjectAnimator.ofFloat(confirmation, View.ALPHA,
635 confirmation.getAlpha(), isUndo ? 0f : 1f);
636 confirmAnim.setInterpolator(isUndo ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500637
Gus Prevas894d9152018-11-12 13:51:40 -0500638 mExpandAnimation = new AnimatorSet();
639 mExpandAnimation.playTogether(promptAnim, confirmAnim);
640 mExpandAnimation.setDuration(150);
641 mExpandAnimation.addListener(new AnimatorListenerAdapter() {
642 boolean mCancelled = false;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500643
Gus Prevas894d9152018-11-12 13:51:40 -0500644 @Override
645 public void onAnimationCancel(Animator animation) {
646 mCancelled = true;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500647 }
Gus Prevas894d9152018-11-12 13:51:40 -0500648
649 @Override
650 public void onAnimationEnd(Animator animation) {
651 if (!mCancelled) {
Julia Reynolds52a27372019-04-03 11:00:17 -0400652 blockingHelper.setVisibility(isUndo ? VISIBLE : GONE);
Gus Prevas894d9152018-11-12 13:51:40 -0500653 confirmation.setVisibility(isUndo ? GONE : VISIBLE);
654 }
655 }
656 });
657 mExpandAnimation.start();
658 }
Rohan Shah142e2da2018-06-14 13:14:18 -0700659
660 // Since we're swapping/update the content, reset the timeout so the UI can't close
661 // immediately after the update.
662 if (mGutsContainer != null) {
663 mGutsContainer.resetFalsingCheck();
664 }
Mady Mellor87d79452017-01-10 11:52:52 -0800665 }
666
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400667 @Override
Gus Prevas9abc5062018-10-31 16:11:04 -0400668 public void onFinishedClosing() {
Gus Prevascaed15c2019-01-18 14:19:51 -0500669 if (mChosenImportance != null) {
670 mStartingChannelImportance = mChosenImportance;
Gus Prevas9abc5062018-10-31 16:11:04 -0400671 }
672 mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
673
Julia Reynolds52a27372019-04-03 11:00:17 -0400674 if (mIsForBlockingHelper) {
675 bindBlockingHelper();
676 } else {
677 bindInlineControls();
678 }
Will Brockmana399f022019-01-30 10:59:36 -0500679
680 mMetricsLogger.write(notificationControlsLogMaker().setType(MetricsEvent.TYPE_CLOSE));
Gus Prevas9abc5062018-10-31 16:11:04 -0400681 }
682
683 @Override
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400684 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
685 super.onInitializeAccessibilityEvent(event);
686 if (mGutsContainer != null &&
687 event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
688 if (mGutsContainer.isExposed()) {
689 event.getText().add(mContext.getString(
690 R.string.notification_channel_controls_opened_accessibility, mAppName));
691 } else {
692 event.getText().add(mContext.getString(
693 R.string.notification_channel_controls_closed_accessibility, mAppName));
694 }
695 }
696 }
697
Julia Reynolds3aedded2017-03-31 14:42:09 -0400698 private Intent getAppSettingsIntent(PackageManager pm, String packageName,
699 NotificationChannel channel, int id, String tag) {
700 Intent intent = new Intent(Intent.ACTION_MAIN)
701 .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
702 .setPackage(packageName);
703 final List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
704 intent,
705 PackageManager.MATCH_DEFAULT_ONLY
706 );
707 if (resolveInfos == null || resolveInfos.size() == 0 || resolveInfos.get(0) == null) {
708 return null;
709 }
710 final ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
711 intent.setClassName(activityInfo.packageName, activityInfo.name);
712 if (channel != null) {
713 intent.putExtra(Notification.EXTRA_CHANNEL_ID, channel.getId());
714 }
715 intent.putExtra(Notification.EXTRA_NOTIFICATION_ID, id);
716 intent.putExtra(Notification.EXTRA_NOTIFICATION_TAG, tag);
717 return intent;
718 }
719
Rohan Shahca0447e2018-03-30 15:18:27 -0700720 /**
721 * Closes the controls and commits the updated importance values (indirectly). If this view is
722 * being used to show the blocking helper, this will immediately dismiss the blocking helper and
723 * commit the updated importance.
724 *
725 * <p><b>Note,</b> this will only get called once the view is dismissing. This means that the
Julia Reynolds52a27372019-04-03 11:00:17 -0400726 * user does not have the ability to undo the action anymore. See
727 * {@link #swapContent(boolean, boolean)} for where undo is handled.
Rohan Shahca0447e2018-03-30 15:18:27 -0700728 */
Rohan Shah524cf7b2018-03-15 14:40:02 -0700729 @VisibleForTesting
Evan Laird7956fe82019-05-30 17:14:57 -0400730 void closeControls(View v, boolean save) {
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700731 int[] parentLoc = new int[2];
732 int[] targetLoc = new int[2];
733 mGutsContainer.getLocationOnScreen(parentLoc);
734 v.getLocationOnScreen(targetLoc);
735 final int centerX = v.getWidth() / 2;
736 final int centerY = v.getHeight() / 2;
737 final int x = targetLoc[0] - parentLoc[0] + centerX;
738 final int y = targetLoc[1] - parentLoc[1] + centerY;
Evan Laird7956fe82019-05-30 17:14:57 -0400739 mGutsContainer.closeControls(x, y, save, false /* force */);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500740 }
741
Mady Mellor87d79452017-01-10 11:52:52 -0800742 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800743 public void setGutsParent(NotificationGuts guts) {
744 mGutsContainer = guts;
Mady Mellor87d79452017-01-10 11:52:52 -0800745 }
746
747 @Override
Mady Mellor434180c2017-02-13 11:29:42 -0800748 public boolean willBeRemoved() {
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400749 return false;
Mady Mellor434180c2017-02-13 11:29:42 -0800750 }
751
752 @Override
Lucas Dupin9b08c012018-05-16 19:53:32 -0700753 public boolean shouldBeSaved() {
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400754 return mPressedApply;
Lucas Dupin9b08c012018-05-16 19:53:32 -0700755 }
756
757 @Override
Mady Mellor87d79452017-01-10 11:52:52 -0800758 public View getContentView() {
759 return this;
760 }
761
762 @Override
Mady Mellorc2dbe492017-03-30 13:22:03 -0700763 public boolean handleCloseControls(boolean save, boolean force) {
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500764 // Save regardless of the importance so we can lock the importance field if the user wants
765 // to keep getting notifications
766 if (save) {
Rohan Shahca0447e2018-03-30 15:18:27 -0700767 saveImportance();
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500768 }
Rohan Shahdd588c72018-05-09 20:32:15 -0700769 logBlockingHelperCounter(mExitReason);
Mady Mellor87d79452017-01-10 11:52:52 -0800770 return false;
771 }
Mady Mellore09fb702017-03-30 13:23:29 -0700772
773 @Override
774 public int getActualHeight() {
775 return getHeight();
776 }
Rohan Shahca0447e2018-03-30 15:18:27 -0700777
Gus Prevas533836a2018-09-24 17:15:32 -0400778 @VisibleForTesting
779 public boolean isAnimating() {
780 return mExpandAnimation != null && mExpandAnimation.isRunning();
781 }
782
Rohan Shahca0447e2018-03-30 15:18:27 -0700783 /**
784 * Runnable to either update the given channel (with a new importance value) or, if no channel
785 * is provided, update notifications enabled state for the package.
786 */
787 private static class UpdateImportanceRunnable implements Runnable {
788 private final INotificationManager mINotificationManager;
789 private final String mPackageName;
790 private final int mAppUid;
791 private final @Nullable NotificationChannel mChannelToUpdate;
792 private final int mCurrentImportance;
793 private final int mNewImportance;
794
795
796 public UpdateImportanceRunnable(INotificationManager notificationManager,
797 String packageName, int appUid, @Nullable NotificationChannel channelToUpdate,
798 int currentImportance, int newImportance) {
799 mINotificationManager = notificationManager;
800 mPackageName = packageName;
801 mAppUid = appUid;
802 mChannelToUpdate = channelToUpdate;
803 mCurrentImportance = currentImportance;
804 mNewImportance = newImportance;
805 }
806
807 @Override
808 public void run() {
809 try {
810 if (mChannelToUpdate != null) {
811 mChannelToUpdate.setImportance(mNewImportance);
812 mChannelToUpdate.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
813 mINotificationManager.updateNotificationChannelForPackage(
814 mPackageName, mAppUid, mChannelToUpdate);
815 } else {
816 // For notifications with more than one channel, update notification enabled
817 // state. If the importance was lowered, we disable notifications.
Rohan Shah590e1b22018-04-10 23:48:47 -0400818 mINotificationManager.setNotificationsEnabledWithImportanceLockForPackage(
Rohan Shahca0447e2018-03-30 15:18:27 -0700819 mPackageName, mAppUid, mNewImportance >= mCurrentImportance);
820 }
821 } catch (RemoteException e) {
822 Log.e(TAG, "Unable to update notification importance", e);
823 }
824 }
825 }
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200826
Will Brockmana399f022019-01-30 10:59:36 -0500827 /**
828 * Returns a LogMaker with all available notification information.
829 * Caller should set category, type, and maybe subtype, before passing it to mMetricsLogger.
830 * @return LogMaker
831 */
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200832 private LogMaker getLogMaker() {
Will Brockmana399f022019-01-30 10:59:36 -0500833 // The constructor requires a category, so also do it in the other branch for consistency.
834 return mSbn == null ? new LogMaker(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
835 : mSbn.getLogMaker().setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER);
836 }
837
838 /**
839 * Returns an initialized LogMaker for logging importance changes.
840 * The caller may override the type before passing it to mMetricsLogger.
841 * @return LogMaker
842 */
843 private LogMaker importanceChangeLogMaker() {
844 Integer chosenImportance =
845 mChosenImportance != null ? mChosenImportance : mStartingChannelImportance;
846 return getLogMaker().setCategory(MetricsEvent.ACTION_SAVE_IMPORTANCE)
847 .setType(MetricsEvent.TYPE_ACTION)
848 .setSubtype(chosenImportance - mStartingChannelImportance);
849 }
850
851 /**
852 * Returns an initialized LogMaker for logging open/close of the info display.
853 * The caller may override the type before passing it to mMetricsLogger.
854 * @return LogMaker
855 */
856 private LogMaker notificationControlsLogMaker() {
857 return getLogMaker().setCategory(MetricsEvent.ACTION_NOTE_CONTROLS)
858 .setType(MetricsEvent.TYPE_OPEN)
859 .setSubtype(mIsForBlockingHelper ? MetricsEvent.BLOCKING_HELPER_DISPLAY
860 : MetricsEvent.BLOCKING_HELPER_UNKNOWN);
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200861 }
Ned Burns369f1472019-05-22 21:07:26 -0400862
863 @Retention(SOURCE)
864 @IntDef({BEHAVIOR_ALERTING, BEHAVIOR_SILENT})
865 private @interface AlertingBehavior {}
866 private static final int BEHAVIOR_ALERTING = 0;
867 private static final int BEHAVIOR_SILENT = 1;
Mady Mellor87d79452017-01-10 11:52:52 -0800868}