blob: 148d83b5ab5c9f0565a77e12ac847436302b84b4 [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;
Ned Burns9512e0c2019-05-30 19:36:04 -040068import com.android.systemui.statusbar.notification.VisualStabilityManager;
Rohan Shah20790b82018-07-02 17:21:04 -070069import com.android.systemui.statusbar.notification.logging.NotificationCounters;
Mady Mellor87d79452017-01-10 11:52:52 -080070
Ned Burns369f1472019-05-22 21:07:26 -040071import java.lang.annotation.Retention;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050072import java.util.List;
Evan Laird47dc4542019-04-24 15:10:52 -040073import java.util.Set;
Mady Mellor87d79452017-01-10 11:52:52 -080074
75/**
Rohan Shahda5dcdd2018-04-27 17:21:50 -070076 * The guts of a notification revealed when performing a long press. This also houses the blocking
77 * helper affordance that allows a user to keep/stop notifications after swiping one away.
Mady Mellor87d79452017-01-10 11:52:52 -080078 */
Mady Mellor95d743c2017-01-10 12:05:27 -080079public class NotificationInfo extends LinearLayout implements NotificationGuts.GutsContent {
Mady Mellor87d79452017-01-10 11:52:52 -080080 private static final String TAG = "InfoGuts";
81
Gus Prevas894d9152018-11-12 13:51:40 -050082 @IntDef(prefix = { "ACTION_" }, value = {
83 ACTION_NONE,
84 ACTION_UNDO,
85 ACTION_TOGGLE_SILENT,
86 ACTION_BLOCK,
Gus Prevas9abc5062018-10-31 16:11:04 -040087 })
Gus Prevas894d9152018-11-12 13:51:40 -050088 public @interface NotificationInfoAction {
89 }
Gus Prevas9abc5062018-10-31 16:11:04 -040090
Gus Prevas894d9152018-11-12 13:51:40 -050091 public static final int ACTION_NONE = 0;
Nadia Benbernoub66516c2019-03-06 11:01:53 -050092 static final int ACTION_UNDO = 1;
Julia Reynolds52a27372019-04-03 11:00:17 -040093 // standard controls
Nadia Benbernoub66516c2019-03-06 11:01:53 -050094 static final int ACTION_TOGGLE_SILENT = 2;
Julia Reynolds52a27372019-04-03 11:00:17 -040095 // unused
Nadia Benbernoub66516c2019-03-06 11:01:53 -050096 static final int ACTION_BLOCK = 3;
Julia Reynolds52a27372019-04-03 11:00:17 -040097 // blocking helper
Nadia Benbernoub66516c2019-03-06 11:01:53 -050098 static final int ACTION_DELIVER_SILENTLY = 4;
Julia Reynolds52a27372019-04-03 11:00:17 -040099 // standard controls
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500100 private static final int ACTION_ALERT = 5;
Gus Prevas9abc5062018-10-31 16:11:04 -0400101
Ned Burns369f1472019-05-22 21:07:26 -0400102 private TextView mPriorityDescriptionView;
103 private TextView mSilentDescriptionView;
Dan Sandler0ed02292019-05-14 19:18:35 -0400104
Mady Mellor87d79452017-01-10 11:52:52 -0800105 private INotificationManager mINotificationManager;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500106 private PackageManager mPm;
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700107 private MetricsLogger mMetricsLogger;
Ned Burns9512e0c2019-05-30 19:36:04 -0400108 private VisualStabilityManager mVisualStabilityManager;
Evan Laird47dc4542019-04-24 15:10:52 -0400109 private ChannelEditorDialogController mChannelEditorDialogController;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500110
Rohan Shahca0447e2018-03-30 15:18:27 -0700111 private String mPackageName;
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400112 private String mAppName;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500113 private int mAppUid;
Julia Reynolds268647a2018-10-25 16:54:27 -0400114 private String mDelegatePkg;
Rohan Shahca0447e2018-03-30 15:18:27 -0700115 private int mNumUniqueChannelsInRow;
Evan Laird47dc4542019-04-24 15:10:52 -0400116 private Set<NotificationChannel> mUniqueChannelsInRow;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500117 private NotificationChannel mSingleNotificationChannel;
Gus Prevas9abc5062018-10-31 16:11:04 -0400118 private int mStartingChannelImportance;
Gus Prevascaed15c2019-01-18 14:19:51 -0500119 private boolean mWasShownHighPriority;
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400120 private boolean mPressedApply;
Evan Laird03cf3502019-05-31 16:46:48 -0400121 private boolean mPresentingChannelEditorDialog = false;
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400122
Gus Prevascaed15c2019-01-18 14:19:51 -0500123 /**
124 * The last importance level chosen by the user. Null if the user has not chosen an importance
125 * level; non-null once the user takes an action which indicates an explicit preference.
126 */
127 @Nullable private Integer mChosenImportance;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500128 private boolean mIsSingleDefaultChannel;
Rohan Shah63411fc2018-03-28 19:05:52 -0700129 private boolean mIsNonblockable;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500130 private StatusBarNotification mSbn;
131 private AnimatorSet mExpandAnimation;
Julia Reynolds35765d82018-08-17 11:39:19 -0400132 private boolean mIsDeviceProvisioned;
Mady Mellor87d79452017-01-10 11:52:52 -0800133
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400134 private CheckSaveListener mCheckSaveListener;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500135 private OnSettingsClickListener mOnSettingsClickListener;
Julia Reynolds3aedded2017-03-31 14:42:09 -0400136 private OnAppSettingsClickListener mAppSettingsClickListener;
Mady Mellor95d743c2017-01-10 12:05:27 -0800137 private NotificationGuts mGutsContainer;
Evan Laird47dc4542019-04-24 15:10:52 -0400138 private Drawable mPkgIcon;
Rohan Shah524cf7b2018-03-15 14:40:02 -0700139
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700140 /** Whether this view is being shown as part of the blocking helper. */
Rohan Shah524cf7b2018-03-15 14:40:02 -0700141 private boolean mIsForBlockingHelper;
Mady Mellor87d79452017-01-10 11:52:52 -0800142
Rohan Shahdd588c72018-05-09 20:32:15 -0700143 /**
144 * String that describes how the user exit or quit out of this view, also used as a counter tag.
145 */
146 private String mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700147
Julia Reynolds52a27372019-04-03 11:00:17 -0400148 // used by standard ui
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500149 private OnClickListener mOnAlert = v -> {
150 mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
151 mChosenImportance = IMPORTANCE_DEFAULT;
Ned Burns369f1472019-05-22 21:07:26 -0400152 applyAlertingBehavior(BEHAVIOR_ALERTING, true /* userTriggered */);
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500153 };
154
Julia Reynolds52a27372019-04-03 11:00:17 -0400155 // used by standard ui
156 private OnClickListener mOnSilent = v -> {
157 mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY;
158 mChosenImportance = IMPORTANCE_LOW;
Ned Burns369f1472019-05-22 21:07:26 -0400159 applyAlertingBehavior(BEHAVIOR_SILENT, true /* userTriggered */);
Julia Reynolds52a27372019-04-03 11:00:17 -0400160 };
161
162 // used by standard ui
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500163 private OnClickListener mOnDismissSettings = v -> {
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400164 mPressedApply = true;
Evan Laird7956fe82019-05-30 17:14:57 -0400165 closeControls(v, true);
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500166 };
167
Julia Reynolds52a27372019-04-03 11:00:17 -0400168 // used by blocking helper
169 private OnClickListener mOnKeepShowing = v -> {
170 mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
Evan Laird7956fe82019-05-30 17:14:57 -0400171 closeControls(v, true);
Julia Reynolds52a27372019-04-03 11:00:17 -0400172 mMetricsLogger.write(getLogMaker().setCategory(
173 MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
174 .setType(MetricsEvent.TYPE_ACTION)
175 .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_STAY_SILENT));
176 };
177
178 // used by blocking helper
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500179 private OnClickListener mOnDeliverSilently = v -> {
180 handleSaveImportance(
181 ACTION_DELIVER_SILENTLY, MetricsEvent.BLOCKING_HELPER_CLICK_STAY_SILENT);
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500182 };
183
184 private void handleSaveImportance(int action, int metricsSubtype) {
Gus Prevas533836a2018-09-24 17:15:32 -0400185 Runnable saveImportance = () -> {
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500186 saveImportanceAndExitReason(action);
Will Brockmana399f022019-01-30 10:59:36 -0500187 if (mIsForBlockingHelper) {
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500188 swapContent(action, true /* animate */);
Will Brockmana399f022019-01-30 10:59:36 -0500189 mMetricsLogger.write(getLogMaker()
190 .setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
191 .setType(MetricsEvent.TYPE_ACTION)
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500192 .setSubtype(metricsSubtype));
Will Brockmana399f022019-01-30 10:59:36 -0500193 }
Gus Prevas533836a2018-09-24 17:15:32 -0400194 };
195 if (mCheckSaveListener != null) {
196 mCheckSaveListener.checkSave(saveImportance, mSbn);
197 } else {
198 saveImportance.run();
199 }
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500200 }
Julia Reynolds437cdb12018-01-03 12:27:24 -0500201
202 private OnClickListener mOnUndo = v -> {
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700203 // Reset exit counter that we'll log and record an undo event separately (not an exit event)
Rohan Shahdd588c72018-05-09 20:32:15 -0700204 mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
Will Brockmana399f022019-01-30 10:59:36 -0500205 if (mIsForBlockingHelper) {
206 logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_UNDO);
207 mMetricsLogger.write(getLogMaker().setCategory(
208 MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
209 .setType(MetricsEvent.TYPE_DISMISS)
210 .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_UNDO));
211 } else {
Julia Reynolds52a27372019-04-03 11:00:17 -0400212 // TODO: this can't happen?
Will Brockmana399f022019-01-30 10:59:36 -0500213 mMetricsLogger.write(importanceChangeLogMaker().setType(MetricsEvent.TYPE_DISMISS));
214 }
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500215 saveImportanceAndExitReason(ACTION_UNDO);
Gus Prevas894d9152018-11-12 13:51:40 -0500216 swapContent(ACTION_UNDO, true /* animate */);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500217 };
218
Mady Mellor87d79452017-01-10 11:52:52 -0800219 public NotificationInfo(Context context, AttributeSet attrs) {
220 super(context, attrs);
221 }
222
Ned Burns369f1472019-05-22 21:07:26 -0400223 @Override
224 protected void onFinishInflate() {
225 super.onFinishInflate();
226
227 mPriorityDescriptionView = findViewById(R.id.alert_summary);
228 mSilentDescriptionView = findViewById(R.id.silence_summary);
229 }
230
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400231 // Specify a CheckSaveListener to override when/if the user's changes are committed.
232 public interface CheckSaveListener {
233 // Invoked when importance has changed and the NotificationInfo wants to try to save it.
234 // Listener should run saveImportance unless the change should be canceled.
Eliot Courtney47098cb2017-10-18 17:30:30 +0900235 void checkSave(Runnable saveImportance, StatusBarNotification sbn);
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400236 }
237
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500238 public interface OnSettingsClickListener {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500239 void onClick(View v, NotificationChannel channel, int appUid);
Mady Mellor87d79452017-01-10 11:52:52 -0800240 }
241
Julia Reynolds3aedded2017-03-31 14:42:09 -0400242 public interface OnAppSettingsClickListener {
243 void onClick(View v, Intent intent);
244 }
245
Rohan Shah524cf7b2018-03-15 14:40:02 -0700246 @VisibleForTesting
247 void bindNotification(
248 final PackageManager pm,
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500249 final INotificationManager iNotificationManager,
Ned Burns9512e0c2019-05-30 19:36:04 -0400250 final VisualStabilityManager visualStabilityManager,
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500251 final String pkg,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500252 final NotificationChannel notificationChannel,
Evan Laird47dc4542019-04-24 15:10:52 -0400253 final Set<NotificationChannel> uniqueChannelsInRow,
Julia Reynolds3aedded2017-03-31 14:42:09 -0400254 final StatusBarNotification sbn,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500255 final CheckSaveListener checkSaveListener,
256 final OnSettingsClickListener onSettingsClick,
257 final OnAppSettingsClickListener onAppSettingsClick,
Julia Reynolds35765d82018-08-17 11:39:19 -0400258 boolean isDeviceProvisioned,
Gus Prevas9abc5062018-10-31 16:11:04 -0400259 boolean isNonblockable,
Gus Prevascaed15c2019-01-18 14:19:51 -0500260 int importance,
261 boolean wasShownHighPriority)
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500262 throws RemoteException {
Ned Burns9512e0c2019-05-30 19:36:04 -0400263 bindNotification(pm, iNotificationManager, visualStabilityManager, pkg, notificationChannel,
Evan Laird47dc4542019-04-24 15:10:52 -0400264 uniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
Julia Reynolds35765d82018-08-17 11:39:19 -0400265 onAppSettingsClick, isDeviceProvisioned, isNonblockable,
Julia Reynolds52a27372019-04-03 11:00:17 -0400266 false /* isBlockingHelper */,
Gus Prevascaed15c2019-01-18 14:19:51 -0500267 importance, wasShownHighPriority);
Julia Reynolds0ef7d842018-01-24 17:50:31 -0500268 }
269
Rohan Shah524cf7b2018-03-15 14:40:02 -0700270 public void bindNotification(
271 PackageManager pm,
272 INotificationManager iNotificationManager,
Ned Burns9512e0c2019-05-30 19:36:04 -0400273 VisualStabilityManager visualStabilityManager,
Rohan Shah524cf7b2018-03-15 14:40:02 -0700274 String pkg,
275 NotificationChannel notificationChannel,
Evan Laird47dc4542019-04-24 15:10:52 -0400276 Set<NotificationChannel> uniqueChannelsInRow,
Rohan Shah524cf7b2018-03-15 14:40:02 -0700277 StatusBarNotification sbn,
278 CheckSaveListener checkSaveListener,
279 OnSettingsClickListener onSettingsClick,
280 OnAppSettingsClickListener onAppSettingsClick,
Julia Reynolds35765d82018-08-17 11:39:19 -0400281 boolean isDeviceProvisioned,
Rohan Shah63411fc2018-03-28 19:05:52 -0700282 boolean isNonblockable,
Rohan Shah524cf7b2018-03-15 14:40:02 -0700283 boolean isForBlockingHelper,
Gus Prevascaed15c2019-01-18 14:19:51 -0500284 int importance,
285 boolean wasShownHighPriority)
Rohan Shah524cf7b2018-03-15 14:40:02 -0700286 throws RemoteException {
Mady Mellor87d79452017-01-10 11:52:52 -0800287 mINotificationManager = iNotificationManager;
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700288 mMetricsLogger = Dependency.get(MetricsLogger.class);
Ned Burns9512e0c2019-05-30 19:36:04 -0400289 mVisualStabilityManager = visualStabilityManager;
Evan Laird47dc4542019-04-24 15:10:52 -0400290 mChannelEditorDialogController = Dependency.get(ChannelEditorDialogController.class);
Rohan Shahca0447e2018-03-30 15:18:27 -0700291 mPackageName = pkg;
Evan Laird47dc4542019-04-24 15:10:52 -0400292 mUniqueChannelsInRow = uniqueChannelsInRow;
293 mNumUniqueChannelsInRow = uniqueChannelsInRow.size();
Julia Reynolds3aedded2017-03-31 14:42:09 -0400294 mSbn = sbn;
295 mPm = pm;
296 mAppSettingsClickListener = onAppSettingsClick;
Rohan Shahca0447e2018-03-30 15:18:27 -0700297 mAppName = mPackageName;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500298 mCheckSaveListener = checkSaveListener;
299 mOnSettingsClickListener = onSettingsClick;
300 mSingleNotificationChannel = notificationChannel;
Gus Prevascaed15c2019-01-18 14:19:51 -0500301 mStartingChannelImportance = mSingleNotificationChannel.getImportance();
302 mWasShownHighPriority = wasShownHighPriority;
Rohan Shah63411fc2018-03-28 19:05:52 -0700303 mIsNonblockable = isNonblockable;
Rohan Shah524cf7b2018-03-15 14:40:02 -0700304 mIsForBlockingHelper = isForBlockingHelper;
Rohan Shahdbd64e72018-03-28 14:46:50 -0700305 mAppUid = mSbn.getUid();
Julia Reynolds268647a2018-10-25 16:54:27 -0400306 mDelegatePkg = mSbn.getOpPkg();
Julia Reynolds35765d82018-08-17 11:39:19 -0400307 mIsDeviceProvisioned = isDeviceProvisioned;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500308
Julia Reynolds437cdb12018-01-03 12:27:24 -0500309 int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400310 pkg, mAppUid, false /* includeDeleted */);
Rohan Shahca0447e2018-03-30 15:18:27 -0700311 if (mNumUniqueChannelsInRow == 0) {
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400312 throw new IllegalArgumentException("bindNotification requires at least one channel");
313 } else {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500314 // Special behavior for the Default channel if no other channels have been defined.
Rohan Shahca0447e2018-03-30 15:18:27 -0700315 mIsSingleDefaultChannel = mNumUniqueChannelsInRow == 1
Rohan Shahdbd64e72018-03-28 14:46:50 -0700316 && mSingleNotificationChannel.getId().equals(
317 NotificationChannel.DEFAULT_CHANNEL_ID)
318 && numTotalChannels == 1;
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400319 }
320
Julia Reynolds437cdb12018-01-03 12:27:24 -0500321 bindHeader();
Julia Reynolds52a27372019-04-03 11:00:17 -0400322 bindChannelDetails();
323
324 if (mIsForBlockingHelper) {
325 bindBlockingHelper();
326 } else {
327 bindInlineControls();
328 }
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200329
Will Brockmana399f022019-01-30 10:59:36 -0500330 mMetricsLogger.write(notificationControlsLogMaker());
Julia Reynolds437cdb12018-01-03 12:27:24 -0500331 }
332
Julia Reynolds52a27372019-04-03 11:00:17 -0400333 private void bindBlockingHelper() {
334 findViewById(R.id.inline_controls).setVisibility(GONE);
335 findViewById(R.id.blocking_helper).setVisibility(VISIBLE);
336
337 findViewById(R.id.undo).setOnClickListener(mOnUndo);
338
339 View turnOffButton = findViewById(R.id.blocking_helper_turn_off_notifications);
340 turnOffButton.setOnClickListener(getSettingsOnClickListener());
341 turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() ? VISIBLE : GONE);
342
343 TextView keepShowing = findViewById(R.id.keep_showing);
344 keepShowing.setOnClickListener(mOnKeepShowing);
345
346 View deliverSilently = findViewById(R.id.deliver_silently);
347 deliverSilently.setOnClickListener(mOnDeliverSilently);
348 }
349
350 private void bindInlineControls() {
351 findViewById(R.id.inline_controls).setVisibility(VISIBLE);
352 findViewById(R.id.blocking_helper).setVisibility(GONE);
353
354 if (mIsNonblockable) {
355 findViewById(R.id.non_configurable_text).setVisibility(VISIBLE);
356 findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE);
357 findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
Julia Reynolds8728d002019-04-19 15:46:13 -0400358 ((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button);
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400359 findViewById(R.id.turn_off_notifications).setVisibility(GONE);
Julia Reynolds52a27372019-04-03 11:00:17 -0400360 } else if (mNumUniqueChannelsInRow > 1) {
361 findViewById(R.id.non_configurable_text).setVisibility(GONE);
362 findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
363 findViewById(R.id.non_configurable_multichannel_text).setVisibility(VISIBLE);
364 } else {
365 findViewById(R.id.non_configurable_text).setVisibility(GONE);
366 findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE);
367 findViewById(R.id.interruptiveness_settings).setVisibility(VISIBLE);
368 }
369
370 View turnOffButton = findViewById(R.id.turn_off_notifications);
Evan Laird47dc4542019-04-24 15:10:52 -0400371 turnOffButton.setOnClickListener(getTurnOffNotificationsClickListener());
Julia Reynolds52a27372019-04-03 11:00:17 -0400372 turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() && !mIsNonblockable
373 ? VISIBLE : GONE);
374
375 View done = findViewById(R.id.done);
376 done.setOnClickListener(mOnDismissSettings);
377
378
Julia Reynolds8728d002019-04-19 15:46:13 -0400379 View silent = findViewById(R.id.silence);
380 View alert = findViewById(R.id.alert);
Julia Reynolds52a27372019-04-03 11:00:17 -0400381 silent.setOnClickListener(mOnSilent);
382 alert.setOnClickListener(mOnAlert);
383
Ned Burns369f1472019-05-22 21:07:26 -0400384 applyAlertingBehavior(
385 mWasShownHighPriority ? BEHAVIOR_ALERTING : BEHAVIOR_SILENT,
386 false /* userTriggered */);
Julia Reynolds52a27372019-04-03 11:00:17 -0400387 }
388
389 private void bindHeader() {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500390 // Package name
Evan Laird47dc4542019-04-24 15:10:52 -0400391 mPkgIcon = null;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500392 ApplicationInfo info;
393 try {
Rohan Shahca0447e2018-03-30 15:18:27 -0700394 info = mPm.getApplicationInfo(
395 mPackageName,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500396 PackageManager.MATCH_UNINSTALLED_PACKAGES
397 | PackageManager.MATCH_DISABLED_COMPONENTS
398 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
399 | PackageManager.MATCH_DIRECT_BOOT_AWARE);
400 if (info != null) {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500401 mAppName = String.valueOf(mPm.getApplicationLabel(info));
Evan Laird47dc4542019-04-24 15:10:52 -0400402 mPkgIcon = mPm.getApplicationIcon(info);
Geoffrey Pitschee8c81e2017-03-23 11:38:56 -0400403 }
Julia Reynolds437cdb12018-01-03 12:27:24 -0500404 } catch (PackageManager.NameNotFoundException e) {
405 // app is gone, just show package name and generic icon
Evan Laird47dc4542019-04-24 15:10:52 -0400406 mPkgIcon = mPm.getDefaultActivityIcon();
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500407 }
Evan Laird47dc4542019-04-24 15:10:52 -0400408 ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(mPkgIcon);
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400409 ((TextView) findViewById(R.id.pkgname)).setText(mAppName);
Mady Mellor87d79452017-01-10 11:52:52 -0800410
Julia Reynolds268647a2018-10-25 16:54:27 -0400411 // Delegate
412 bindDelegate();
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000413
Julia Reynolds52a27372019-04-03 11:00:17 -0400414 // Set up app settings link (i.e. Customize)
415 View settingsLinkView = findViewById(R.id.app_settings);
416 Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName,
417 mSingleNotificationChannel,
418 mSbn.getId(), mSbn.getTag());
419 if (settingsIntent != null
420 && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
421 settingsLinkView.setVisibility(VISIBLE);
422 settingsLinkView.setOnClickListener((View view) -> {
423 mAppSettingsClickListener.onClick(view, settingsIntent);
424 });
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000425 } else {
Julia Reynolds52a27372019-04-03 11:00:17 -0400426 settingsLinkView.setVisibility(View.GONE);
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000427 }
Julia Reynolds52a27372019-04-03 11:00:17 -0400428
429 // System Settings button.
430 final View settingsButton = findViewById(R.id.info);
431 settingsButton.setOnClickListener(getSettingsOnClickListener());
432 settingsButton.setVisibility(settingsButton.hasOnClickListeners() ? VISIBLE : GONE);
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000433 }
434
Julia Reynolds52a27372019-04-03 11:00:17 -0400435 private OnClickListener getSettingsOnClickListener() {
436 if (mAppUid >= 0 && mOnSettingsClickListener != null && mIsDeviceProvisioned) {
437 final int appUidF = mAppUid;
438 return ((View view) -> {
439 logBlockingHelperCounter(
440 NotificationCounters.BLOCKING_HELPER_NOTIF_SETTINGS);
441 mOnSettingsClickListener.onClick(view,
442 mNumUniqueChannelsInRow > 1 ? null : mSingleNotificationChannel,
443 appUidF);
444 });
445 }
446 return null;
447 }
448
Evan Laird47dc4542019-04-24 15:10:52 -0400449 private OnClickListener getTurnOffNotificationsClickListener() {
450 return ((View view) -> {
Evan Laird03cf3502019-05-31 16:46:48 -0400451 if (!mPresentingChannelEditorDialog && mChannelEditorDialogController != null) {
452 mPresentingChannelEditorDialog = true;
453
Evan Laird47dc4542019-04-24 15:10:52 -0400454 mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid,
455 mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener);
Evan Laird03cf3502019-05-31 16:46:48 -0400456 mChannelEditorDialogController.setOnFinishListener(() -> {
457 mPresentingChannelEditorDialog = false;
458 closeControls(this, false);
459 });
Evan Laird47dc4542019-04-24 15:10:52 -0400460 mChannelEditorDialogController.show();
461 }
462 });
463 }
464
Julia Reynolds52a27372019-04-03 11:00:17 -0400465 private void bindChannelDetails() throws RemoteException {
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000466 bindName();
Julia Reynolds268647a2018-10-25 16:54:27 -0400467 bindGroup();
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000468 }
469
470 private void bindName() {
471 final TextView channelName = findViewById(R.id.channel_name);
472 if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) {
473 channelName.setVisibility(View.GONE);
474 } else {
475 channelName.setText(mSingleNotificationChannel.getName());
476 }
Julia Reynoldsac98aea2018-10-25 16:54:27 -0400477 }
478
Julia Reynolds268647a2018-10-25 16:54:27 -0400479 private void bindDelegate() {
480 TextView delegateView = findViewById(R.id.delegate_name);
481 TextView dividerView = findViewById(R.id.pkg_divider);
482
483 CharSequence delegatePkg = null;
484 if (!TextUtils.equals(mPackageName, mDelegatePkg)) {
485 // this notification was posted by a delegate!
Julia Reynolds268647a2018-10-25 16:54:27 -0400486 delegateView.setVisibility(View.VISIBLE);
487 dividerView.setVisibility(View.VISIBLE);
488 } else {
489 delegateView.setVisibility(View.GONE);
490 dividerView.setVisibility(View.GONE);
491 }
492 }
493
494 private void bindGroup() throws RemoteException {
495 // Set group information if this channel has an associated group.
496 CharSequence groupName = null;
497 if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
498 final NotificationChannelGroup notificationChannelGroup =
499 mINotificationManager.getNotificationChannelGroupForPackage(
500 mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
501 if (notificationChannelGroup != null) {
502 groupName = notificationChannelGroup.getName();
503 }
504 }
505 TextView groupNameView = findViewById(R.id.group_name);
Julia Reynolds268647a2018-10-25 16:54:27 -0400506 if (groupName != null) {
507 groupNameView.setText(groupName);
508 groupNameView.setVisibility(View.VISIBLE);
Julia Reynolds268647a2018-10-25 16:54:27 -0400509 } else {
510 groupNameView.setVisibility(View.GONE);
Julia Reynolds268647a2018-10-25 16:54:27 -0400511 }
512 }
513
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500514
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700515 @VisibleForTesting
516 void logBlockingHelperCounter(String counterTag) {
517 if (mIsForBlockingHelper) {
518 mMetricsLogger.count(counterTag, 1);
519 }
520 }
521
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500522 private void saveImportance() {
Gus Prevas894d9152018-11-12 13:51:40 -0500523 if (!mIsNonblockable
524 || mExitReason != NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS) {
Gus Prevascaed15c2019-01-18 14:19:51 -0500525 if (mChosenImportance == null) {
526 mChosenImportance = mStartingChannelImportance;
527 }
Gus Prevas533836a2018-09-24 17:15:32 -0400528 updateImportance();
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500529 }
Rohan Shahca0447e2018-03-30 15:18:27 -0700530 }
531
532 /**
533 * Commits the updated importance values on the background thread.
534 */
535 private void updateImportance() {
Gus Prevascaed15c2019-01-18 14:19:51 -0500536 if (mChosenImportance != null) {
537 mMetricsLogger.write(importanceChangeLogMaker());
Rohan Shahca0447e2018-03-30 15:18:27 -0700538
Julia Reynolds8728d002019-04-19 15:46:13 -0400539 int newImportance = mChosenImportance;
540 if (mStartingChannelImportance != IMPORTANCE_UNSPECIFIED) {
541 if ((mWasShownHighPriority && mChosenImportance >= IMPORTANCE_DEFAULT)
542 || (!mWasShownHighPriority && mChosenImportance < IMPORTANCE_DEFAULT)) {
543 newImportance = mStartingChannelImportance;
544 }
545 }
546
Gus Prevascaed15c2019-01-18 14:19:51 -0500547 Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
548 bgHandler.post(
549 new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
550 mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
Julia Reynolds8728d002019-04-19 15:46:13 -0400551 mStartingChannelImportance, newImportance));
Ned Burns9512e0c2019-05-30 19:36:04 -0400552 mVisualStabilityManager.temporarilyAllowReordering();
Gus Prevascaed15c2019-01-18 14:19:51 -0500553 }
Mady Mellor87d79452017-01-10 11:52:52 -0800554 }
555
Ned Burns369f1472019-05-22 21:07:26 -0400556 private void applyAlertingBehavior(@AlertingBehavior int behavior, boolean userTriggered) {
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400557 if (userTriggered) {
Ned Burns369f1472019-05-22 21:07:26 -0400558 TransitionSet transition = new TransitionSet();
559 transition.setOrdering(TransitionSet.ORDERING_TOGETHER);
560 transition.addTransition(new Fade(Fade.OUT))
561 .addTransition(new ChangeBounds())
562 .addTransition(
563 new Fade(Fade.IN)
564 .setStartDelay(150)
565 .setDuration(200)
566 .setInterpolator(FAST_OUT_SLOW_IN));
567 transition.setDuration(350);
568 transition.setInterpolator(FAST_OUT_SLOW_IN);
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400569 TransitionManager.beginDelayedTransition(this, transition);
570 }
Ned Burns369f1472019-05-22 21:07:26 -0400571
572 View alert = findViewById(R.id.alert);
573 View silence = findViewById(R.id.silence);
574
575 switch (behavior) {
576 case BEHAVIOR_ALERTING:
Ned Burns369f1472019-05-22 21:07:26 -0400577 mPriorityDescriptionView.setVisibility(VISIBLE);
578 mSilentDescriptionView.setVisibility(GONE);
Julia Reynoldsdaab21a2019-06-05 14:30:05 -0400579 post(() -> {
580 alert.setSelected(true);
581 silence.setSelected(false);
582 });
Ned Burns369f1472019-05-22 21:07:26 -0400583 break;
584 case BEHAVIOR_SILENT:
Julia Reynoldsdaab21a2019-06-05 14:30:05 -0400585
Ned Burns369f1472019-05-22 21:07:26 -0400586 mSilentDescriptionView.setVisibility(VISIBLE);
587 mPriorityDescriptionView.setVisibility(GONE);
Julia Reynoldsdaab21a2019-06-05 14:30:05 -0400588 post(() -> {
589 alert.setSelected(false);
590 silence.setSelected(true);
591 });
Ned Burns369f1472019-05-22 21:07:26 -0400592 break;
593 default:
594 throw new IllegalArgumentException("Unrecognized alerting behavior: " + behavior);
Julia Reynolds8728d002019-04-19 15:46:13 -0400595 }
Ned Burns369f1472019-05-22 21:07:26 -0400596
597 boolean isAChange = mWasShownHighPriority != (behavior == BEHAVIOR_ALERTING);
598 TextView done = findViewById(R.id.done);
599 done.setText(isAChange ? R.string.inline_ok_button : R.string.inline_done_button);
Julia Reynolds8728d002019-04-19 15:46:13 -0400600 }
601
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500602 private void saveImportanceAndExitReason(@NotificationInfoAction int action) {
603 switch (action) {
604 case ACTION_UNDO:
605 mChosenImportance = mStartingChannelImportance;
606 break;
607 case ACTION_DELIVER_SILENTLY:
608 mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY;
Julia Reynolds8728d002019-04-19 15:46:13 -0400609 mChosenImportance = mWasShownHighPriority
610 ? IMPORTANCE_LOW : mStartingChannelImportance;
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500611 break;
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500612 default:
613 throw new IllegalArgumentException();
614 }
615 }
616
Julia Reynolds52a27372019-04-03 11:00:17 -0400617 // only used for blocking helper
Gus Prevas894d9152018-11-12 13:51:40 -0500618 private void swapContent(@NotificationInfoAction int action, boolean animate) {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500619 if (mExpandAnimation != null) {
620 mExpandAnimation.cancel();
621 }
Mady Mellor87d79452017-01-10 11:52:52 -0800622
Julia Reynolds52a27372019-04-03 11:00:17 -0400623 View blockingHelper = findViewById(R.id.blocking_helper);
Julia Reynoldse0341482018-03-08 14:42:50 -0500624 ViewGroup confirmation = findViewById(R.id.confirmation);
625 TextView confirmationText = findViewById(R.id.confirmation_text);
Julia Reynoldse0341482018-03-08 14:42:50 -0500626
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500627 saveImportanceAndExitReason(action);
628
Gus Prevas9abc5062018-10-31 16:11:04 -0400629 switch (action) {
Gus Prevas894d9152018-11-12 13:51:40 -0500630 case ACTION_UNDO:
Gus Prevas9abc5062018-10-31 16:11:04 -0400631 break;
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500632 case ACTION_DELIVER_SILENTLY:
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500633 confirmationText.setText(R.string.notification_channel_silenced);
634 break;
Gus Prevas9abc5062018-10-31 16:11:04 -0400635 default:
636 throw new IllegalArgumentException();
Julia Reynolds437cdb12018-01-03 12:27:24 -0500637 }
638
Gus Prevas894d9152018-11-12 13:51:40 -0500639 boolean isUndo = action == ACTION_UNDO;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500640
Julia Reynolds52a27372019-04-03 11:00:17 -0400641 blockingHelper.setVisibility(isUndo ? VISIBLE : GONE);
642 findViewById(R.id.channel_info).setVisibility(isUndo ? VISIBLE : GONE);
643 findViewById(R.id.header).setVisibility(isUndo ? VISIBLE : GONE);
Gus Prevas9abc5062018-10-31 16:11:04 -0400644 confirmation.setVisibility(isUndo ? GONE : VISIBLE);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500645
Gus Prevas894d9152018-11-12 13:51:40 -0500646 if (animate) {
Julia Reynolds52a27372019-04-03 11:00:17 -0400647 ObjectAnimator promptAnim = ObjectAnimator.ofFloat(blockingHelper, View.ALPHA,
648 blockingHelper.getAlpha(), isUndo ? 1f : 0f);
Gus Prevas894d9152018-11-12 13:51:40 -0500649 promptAnim.setInterpolator(isUndo ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
650 ObjectAnimator confirmAnim = ObjectAnimator.ofFloat(confirmation, View.ALPHA,
651 confirmation.getAlpha(), isUndo ? 0f : 1f);
652 confirmAnim.setInterpolator(isUndo ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500653
Gus Prevas894d9152018-11-12 13:51:40 -0500654 mExpandAnimation = new AnimatorSet();
655 mExpandAnimation.playTogether(promptAnim, confirmAnim);
656 mExpandAnimation.setDuration(150);
657 mExpandAnimation.addListener(new AnimatorListenerAdapter() {
658 boolean mCancelled = false;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500659
Gus Prevas894d9152018-11-12 13:51:40 -0500660 @Override
661 public void onAnimationCancel(Animator animation) {
662 mCancelled = true;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500663 }
Gus Prevas894d9152018-11-12 13:51:40 -0500664
665 @Override
666 public void onAnimationEnd(Animator animation) {
667 if (!mCancelled) {
Julia Reynolds52a27372019-04-03 11:00:17 -0400668 blockingHelper.setVisibility(isUndo ? VISIBLE : GONE);
Gus Prevas894d9152018-11-12 13:51:40 -0500669 confirmation.setVisibility(isUndo ? GONE : VISIBLE);
670 }
671 }
672 });
673 mExpandAnimation.start();
674 }
Rohan Shah142e2da2018-06-14 13:14:18 -0700675
676 // Since we're swapping/update the content, reset the timeout so the UI can't close
677 // immediately after the update.
678 if (mGutsContainer != null) {
679 mGutsContainer.resetFalsingCheck();
680 }
Mady Mellor87d79452017-01-10 11:52:52 -0800681 }
682
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400683 @Override
Gus Prevas9abc5062018-10-31 16:11:04 -0400684 public void onFinishedClosing() {
Gus Prevascaed15c2019-01-18 14:19:51 -0500685 if (mChosenImportance != null) {
686 mStartingChannelImportance = mChosenImportance;
Gus Prevas9abc5062018-10-31 16:11:04 -0400687 }
688 mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
689
Julia Reynolds52a27372019-04-03 11:00:17 -0400690 if (mIsForBlockingHelper) {
691 bindBlockingHelper();
692 } else {
693 bindInlineControls();
694 }
Will Brockmana399f022019-01-30 10:59:36 -0500695
696 mMetricsLogger.write(notificationControlsLogMaker().setType(MetricsEvent.TYPE_CLOSE));
Gus Prevas9abc5062018-10-31 16:11:04 -0400697 }
698
699 @Override
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400700 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
701 super.onInitializeAccessibilityEvent(event);
702 if (mGutsContainer != null &&
703 event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
704 if (mGutsContainer.isExposed()) {
705 event.getText().add(mContext.getString(
706 R.string.notification_channel_controls_opened_accessibility, mAppName));
707 } else {
708 event.getText().add(mContext.getString(
709 R.string.notification_channel_controls_closed_accessibility, mAppName));
710 }
711 }
712 }
713
Julia Reynolds3aedded2017-03-31 14:42:09 -0400714 private Intent getAppSettingsIntent(PackageManager pm, String packageName,
715 NotificationChannel channel, int id, String tag) {
716 Intent intent = new Intent(Intent.ACTION_MAIN)
717 .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
718 .setPackage(packageName);
719 final List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
720 intent,
721 PackageManager.MATCH_DEFAULT_ONLY
722 );
723 if (resolveInfos == null || resolveInfos.size() == 0 || resolveInfos.get(0) == null) {
724 return null;
725 }
726 final ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
727 intent.setClassName(activityInfo.packageName, activityInfo.name);
728 if (channel != null) {
729 intent.putExtra(Notification.EXTRA_CHANNEL_ID, channel.getId());
730 }
731 intent.putExtra(Notification.EXTRA_NOTIFICATION_ID, id);
732 intent.putExtra(Notification.EXTRA_NOTIFICATION_TAG, tag);
733 return intent;
734 }
735
Rohan Shahca0447e2018-03-30 15:18:27 -0700736 /**
737 * Closes the controls and commits the updated importance values (indirectly). If this view is
738 * being used to show the blocking helper, this will immediately dismiss the blocking helper and
739 * commit the updated importance.
740 *
741 * <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 -0400742 * user does not have the ability to undo the action anymore. See
743 * {@link #swapContent(boolean, boolean)} for where undo is handled.
Rohan Shahca0447e2018-03-30 15:18:27 -0700744 */
Rohan Shah524cf7b2018-03-15 14:40:02 -0700745 @VisibleForTesting
Evan Laird7956fe82019-05-30 17:14:57 -0400746 void closeControls(View v, boolean save) {
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700747 int[] parentLoc = new int[2];
748 int[] targetLoc = new int[2];
749 mGutsContainer.getLocationOnScreen(parentLoc);
750 v.getLocationOnScreen(targetLoc);
751 final int centerX = v.getWidth() / 2;
752 final int centerY = v.getHeight() / 2;
753 final int x = targetLoc[0] - parentLoc[0] + centerX;
754 final int y = targetLoc[1] - parentLoc[1] + centerY;
Evan Laird7956fe82019-05-30 17:14:57 -0400755 mGutsContainer.closeControls(x, y, save, false /* force */);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500756 }
757
Mady Mellor87d79452017-01-10 11:52:52 -0800758 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800759 public void setGutsParent(NotificationGuts guts) {
760 mGutsContainer = guts;
Mady Mellor87d79452017-01-10 11:52:52 -0800761 }
762
763 @Override
Mady Mellor434180c2017-02-13 11:29:42 -0800764 public boolean willBeRemoved() {
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400765 return false;
Mady Mellor434180c2017-02-13 11:29:42 -0800766 }
767
768 @Override
Lucas Dupin9b08c012018-05-16 19:53:32 -0700769 public boolean shouldBeSaved() {
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400770 return mPressedApply;
Lucas Dupin9b08c012018-05-16 19:53:32 -0700771 }
772
773 @Override
Mady Mellor87d79452017-01-10 11:52:52 -0800774 public View getContentView() {
775 return this;
776 }
777
778 @Override
Mady Mellorc2dbe492017-03-30 13:22:03 -0700779 public boolean handleCloseControls(boolean save, boolean force) {
Evan Laird03cf3502019-05-31 16:46:48 -0400780 if (mPresentingChannelEditorDialog && mChannelEditorDialogController != null) {
781 mPresentingChannelEditorDialog = false;
782 // No need for the finish listener because we're closing
783 mChannelEditorDialogController.setOnFinishListener(null);
784 mChannelEditorDialogController.close();
785 }
786
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500787 // Save regardless of the importance so we can lock the importance field if the user wants
788 // to keep getting notifications
789 if (save) {
Rohan Shahca0447e2018-03-30 15:18:27 -0700790 saveImportance();
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500791 }
Rohan Shahdd588c72018-05-09 20:32:15 -0700792 logBlockingHelperCounter(mExitReason);
Mady Mellor87d79452017-01-10 11:52:52 -0800793 return false;
794 }
Mady Mellore09fb702017-03-30 13:23:29 -0700795
796 @Override
797 public int getActualHeight() {
798 return getHeight();
799 }
Rohan Shahca0447e2018-03-30 15:18:27 -0700800
Gus Prevas533836a2018-09-24 17:15:32 -0400801 @VisibleForTesting
802 public boolean isAnimating() {
803 return mExpandAnimation != null && mExpandAnimation.isRunning();
804 }
805
Rohan Shahca0447e2018-03-30 15:18:27 -0700806 /**
807 * Runnable to either update the given channel (with a new importance value) or, if no channel
808 * is provided, update notifications enabled state for the package.
809 */
810 private static class UpdateImportanceRunnable implements Runnable {
811 private final INotificationManager mINotificationManager;
812 private final String mPackageName;
813 private final int mAppUid;
814 private final @Nullable NotificationChannel mChannelToUpdate;
815 private final int mCurrentImportance;
816 private final int mNewImportance;
817
818
819 public UpdateImportanceRunnable(INotificationManager notificationManager,
820 String packageName, int appUid, @Nullable NotificationChannel channelToUpdate,
821 int currentImportance, int newImportance) {
822 mINotificationManager = notificationManager;
823 mPackageName = packageName;
824 mAppUid = appUid;
825 mChannelToUpdate = channelToUpdate;
826 mCurrentImportance = currentImportance;
827 mNewImportance = newImportance;
828 }
829
830 @Override
831 public void run() {
832 try {
833 if (mChannelToUpdate != null) {
834 mChannelToUpdate.setImportance(mNewImportance);
835 mChannelToUpdate.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
836 mINotificationManager.updateNotificationChannelForPackage(
837 mPackageName, mAppUid, mChannelToUpdate);
838 } else {
839 // For notifications with more than one channel, update notification enabled
840 // state. If the importance was lowered, we disable notifications.
Rohan Shah590e1b22018-04-10 23:48:47 -0400841 mINotificationManager.setNotificationsEnabledWithImportanceLockForPackage(
Rohan Shahca0447e2018-03-30 15:18:27 -0700842 mPackageName, mAppUid, mNewImportance >= mCurrentImportance);
843 }
844 } catch (RemoteException e) {
845 Log.e(TAG, "Unable to update notification importance", e);
846 }
847 }
848 }
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200849
Will Brockmana399f022019-01-30 10:59:36 -0500850 /**
851 * Returns a LogMaker with all available notification information.
852 * Caller should set category, type, and maybe subtype, before passing it to mMetricsLogger.
853 * @return LogMaker
854 */
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200855 private LogMaker getLogMaker() {
Will Brockmana399f022019-01-30 10:59:36 -0500856 // The constructor requires a category, so also do it in the other branch for consistency.
857 return mSbn == null ? new LogMaker(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
858 : mSbn.getLogMaker().setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER);
859 }
860
861 /**
862 * Returns an initialized LogMaker for logging importance changes.
863 * The caller may override the type before passing it to mMetricsLogger.
864 * @return LogMaker
865 */
866 private LogMaker importanceChangeLogMaker() {
867 Integer chosenImportance =
868 mChosenImportance != null ? mChosenImportance : mStartingChannelImportance;
869 return getLogMaker().setCategory(MetricsEvent.ACTION_SAVE_IMPORTANCE)
870 .setType(MetricsEvent.TYPE_ACTION)
871 .setSubtype(chosenImportance - mStartingChannelImportance);
872 }
873
874 /**
875 * Returns an initialized LogMaker for logging open/close of the info display.
876 * The caller may override the type before passing it to mMetricsLogger.
877 * @return LogMaker
878 */
879 private LogMaker notificationControlsLogMaker() {
880 return getLogMaker().setCategory(MetricsEvent.ACTION_NOTE_CONTROLS)
881 .setType(MetricsEvent.TYPE_OPEN)
882 .setSubtype(mIsForBlockingHelper ? MetricsEvent.BLOCKING_HELPER_DISPLAY
883 : MetricsEvent.BLOCKING_HELPER_UNKNOWN);
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200884 }
Ned Burns369f1472019-05-22 21:07:26 -0400885
886 @Retention(SOURCE)
887 @IntDef({BEHAVIOR_ALERTING, BEHAVIOR_SILENT})
888 private @interface AlertingBehavior {}
889 private static final int BEHAVIOR_ALERTING = 0;
890 private static final int BEHAVIOR_SILENT = 1;
Mady Mellor87d79452017-01-10 11:52:52 -0800891}