blob: 942f56689170d344180acd55677c489933369d4c [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
Julia Reynolds437cdb12018-01-03 12:27:24 -050023import android.animation.Animator;
24import android.animation.AnimatorListenerAdapter;
25import android.animation.AnimatorSet;
26import android.animation.ObjectAnimator;
Gus Prevas9abc5062018-10-31 16:11:04 -040027import android.annotation.IntDef;
Rohan Shahca0447e2018-03-30 15:18:27 -070028import android.annotation.Nullable;
Mady Mellor87d79452017-01-10 11:52:52 -080029import android.app.INotificationManager;
Julia Reynolds3aedded2017-03-31 14:42:09 -040030import android.app.Notification;
Mady Mellor87d79452017-01-10 11:52:52 -080031import android.app.NotificationChannel;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -050032import android.app.NotificationChannelGroup;
Mady Mellor87d79452017-01-10 11:52:52 -080033import android.content.Context;
Julia Reynolds3aedded2017-03-31 14:42:09 -040034import android.content.Intent;
35import android.content.pm.ActivityInfo;
Mady Mellor87d79452017-01-10 11:52:52 -080036import android.content.pm.ApplicationInfo;
Mady Mellor87d79452017-01-10 11:52:52 -080037import android.content.pm.PackageManager;
Julia Reynolds3aedded2017-03-31 14:42:09 -040038import android.content.pm.ResolveInfo;
Mady Mellor87d79452017-01-10 11:52:52 -080039import android.graphics.drawable.Drawable;
William Brockman75cf66a2018-12-21 13:25:01 -050040import android.metrics.LogMaker;
Rohan Shahca0447e2018-03-30 15:18:27 -070041import android.os.Handler;
Mady Mellor87d79452017-01-10 11:52:52 -080042import android.os.RemoteException;
Julia Reynolds4d1dd792019-04-29 11:48:29 -040043import android.provider.Settings;
Julia Reynolds3aedded2017-03-31 14:42:09 -040044import android.service.notification.StatusBarNotification;
45import android.text.TextUtils;
Julia Reynolds4d1dd792019-04-29 11:48:29 -040046import android.transition.AutoTransition;
47import android.transition.TransitionManager;
Mady Mellor87d79452017-01-10 11:52:52 -080048import android.util.AttributeSet;
Rohan Shahca0447e2018-03-30 15:18:27 -070049import android.util.Log;
Mady Mellor87d79452017-01-10 11:52:52 -080050import android.view.View;
Julia Reynoldse0341482018-03-08 14:42:50 -050051import android.view.ViewGroup;
Geoffrey Pitschd94e7882017-04-06 09:52:11 -040052import android.view.accessibility.AccessibilityEvent;
Mady Mellor87d79452017-01-10 11:52:52 -080053import android.widget.ImageView;
54import android.widget.LinearLayout;
Mady Mellor87d79452017-01-10 11:52:52 -080055import android.widget.TextView;
56
Rohan Shah524cf7b2018-03-15 14:40:02 -070057import com.android.internal.annotations.VisibleForTesting;
Mady Mellor87d79452017-01-10 11:52:52 -080058import com.android.internal.logging.MetricsLogger;
59import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Rohan Shah524cf7b2018-03-15 14:40:02 -070060import com.android.systemui.Dependency;
Julia Reynolds437cdb12018-01-03 12:27:24 -050061import com.android.systemui.Interpolators;
Mady Mellor87d79452017-01-10 11:52:52 -080062import com.android.systemui.R;
Rohan Shah20790b82018-07-02 17:21:04 -070063import com.android.systemui.statusbar.notification.logging.NotificationCounters;
Mady Mellor87d79452017-01-10 11:52:52 -080064
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050065import java.util.List;
Evan Laird47dc4542019-04-24 15:10:52 -040066import java.util.Set;
Mady Mellor87d79452017-01-10 11:52:52 -080067
68/**
Rohan Shahda5dcdd2018-04-27 17:21:50 -070069 * The guts of a notification revealed when performing a long press. This also houses the blocking
70 * helper affordance that allows a user to keep/stop notifications after swiping one away.
Mady Mellor87d79452017-01-10 11:52:52 -080071 */
Mady Mellor95d743c2017-01-10 12:05:27 -080072public class NotificationInfo extends LinearLayout implements NotificationGuts.GutsContent {
Mady Mellor87d79452017-01-10 11:52:52 -080073 private static final String TAG = "InfoGuts";
74
Gus Prevas894d9152018-11-12 13:51:40 -050075 @IntDef(prefix = { "ACTION_" }, value = {
76 ACTION_NONE,
77 ACTION_UNDO,
78 ACTION_TOGGLE_SILENT,
79 ACTION_BLOCK,
Gus Prevas9abc5062018-10-31 16:11:04 -040080 })
Gus Prevas894d9152018-11-12 13:51:40 -050081 public @interface NotificationInfoAction {
82 }
Gus Prevas9abc5062018-10-31 16:11:04 -040083
Gus Prevas894d9152018-11-12 13:51:40 -050084 public static final int ACTION_NONE = 0;
Nadia Benbernoub66516c2019-03-06 11:01:53 -050085 static final int ACTION_UNDO = 1;
Julia Reynolds52a27372019-04-03 11:00:17 -040086 // standard controls
Nadia Benbernoub66516c2019-03-06 11:01:53 -050087 static final int ACTION_TOGGLE_SILENT = 2;
Julia Reynolds52a27372019-04-03 11:00:17 -040088 // unused
Nadia Benbernoub66516c2019-03-06 11:01:53 -050089 static final int ACTION_BLOCK = 3;
Julia Reynolds52a27372019-04-03 11:00:17 -040090 // blocking helper
Nadia Benbernoub66516c2019-03-06 11:01:53 -050091 static final int ACTION_DELIVER_SILENTLY = 4;
Julia Reynolds52a27372019-04-03 11:00:17 -040092 // standard controls
Nadia Benbernoub66516c2019-03-06 11:01:53 -050093 private static final int ACTION_ALERT = 5;
Gus Prevas9abc5062018-10-31 16:11:04 -040094
Julia Reynolds4d1dd792019-04-29 11:48:29 -040095 private static final int BUTTON_ANIM_TIME_MS = 200;
96
Mady Mellor87d79452017-01-10 11:52:52 -080097 private INotificationManager mINotificationManager;
Julia Reynolds437cdb12018-01-03 12:27:24 -050098 private PackageManager mPm;
Rohan Shahda5dcdd2018-04-27 17:21:50 -070099 private MetricsLogger mMetricsLogger;
Evan Laird47dc4542019-04-24 15:10:52 -0400100 private ChannelEditorDialogController mChannelEditorDialogController;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500101
Rohan Shahca0447e2018-03-30 15:18:27 -0700102 private String mPackageName;
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400103 private String mAppName;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500104 private int mAppUid;
Julia Reynolds268647a2018-10-25 16:54:27 -0400105 private String mDelegatePkg;
Rohan Shahca0447e2018-03-30 15:18:27 -0700106 private int mNumUniqueChannelsInRow;
Evan Laird47dc4542019-04-24 15:10:52 -0400107 private Set<NotificationChannel> mUniqueChannelsInRow;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500108 private NotificationChannel mSingleNotificationChannel;
Gus Prevas9abc5062018-10-31 16:11:04 -0400109 private int mStartingChannelImportance;
Gus Prevascaed15c2019-01-18 14:19:51 -0500110 private boolean mWasShownHighPriority;
Julia Reynolds8728d002019-04-19 15:46:13 -0400111 private boolean mShowOnLockscreen;
112 private boolean mShowInStatusBar;
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400113 private boolean mPressedApply;
114
Gus Prevascaed15c2019-01-18 14:19:51 -0500115 /**
116 * The last importance level chosen by the user. Null if the user has not chosen an importance
117 * level; non-null once the user takes an action which indicates an explicit preference.
118 */
119 @Nullable private Integer mChosenImportance;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500120 private boolean mIsSingleDefaultChannel;
Rohan Shah63411fc2018-03-28 19:05:52 -0700121 private boolean mIsNonblockable;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500122 private StatusBarNotification mSbn;
123 private AnimatorSet mExpandAnimation;
Julia Reynolds35765d82018-08-17 11:39:19 -0400124 private boolean mIsDeviceProvisioned;
Mady Mellor87d79452017-01-10 11:52:52 -0800125
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400126 private CheckSaveListener mCheckSaveListener;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500127 private OnSettingsClickListener mOnSettingsClickListener;
Julia Reynolds3aedded2017-03-31 14:42:09 -0400128 private OnAppSettingsClickListener mAppSettingsClickListener;
Mady Mellor95d743c2017-01-10 12:05:27 -0800129 private NotificationGuts mGutsContainer;
Julia Reynolds8728d002019-04-19 15:46:13 -0400130 private Drawable mSelectedBackground;
131 private Drawable mUnselectedBackground;
Evan Laird47dc4542019-04-24 15:10:52 -0400132 private Drawable mPkgIcon;
Rohan Shah524cf7b2018-03-15 14:40:02 -0700133
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700134 /** Whether this view is being shown as part of the blocking helper. */
Rohan Shah524cf7b2018-03-15 14:40:02 -0700135 private boolean mIsForBlockingHelper;
Mady Mellor87d79452017-01-10 11:52:52 -0800136
Rohan Shahdd588c72018-05-09 20:32:15 -0700137 /**
138 * String that describes how the user exit or quit out of this view, also used as a counter tag.
139 */
140 private String mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700141
Julia Reynolds52a27372019-04-03 11:00:17 -0400142 // used by standard ui
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500143 private OnClickListener mOnAlert = v -> {
144 mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
145 mChosenImportance = IMPORTANCE_DEFAULT;
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400146 setImportanceSummary(ACTION_ALERT, true);
Julia Reynolds52a27372019-04-03 11:00:17 -0400147 updateButtons(ACTION_ALERT);
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500148 };
149
Julia Reynolds52a27372019-04-03 11:00:17 -0400150 // used by standard ui
151 private OnClickListener mOnSilent = v -> {
152 mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY;
153 mChosenImportance = IMPORTANCE_LOW;
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400154 setImportanceSummary(ACTION_TOGGLE_SILENT, true);
Julia Reynolds52a27372019-04-03 11:00:17 -0400155 updateButtons(ACTION_TOGGLE_SILENT);
156 };
157
158 // used by standard ui
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500159 private OnClickListener mOnDismissSettings = v -> {
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400160 mPressedApply = true;
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500161 closeControls(v);
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500162 };
163
Julia Reynolds52a27372019-04-03 11:00:17 -0400164 // used by blocking helper
165 private OnClickListener mOnKeepShowing = v -> {
166 mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
167 closeControls(v);
168 mMetricsLogger.write(getLogMaker().setCategory(
169 MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
170 .setType(MetricsEvent.TYPE_ACTION)
171 .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_STAY_SILENT));
172 };
173
174 // used by blocking helper
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500175 private OnClickListener mOnDeliverSilently = v -> {
176 handleSaveImportance(
177 ACTION_DELIVER_SILENTLY, MetricsEvent.BLOCKING_HELPER_CLICK_STAY_SILENT);
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500178 };
179
180 private void handleSaveImportance(int action, int metricsSubtype) {
Gus Prevas533836a2018-09-24 17:15:32 -0400181 Runnable saveImportance = () -> {
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500182 saveImportanceAndExitReason(action);
Will Brockmana399f022019-01-30 10:59:36 -0500183 if (mIsForBlockingHelper) {
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500184 swapContent(action, true /* animate */);
Will Brockmana399f022019-01-30 10:59:36 -0500185 mMetricsLogger.write(getLogMaker()
186 .setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
187 .setType(MetricsEvent.TYPE_ACTION)
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500188 .setSubtype(metricsSubtype));
Will Brockmana399f022019-01-30 10:59:36 -0500189 }
Gus Prevas533836a2018-09-24 17:15:32 -0400190 };
191 if (mCheckSaveListener != null) {
192 mCheckSaveListener.checkSave(saveImportance, mSbn);
193 } else {
194 saveImportance.run();
195 }
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500196 }
Julia Reynolds437cdb12018-01-03 12:27:24 -0500197
198 private OnClickListener mOnUndo = v -> {
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700199 // Reset exit counter that we'll log and record an undo event separately (not an exit event)
Rohan Shahdd588c72018-05-09 20:32:15 -0700200 mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
Will Brockmana399f022019-01-30 10:59:36 -0500201 if (mIsForBlockingHelper) {
202 logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_UNDO);
203 mMetricsLogger.write(getLogMaker().setCategory(
204 MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
205 .setType(MetricsEvent.TYPE_DISMISS)
206 .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_UNDO));
207 } else {
Julia Reynolds52a27372019-04-03 11:00:17 -0400208 // TODO: this can't happen?
Will Brockmana399f022019-01-30 10:59:36 -0500209 mMetricsLogger.write(importanceChangeLogMaker().setType(MetricsEvent.TYPE_DISMISS));
210 }
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500211 saveImportanceAndExitReason(ACTION_UNDO);
Gus Prevas894d9152018-11-12 13:51:40 -0500212 swapContent(ACTION_UNDO, true /* animate */);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500213 };
214
Mady Mellor87d79452017-01-10 11:52:52 -0800215 public NotificationInfo(Context context, AttributeSet attrs) {
216 super(context, attrs);
217 }
218
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400219 // Specify a CheckSaveListener to override when/if the user's changes are committed.
220 public interface CheckSaveListener {
221 // Invoked when importance has changed and the NotificationInfo wants to try to save it.
222 // Listener should run saveImportance unless the change should be canceled.
Eliot Courtney47098cb2017-10-18 17:30:30 +0900223 void checkSave(Runnable saveImportance, StatusBarNotification sbn);
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400224 }
225
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500226 public interface OnSettingsClickListener {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500227 void onClick(View v, NotificationChannel channel, int appUid);
Mady Mellor87d79452017-01-10 11:52:52 -0800228 }
229
Julia Reynolds3aedded2017-03-31 14:42:09 -0400230 public interface OnAppSettingsClickListener {
231 void onClick(View v, Intent intent);
232 }
233
Rohan Shah524cf7b2018-03-15 14:40:02 -0700234 @VisibleForTesting
235 void bindNotification(
236 final PackageManager pm,
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500237 final INotificationManager iNotificationManager,
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500238 final String pkg,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500239 final NotificationChannel notificationChannel,
Evan Laird47dc4542019-04-24 15:10:52 -0400240 final Set<NotificationChannel> uniqueChannelsInRow,
Julia Reynolds3aedded2017-03-31 14:42:09 -0400241 final StatusBarNotification sbn,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500242 final CheckSaveListener checkSaveListener,
243 final OnSettingsClickListener onSettingsClick,
244 final OnAppSettingsClickListener onAppSettingsClick,
Julia Reynolds35765d82018-08-17 11:39:19 -0400245 boolean isDeviceProvisioned,
Gus Prevas9abc5062018-10-31 16:11:04 -0400246 boolean isNonblockable,
Gus Prevascaed15c2019-01-18 14:19:51 -0500247 int importance,
248 boolean wasShownHighPriority)
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500249 throws RemoteException {
Rohan Shahca0447e2018-03-30 15:18:27 -0700250 bindNotification(pm, iNotificationManager, pkg, notificationChannel,
Evan Laird47dc4542019-04-24 15:10:52 -0400251 uniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
Julia Reynolds35765d82018-08-17 11:39:19 -0400252 onAppSettingsClick, isDeviceProvisioned, isNonblockable,
Julia Reynolds52a27372019-04-03 11:00:17 -0400253 false /* isBlockingHelper */,
Gus Prevascaed15c2019-01-18 14:19:51 -0500254 importance, wasShownHighPriority);
Julia Reynolds0ef7d842018-01-24 17:50:31 -0500255 }
256
Rohan Shah524cf7b2018-03-15 14:40:02 -0700257 public void bindNotification(
258 PackageManager pm,
259 INotificationManager iNotificationManager,
260 String pkg,
261 NotificationChannel notificationChannel,
Evan Laird47dc4542019-04-24 15:10:52 -0400262 Set<NotificationChannel> uniqueChannelsInRow,
Rohan Shah524cf7b2018-03-15 14:40:02 -0700263 StatusBarNotification sbn,
264 CheckSaveListener checkSaveListener,
265 OnSettingsClickListener onSettingsClick,
266 OnAppSettingsClickListener onAppSettingsClick,
Julia Reynolds35765d82018-08-17 11:39:19 -0400267 boolean isDeviceProvisioned,
Rohan Shah63411fc2018-03-28 19:05:52 -0700268 boolean isNonblockable,
Rohan Shah524cf7b2018-03-15 14:40:02 -0700269 boolean isForBlockingHelper,
Gus Prevascaed15c2019-01-18 14:19:51 -0500270 int importance,
271 boolean wasShownHighPriority)
Rohan Shah524cf7b2018-03-15 14:40:02 -0700272 throws RemoteException {
Mady Mellor87d79452017-01-10 11:52:52 -0800273 mINotificationManager = iNotificationManager;
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700274 mMetricsLogger = Dependency.get(MetricsLogger.class);
Evan Laird47dc4542019-04-24 15:10:52 -0400275 mChannelEditorDialogController = Dependency.get(ChannelEditorDialogController.class);
Rohan Shahca0447e2018-03-30 15:18:27 -0700276 mPackageName = pkg;
Evan Laird47dc4542019-04-24 15:10:52 -0400277 mUniqueChannelsInRow = uniqueChannelsInRow;
278 mNumUniqueChannelsInRow = uniqueChannelsInRow.size();
Julia Reynolds3aedded2017-03-31 14:42:09 -0400279 mSbn = sbn;
280 mPm = pm;
281 mAppSettingsClickListener = onAppSettingsClick;
Rohan Shahca0447e2018-03-30 15:18:27 -0700282 mAppName = mPackageName;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500283 mCheckSaveListener = checkSaveListener;
284 mOnSettingsClickListener = onSettingsClick;
285 mSingleNotificationChannel = notificationChannel;
Gus Prevascaed15c2019-01-18 14:19:51 -0500286 mStartingChannelImportance = mSingleNotificationChannel.getImportance();
287 mWasShownHighPriority = wasShownHighPriority;
Rohan Shah63411fc2018-03-28 19:05:52 -0700288 mIsNonblockable = isNonblockable;
Rohan Shah524cf7b2018-03-15 14:40:02 -0700289 mIsForBlockingHelper = isForBlockingHelper;
Rohan Shahdbd64e72018-03-28 14:46:50 -0700290 mAppUid = mSbn.getUid();
Julia Reynolds268647a2018-10-25 16:54:27 -0400291 mDelegatePkg = mSbn.getOpPkg();
Julia Reynolds35765d82018-08-17 11:39:19 -0400292 mIsDeviceProvisioned = isDeviceProvisioned;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500293
Julia Reynolds8728d002019-04-19 15:46:13 -0400294 mSelectedBackground = mContext.getDrawable(R.drawable.button_border_selected);
295 mUnselectedBackground = mContext.getDrawable(R.drawable.button_border_unselected);
Julia Reynolds52a27372019-04-03 11:00:17 -0400296
Julia Reynolds437cdb12018-01-03 12:27:24 -0500297 int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400298 pkg, mAppUid, false /* includeDeleted */);
Rohan Shahca0447e2018-03-30 15:18:27 -0700299 if (mNumUniqueChannelsInRow == 0) {
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400300 throw new IllegalArgumentException("bindNotification requires at least one channel");
301 } else {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500302 // Special behavior for the Default channel if no other channels have been defined.
Rohan Shahca0447e2018-03-30 15:18:27 -0700303 mIsSingleDefaultChannel = mNumUniqueChannelsInRow == 1
Rohan Shahdbd64e72018-03-28 14:46:50 -0700304 && mSingleNotificationChannel.getId().equals(
305 NotificationChannel.DEFAULT_CHANNEL_ID)
306 && numTotalChannels == 1;
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400307 }
308
Julia Reynolds8728d002019-04-19 15:46:13 -0400309 mShowInStatusBar = !mINotificationManager.shouldHideSilentStatusIcons(
310 mContext.getPackageName());
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400311 mShowOnLockscreen = Settings.Secure.getInt(mContext.getContentResolver(),
312 Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0) == 1;
Julia Reynolds8728d002019-04-19 15:46:13 -0400313
Julia Reynolds437cdb12018-01-03 12:27:24 -0500314 bindHeader();
Julia Reynolds52a27372019-04-03 11:00:17 -0400315 bindChannelDetails();
316
317 if (mIsForBlockingHelper) {
318 bindBlockingHelper();
319 } else {
320 bindInlineControls();
321 }
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200322
Will Brockmana399f022019-01-30 10:59:36 -0500323 mMetricsLogger.write(notificationControlsLogMaker());
Julia Reynolds437cdb12018-01-03 12:27:24 -0500324 }
325
Julia Reynolds52a27372019-04-03 11:00:17 -0400326 private void bindBlockingHelper() {
327 findViewById(R.id.inline_controls).setVisibility(GONE);
328 findViewById(R.id.blocking_helper).setVisibility(VISIBLE);
329
330 findViewById(R.id.undo).setOnClickListener(mOnUndo);
331
332 View turnOffButton = findViewById(R.id.blocking_helper_turn_off_notifications);
333 turnOffButton.setOnClickListener(getSettingsOnClickListener());
334 turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() ? VISIBLE : GONE);
335
336 TextView keepShowing = findViewById(R.id.keep_showing);
337 keepShowing.setOnClickListener(mOnKeepShowing);
338
339 View deliverSilently = findViewById(R.id.deliver_silently);
340 deliverSilently.setOnClickListener(mOnDeliverSilently);
341 }
342
343 private void bindInlineControls() {
344 findViewById(R.id.inline_controls).setVisibility(VISIBLE);
345 findViewById(R.id.blocking_helper).setVisibility(GONE);
346
347 if (mIsNonblockable) {
348 findViewById(R.id.non_configurable_text).setVisibility(VISIBLE);
349 findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE);
350 findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
Julia Reynolds8728d002019-04-19 15:46:13 -0400351 ((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button);
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400352 findViewById(R.id.turn_off_notifications).setVisibility(GONE);
Julia Reynolds52a27372019-04-03 11:00:17 -0400353 } else if (mNumUniqueChannelsInRow > 1) {
354 findViewById(R.id.non_configurable_text).setVisibility(GONE);
355 findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
356 findViewById(R.id.non_configurable_multichannel_text).setVisibility(VISIBLE);
357 } else {
358 findViewById(R.id.non_configurable_text).setVisibility(GONE);
359 findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE);
360 findViewById(R.id.interruptiveness_settings).setVisibility(VISIBLE);
361 }
362
363 View turnOffButton = findViewById(R.id.turn_off_notifications);
Evan Laird47dc4542019-04-24 15:10:52 -0400364 turnOffButton.setOnClickListener(getTurnOffNotificationsClickListener());
Julia Reynolds52a27372019-04-03 11:00:17 -0400365 turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() && !mIsNonblockable
366 ? VISIBLE : GONE);
367
368 View done = findViewById(R.id.done);
369 done.setOnClickListener(mOnDismissSettings);
370
371
Julia Reynolds8728d002019-04-19 15:46:13 -0400372 View silent = findViewById(R.id.silence);
373 View alert = findViewById(R.id.alert);
Julia Reynolds52a27372019-04-03 11:00:17 -0400374 silent.setOnClickListener(mOnSilent);
375 alert.setOnClickListener(mOnAlert);
376
377 if (mWasShownHighPriority) {
378 updateButtons(ACTION_ALERT);
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400379 setImportanceSummary(ACTION_ALERT, false);
Julia Reynolds52a27372019-04-03 11:00:17 -0400380 } else {
381 updateButtons(ACTION_TOGGLE_SILENT);
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400382 setImportanceSummary(ACTION_TOGGLE_SILENT, false);
Julia Reynolds52a27372019-04-03 11:00:17 -0400383 }
384 }
385
386 private void bindHeader() {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500387 // Package name
Evan Laird47dc4542019-04-24 15:10:52 -0400388 mPkgIcon = null;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500389 ApplicationInfo info;
390 try {
Rohan Shahca0447e2018-03-30 15:18:27 -0700391 info = mPm.getApplicationInfo(
392 mPackageName,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500393 PackageManager.MATCH_UNINSTALLED_PACKAGES
394 | PackageManager.MATCH_DISABLED_COMPONENTS
395 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
396 | PackageManager.MATCH_DIRECT_BOOT_AWARE);
397 if (info != null) {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500398 mAppName = String.valueOf(mPm.getApplicationLabel(info));
Evan Laird47dc4542019-04-24 15:10:52 -0400399 mPkgIcon = mPm.getApplicationIcon(info);
Geoffrey Pitschee8c81e2017-03-23 11:38:56 -0400400 }
Julia Reynolds437cdb12018-01-03 12:27:24 -0500401 } catch (PackageManager.NameNotFoundException e) {
402 // app is gone, just show package name and generic icon
Evan Laird47dc4542019-04-24 15:10:52 -0400403 mPkgIcon = mPm.getDefaultActivityIcon();
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500404 }
Evan Laird47dc4542019-04-24 15:10:52 -0400405 ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(mPkgIcon);
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400406 ((TextView) findViewById(R.id.pkgname)).setText(mAppName);
Mady Mellor87d79452017-01-10 11:52:52 -0800407
Julia Reynolds268647a2018-10-25 16:54:27 -0400408 // Delegate
409 bindDelegate();
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000410
Julia Reynolds52a27372019-04-03 11:00:17 -0400411 // Set up app settings link (i.e. Customize)
412 View settingsLinkView = findViewById(R.id.app_settings);
413 Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName,
414 mSingleNotificationChannel,
415 mSbn.getId(), mSbn.getTag());
416 if (settingsIntent != null
417 && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
418 settingsLinkView.setVisibility(VISIBLE);
419 settingsLinkView.setOnClickListener((View view) -> {
420 mAppSettingsClickListener.onClick(view, settingsIntent);
421 });
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000422 } else {
Julia Reynolds52a27372019-04-03 11:00:17 -0400423 settingsLinkView.setVisibility(View.GONE);
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000424 }
Julia Reynolds52a27372019-04-03 11:00:17 -0400425
426 // System Settings button.
427 final View settingsButton = findViewById(R.id.info);
428 settingsButton.setOnClickListener(getSettingsOnClickListener());
429 settingsButton.setVisibility(settingsButton.hasOnClickListeners() ? VISIBLE : GONE);
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000430 }
431
Julia Reynolds52a27372019-04-03 11:00:17 -0400432 private OnClickListener getSettingsOnClickListener() {
433 if (mAppUid >= 0 && mOnSettingsClickListener != null && mIsDeviceProvisioned) {
434 final int appUidF = mAppUid;
435 return ((View view) -> {
436 logBlockingHelperCounter(
437 NotificationCounters.BLOCKING_HELPER_NOTIF_SETTINGS);
438 mOnSettingsClickListener.onClick(view,
439 mNumUniqueChannelsInRow > 1 ? null : mSingleNotificationChannel,
440 appUidF);
441 });
442 }
443 return null;
444 }
445
Evan Laird47dc4542019-04-24 15:10:52 -0400446 private OnClickListener getTurnOffNotificationsClickListener() {
447 return ((View view) -> {
448 if (mChannelEditorDialogController != null) {
449 mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid,
450 mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener);
451 mChannelEditorDialogController.show();
452 }
453 });
454 }
455
Julia Reynolds52a27372019-04-03 11:00:17 -0400456 private void bindChannelDetails() throws RemoteException {
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000457 bindName();
Julia Reynolds268647a2018-10-25 16:54:27 -0400458 bindGroup();
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000459 }
460
461 private void bindName() {
462 final TextView channelName = findViewById(R.id.channel_name);
463 if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) {
464 channelName.setVisibility(View.GONE);
465 } else {
466 channelName.setText(mSingleNotificationChannel.getName());
467 }
Julia Reynoldsac98aea2018-10-25 16:54:27 -0400468 }
469
Julia Reynolds268647a2018-10-25 16:54:27 -0400470 private void bindDelegate() {
471 TextView delegateView = findViewById(R.id.delegate_name);
472 TextView dividerView = findViewById(R.id.pkg_divider);
473
474 CharSequence delegatePkg = null;
475 if (!TextUtils.equals(mPackageName, mDelegatePkg)) {
476 // this notification was posted by a delegate!
Julia Reynolds268647a2018-10-25 16:54:27 -0400477 delegateView.setVisibility(View.VISIBLE);
478 dividerView.setVisibility(View.VISIBLE);
479 } else {
480 delegateView.setVisibility(View.GONE);
481 dividerView.setVisibility(View.GONE);
482 }
483 }
484
485 private void bindGroup() throws RemoteException {
486 // Set group information if this channel has an associated group.
487 CharSequence groupName = null;
488 if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
489 final NotificationChannelGroup notificationChannelGroup =
490 mINotificationManager.getNotificationChannelGroupForPackage(
491 mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
492 if (notificationChannelGroup != null) {
493 groupName = notificationChannelGroup.getName();
494 }
495 }
496 TextView groupNameView = findViewById(R.id.group_name);
Julia Reynolds268647a2018-10-25 16:54:27 -0400497 if (groupName != null) {
498 groupNameView.setText(groupName);
499 groupNameView.setVisibility(View.VISIBLE);
Julia Reynolds268647a2018-10-25 16:54:27 -0400500 } else {
501 groupNameView.setVisibility(View.GONE);
Julia Reynolds268647a2018-10-25 16:54:27 -0400502 }
503 }
504
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500505
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700506 @VisibleForTesting
507 void logBlockingHelperCounter(String counterTag) {
508 if (mIsForBlockingHelper) {
509 mMetricsLogger.count(counterTag, 1);
510 }
511 }
512
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500513 private void saveImportance() {
Gus Prevas894d9152018-11-12 13:51:40 -0500514 if (!mIsNonblockable
515 || mExitReason != NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS) {
Gus Prevascaed15c2019-01-18 14:19:51 -0500516 if (mChosenImportance == null) {
517 mChosenImportance = mStartingChannelImportance;
518 }
Gus Prevas533836a2018-09-24 17:15:32 -0400519 updateImportance();
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500520 }
Rohan Shahca0447e2018-03-30 15:18:27 -0700521 }
522
523 /**
524 * Commits the updated importance values on the background thread.
525 */
526 private void updateImportance() {
Gus Prevascaed15c2019-01-18 14:19:51 -0500527 if (mChosenImportance != null) {
528 mMetricsLogger.write(importanceChangeLogMaker());
Rohan Shahca0447e2018-03-30 15:18:27 -0700529
Julia Reynolds8728d002019-04-19 15:46:13 -0400530 int newImportance = mChosenImportance;
531 if (mStartingChannelImportance != IMPORTANCE_UNSPECIFIED) {
532 if ((mWasShownHighPriority && mChosenImportance >= IMPORTANCE_DEFAULT)
533 || (!mWasShownHighPriority && mChosenImportance < IMPORTANCE_DEFAULT)) {
534 newImportance = mStartingChannelImportance;
535 }
536 }
537
Gus Prevascaed15c2019-01-18 14:19:51 -0500538 Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
539 bgHandler.post(
540 new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
541 mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
Julia Reynolds8728d002019-04-19 15:46:13 -0400542 mStartingChannelImportance, newImportance));
Gus Prevascaed15c2019-01-18 14:19:51 -0500543 }
Mady Mellor87d79452017-01-10 11:52:52 -0800544 }
545
Julia Reynolds52a27372019-04-03 11:00:17 -0400546 private void updateButtons(int blockState) {
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400547 View silence = findViewById(R.id.silence);
548 View alert = findViewById(R.id.alert);
Julia Reynolds8728d002019-04-19 15:46:13 -0400549 TextView done = findViewById(R.id.done);
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500550 switch (blockState) {
Julia Reynolds52a27372019-04-03 11:00:17 -0400551 case ACTION_TOGGLE_SILENT:
Julia Reynolds8728d002019-04-19 15:46:13 -0400552 updateButtons(silence, alert);
553 if (mWasShownHighPriority) {
554 done.setText(R.string.inline_ok_button);
555 } else {
556 done.setText(R.string.inline_done_button);
557 }
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500558 break;
559 case ACTION_ALERT:
Julia Reynolds8728d002019-04-19 15:46:13 -0400560 updateButtons(alert, silence);
561 if (mWasShownHighPriority) {
562 done.setText(R.string.inline_done_button);
563 } else {
564 done.setText(R.string.inline_ok_button);
565 }
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500566 break;
567 }
568 }
569
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400570 private void updateButtons(View selected, View unselected) {
Julia Reynolds8728d002019-04-19 15:46:13 -0400571 selected.setBackground(mSelectedBackground);
572 selected.setSelected(true);
Julia Reynolds8728d002019-04-19 15:46:13 -0400573 unselected.setBackground(mUnselectedBackground);
574 unselected.setSelected(false);
Julia Reynolds8728d002019-04-19 15:46:13 -0400575 }
576
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400577 void setImportanceSummary(int blockState, boolean userTriggered) {
578 if (userTriggered) {
579 AutoTransition transition = new AutoTransition();
580 transition.setDuration(BUTTON_ANIM_TIME_MS);
581 TransitionManager.beginDelayedTransition(this, transition);
582 }
Julia Reynolds8728d002019-04-19 15:46:13 -0400583 if (blockState == ACTION_ALERT) {
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400584 TextView view = findViewById(R.id.alert_summary);
585 view.setVisibility(VISIBLE);
586 findViewById(R.id.silence_summary).setVisibility(GONE);
Julia Reynolds8728d002019-04-19 15:46:13 -0400587 view.setText(R.string.notification_channel_summary_default);
588 } else {
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400589 TextView view = findViewById(R.id.silence_summary);
590 view.setVisibility(VISIBLE);
591 findViewById(R.id.alert_summary).setVisibility(GONE);
Julia Reynolds8728d002019-04-19 15:46:13 -0400592 if (mShowInStatusBar) {
593 if (mShowOnLockscreen) {
594 view.setText(R.string.notification_channel_summary_low_status_lock);
595 } else {
596 view.setText(R.string.notification_channel_summary_low_status);
597 }
598 } else if (mShowOnLockscreen) {
599 view.setText(R.string.notification_channel_summary_low_lock);
600 } else {
601 view.setText(R.string.notification_channel_summary_low);
602 }
603 }
604 }
605
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500606 private void saveImportanceAndExitReason(@NotificationInfoAction int action) {
607 switch (action) {
608 case ACTION_UNDO:
609 mChosenImportance = mStartingChannelImportance;
610 break;
611 case ACTION_DELIVER_SILENTLY:
612 mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY;
Julia Reynolds8728d002019-04-19 15:46:13 -0400613 mChosenImportance = mWasShownHighPriority
614 ? IMPORTANCE_LOW : mStartingChannelImportance;
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500615 break;
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500616 default:
617 throw new IllegalArgumentException();
618 }
619 }
620
Julia Reynolds52a27372019-04-03 11:00:17 -0400621 // only used for blocking helper
Gus Prevas894d9152018-11-12 13:51:40 -0500622 private void swapContent(@NotificationInfoAction int action, boolean animate) {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500623 if (mExpandAnimation != null) {
624 mExpandAnimation.cancel();
625 }
Mady Mellor87d79452017-01-10 11:52:52 -0800626
Julia Reynolds52a27372019-04-03 11:00:17 -0400627 View blockingHelper = findViewById(R.id.blocking_helper);
Julia Reynoldse0341482018-03-08 14:42:50 -0500628 ViewGroup confirmation = findViewById(R.id.confirmation);
629 TextView confirmationText = findViewById(R.id.confirmation_text);
Julia Reynoldse0341482018-03-08 14:42:50 -0500630
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500631 saveImportanceAndExitReason(action);
632
Gus Prevas9abc5062018-10-31 16:11:04 -0400633 switch (action) {
Gus Prevas894d9152018-11-12 13:51:40 -0500634 case ACTION_UNDO:
Gus Prevas9abc5062018-10-31 16:11:04 -0400635 break;
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500636 case ACTION_DELIVER_SILENTLY:
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500637 confirmationText.setText(R.string.notification_channel_silenced);
638 break;
Gus Prevas9abc5062018-10-31 16:11:04 -0400639 default:
640 throw new IllegalArgumentException();
Julia Reynolds437cdb12018-01-03 12:27:24 -0500641 }
642
Gus Prevas894d9152018-11-12 13:51:40 -0500643 boolean isUndo = action == ACTION_UNDO;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500644
Julia Reynolds52a27372019-04-03 11:00:17 -0400645 blockingHelper.setVisibility(isUndo ? VISIBLE : GONE);
646 findViewById(R.id.channel_info).setVisibility(isUndo ? VISIBLE : GONE);
647 findViewById(R.id.header).setVisibility(isUndo ? VISIBLE : GONE);
Gus Prevas9abc5062018-10-31 16:11:04 -0400648 confirmation.setVisibility(isUndo ? GONE : VISIBLE);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500649
Gus Prevas894d9152018-11-12 13:51:40 -0500650 if (animate) {
Julia Reynolds52a27372019-04-03 11:00:17 -0400651 ObjectAnimator promptAnim = ObjectAnimator.ofFloat(blockingHelper, View.ALPHA,
652 blockingHelper.getAlpha(), isUndo ? 1f : 0f);
Gus Prevas894d9152018-11-12 13:51:40 -0500653 promptAnim.setInterpolator(isUndo ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
654 ObjectAnimator confirmAnim = ObjectAnimator.ofFloat(confirmation, View.ALPHA,
655 confirmation.getAlpha(), isUndo ? 0f : 1f);
656 confirmAnim.setInterpolator(isUndo ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500657
Gus Prevas894d9152018-11-12 13:51:40 -0500658 mExpandAnimation = new AnimatorSet();
659 mExpandAnimation.playTogether(promptAnim, confirmAnim);
660 mExpandAnimation.setDuration(150);
661 mExpandAnimation.addListener(new AnimatorListenerAdapter() {
662 boolean mCancelled = false;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500663
Gus Prevas894d9152018-11-12 13:51:40 -0500664 @Override
665 public void onAnimationCancel(Animator animation) {
666 mCancelled = true;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500667 }
Gus Prevas894d9152018-11-12 13:51:40 -0500668
669 @Override
670 public void onAnimationEnd(Animator animation) {
671 if (!mCancelled) {
Julia Reynolds52a27372019-04-03 11:00:17 -0400672 blockingHelper.setVisibility(isUndo ? VISIBLE : GONE);
Gus Prevas894d9152018-11-12 13:51:40 -0500673 confirmation.setVisibility(isUndo ? GONE : VISIBLE);
674 }
675 }
676 });
677 mExpandAnimation.start();
678 }
Rohan Shah142e2da2018-06-14 13:14:18 -0700679
680 // Since we're swapping/update the content, reset the timeout so the UI can't close
681 // immediately after the update.
682 if (mGutsContainer != null) {
683 mGutsContainer.resetFalsingCheck();
684 }
Mady Mellor87d79452017-01-10 11:52:52 -0800685 }
686
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400687 @Override
Gus Prevas9abc5062018-10-31 16:11:04 -0400688 public void onFinishedClosing() {
Gus Prevascaed15c2019-01-18 14:19:51 -0500689 if (mChosenImportance != null) {
690 mStartingChannelImportance = mChosenImportance;
Gus Prevas9abc5062018-10-31 16:11:04 -0400691 }
692 mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
693
Julia Reynolds52a27372019-04-03 11:00:17 -0400694 if (mIsForBlockingHelper) {
695 bindBlockingHelper();
696 } else {
697 bindInlineControls();
698 }
Will Brockmana399f022019-01-30 10:59:36 -0500699
700 mMetricsLogger.write(notificationControlsLogMaker().setType(MetricsEvent.TYPE_CLOSE));
Gus Prevas9abc5062018-10-31 16:11:04 -0400701 }
702
703 @Override
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400704 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
705 super.onInitializeAccessibilityEvent(event);
706 if (mGutsContainer != null &&
707 event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
708 if (mGutsContainer.isExposed()) {
709 event.getText().add(mContext.getString(
710 R.string.notification_channel_controls_opened_accessibility, mAppName));
711 } else {
712 event.getText().add(mContext.getString(
713 R.string.notification_channel_controls_closed_accessibility, mAppName));
714 }
715 }
716 }
717
Julia Reynolds3aedded2017-03-31 14:42:09 -0400718 private Intent getAppSettingsIntent(PackageManager pm, String packageName,
719 NotificationChannel channel, int id, String tag) {
720 Intent intent = new Intent(Intent.ACTION_MAIN)
721 .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
722 .setPackage(packageName);
723 final List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
724 intent,
725 PackageManager.MATCH_DEFAULT_ONLY
726 );
727 if (resolveInfos == null || resolveInfos.size() == 0 || resolveInfos.get(0) == null) {
728 return null;
729 }
730 final ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
731 intent.setClassName(activityInfo.packageName, activityInfo.name);
732 if (channel != null) {
733 intent.putExtra(Notification.EXTRA_CHANNEL_ID, channel.getId());
734 }
735 intent.putExtra(Notification.EXTRA_NOTIFICATION_ID, id);
736 intent.putExtra(Notification.EXTRA_NOTIFICATION_TAG, tag);
737 return intent;
738 }
739
Rohan Shahca0447e2018-03-30 15:18:27 -0700740 /**
741 * Closes the controls and commits the updated importance values (indirectly). If this view is
742 * being used to show the blocking helper, this will immediately dismiss the blocking helper and
743 * commit the updated importance.
744 *
745 * <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 -0400746 * user does not have the ability to undo the action anymore. See
747 * {@link #swapContent(boolean, boolean)} for where undo is handled.
Rohan Shahca0447e2018-03-30 15:18:27 -0700748 */
Rohan Shah524cf7b2018-03-15 14:40:02 -0700749 @VisibleForTesting
750 void closeControls(View v) {
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700751 int[] parentLoc = new int[2];
752 int[] targetLoc = new int[2];
753 mGutsContainer.getLocationOnScreen(parentLoc);
754 v.getLocationOnScreen(targetLoc);
755 final int centerX = v.getWidth() / 2;
756 final int centerY = v.getHeight() / 2;
757 final int x = targetLoc[0] - parentLoc[0] + centerX;
758 final int y = targetLoc[1] - parentLoc[1] + centerY;
759 mGutsContainer.closeControls(x, y, true /* save */, false /* force */);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500760 }
761
Mady Mellor87d79452017-01-10 11:52:52 -0800762 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800763 public void setGutsParent(NotificationGuts guts) {
764 mGutsContainer = guts;
Mady Mellor87d79452017-01-10 11:52:52 -0800765 }
766
767 @Override
Mady Mellor434180c2017-02-13 11:29:42 -0800768 public boolean willBeRemoved() {
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400769 return false;
Mady Mellor434180c2017-02-13 11:29:42 -0800770 }
771
772 @Override
Lucas Dupin9b08c012018-05-16 19:53:32 -0700773 public boolean shouldBeSaved() {
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400774 return mPressedApply;
Lucas Dupin9b08c012018-05-16 19:53:32 -0700775 }
776
777 @Override
Mady Mellor87d79452017-01-10 11:52:52 -0800778 public View getContentView() {
779 return this;
780 }
781
782 @Override
Mady Mellorc2dbe492017-03-30 13:22:03 -0700783 public boolean handleCloseControls(boolean save, boolean force) {
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500784 // Save regardless of the importance so we can lock the importance field if the user wants
785 // to keep getting notifications
786 if (save) {
Rohan Shahca0447e2018-03-30 15:18:27 -0700787 saveImportance();
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500788 }
Rohan Shahdd588c72018-05-09 20:32:15 -0700789 logBlockingHelperCounter(mExitReason);
Mady Mellor87d79452017-01-10 11:52:52 -0800790 return false;
791 }
Mady Mellore09fb702017-03-30 13:23:29 -0700792
793 @Override
794 public int getActualHeight() {
795 return getHeight();
796 }
Rohan Shahca0447e2018-03-30 15:18:27 -0700797
Gus Prevas533836a2018-09-24 17:15:32 -0400798 @VisibleForTesting
799 public boolean isAnimating() {
800 return mExpandAnimation != null && mExpandAnimation.isRunning();
801 }
802
Rohan Shahca0447e2018-03-30 15:18:27 -0700803 /**
804 * Runnable to either update the given channel (with a new importance value) or, if no channel
805 * is provided, update notifications enabled state for the package.
806 */
807 private static class UpdateImportanceRunnable implements Runnable {
808 private final INotificationManager mINotificationManager;
809 private final String mPackageName;
810 private final int mAppUid;
811 private final @Nullable NotificationChannel mChannelToUpdate;
812 private final int mCurrentImportance;
813 private final int mNewImportance;
814
815
816 public UpdateImportanceRunnable(INotificationManager notificationManager,
817 String packageName, int appUid, @Nullable NotificationChannel channelToUpdate,
818 int currentImportance, int newImportance) {
819 mINotificationManager = notificationManager;
820 mPackageName = packageName;
821 mAppUid = appUid;
822 mChannelToUpdate = channelToUpdate;
823 mCurrentImportance = currentImportance;
824 mNewImportance = newImportance;
825 }
826
827 @Override
828 public void run() {
829 try {
830 if (mChannelToUpdate != null) {
831 mChannelToUpdate.setImportance(mNewImportance);
832 mChannelToUpdate.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
833 mINotificationManager.updateNotificationChannelForPackage(
834 mPackageName, mAppUid, mChannelToUpdate);
835 } else {
836 // For notifications with more than one channel, update notification enabled
837 // state. If the importance was lowered, we disable notifications.
Rohan Shah590e1b22018-04-10 23:48:47 -0400838 mINotificationManager.setNotificationsEnabledWithImportanceLockForPackage(
Rohan Shahca0447e2018-03-30 15:18:27 -0700839 mPackageName, mAppUid, mNewImportance >= mCurrentImportance);
840 }
841 } catch (RemoteException e) {
842 Log.e(TAG, "Unable to update notification importance", e);
843 }
844 }
845 }
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200846
Will Brockmana399f022019-01-30 10:59:36 -0500847 /**
848 * Returns a LogMaker with all available notification information.
849 * Caller should set category, type, and maybe subtype, before passing it to mMetricsLogger.
850 * @return LogMaker
851 */
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200852 private LogMaker getLogMaker() {
Will Brockmana399f022019-01-30 10:59:36 -0500853 // The constructor requires a category, so also do it in the other branch for consistency.
854 return mSbn == null ? new LogMaker(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
855 : mSbn.getLogMaker().setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER);
856 }
857
858 /**
859 * Returns an initialized LogMaker for logging importance changes.
860 * The caller may override the type before passing it to mMetricsLogger.
861 * @return LogMaker
862 */
863 private LogMaker importanceChangeLogMaker() {
864 Integer chosenImportance =
865 mChosenImportance != null ? mChosenImportance : mStartingChannelImportance;
866 return getLogMaker().setCategory(MetricsEvent.ACTION_SAVE_IMPORTANCE)
867 .setType(MetricsEvent.TYPE_ACTION)
868 .setSubtype(chosenImportance - mStartingChannelImportance);
869 }
870
871 /**
872 * Returns an initialized LogMaker for logging open/close of the info display.
873 * The caller may override the type before passing it to mMetricsLogger.
874 * @return LogMaker
875 */
876 private LogMaker notificationControlsLogMaker() {
877 return getLogMaker().setCategory(MetricsEvent.ACTION_NOTE_CONTROLS)
878 .setType(MetricsEvent.TYPE_OPEN)
879 .setSubtype(mIsForBlockingHelper ? MetricsEvent.BLOCKING_HELPER_DISPLAY
880 : MetricsEvent.BLOCKING_HELPER_UNKNOWN);
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200881 }
Mady Mellor87d79452017-01-10 11:52:52 -0800882}