blob: 622b869c9e4c5c3c394860aa792af8f54945310d [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 Reynoldse0341482018-03-08 14:42:50 -050021import static android.app.NotificationManager.IMPORTANCE_MIN;
Julia Reynolds3aedded2017-03-31 14:42:09 -040022import static android.app.NotificationManager.IMPORTANCE_NONE;
23
Julia Reynolds437cdb12018-01-03 12:27:24 -050024import android.animation.Animator;
25import android.animation.AnimatorListenerAdapter;
26import android.animation.AnimatorSet;
27import android.animation.ObjectAnimator;
Gus Prevas9abc5062018-10-31 16:11:04 -040028import android.annotation.IntDef;
Rohan Shahca0447e2018-03-30 15:18:27 -070029import android.annotation.Nullable;
Mady Mellor87d79452017-01-10 11:52:52 -080030import android.app.INotificationManager;
Julia Reynolds3aedded2017-03-31 14:42:09 -040031import android.app.Notification;
Mady Mellor87d79452017-01-10 11:52:52 -080032import android.app.NotificationChannel;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -050033import android.app.NotificationChannelGroup;
Mady Mellor87d79452017-01-10 11:52:52 -080034import android.content.Context;
Julia Reynolds3aedded2017-03-31 14:42:09 -040035import android.content.Intent;
36import android.content.pm.ActivityInfo;
Mady Mellor87d79452017-01-10 11:52:52 -080037import android.content.pm.ApplicationInfo;
Mady Mellor87d79452017-01-10 11:52:52 -080038import android.content.pm.PackageManager;
Julia Reynolds3aedded2017-03-31 14:42:09 -040039import android.content.pm.ResolveInfo;
Nadia Benbernoub66516c2019-03-06 11:01:53 -050040import android.graphics.Color;
Mady Mellor87d79452017-01-10 11:52:52 -080041import android.graphics.drawable.Drawable;
Julia Reynolds52a27372019-04-03 11:00:17 -040042import android.graphics.drawable.GradientDrawable;
William Brockman75cf66a2018-12-21 13:25:01 -050043import android.metrics.LogMaker;
Rohan Shahca0447e2018-03-30 15:18:27 -070044import android.os.Handler;
Mady Mellor87d79452017-01-10 11:52:52 -080045import android.os.RemoteException;
Julia Reynolds3aedded2017-03-31 14:42:09 -040046import android.service.notification.StatusBarNotification;
47import android.text.TextUtils;
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;
Gus Prevas9abc5062018-10-31 16:11:04 -040063import com.android.systemui.statusbar.notification.NotificationUtils;
Rohan Shah20790b82018-07-02 17:21:04 -070064import com.android.systemui.statusbar.notification.logging.NotificationCounters;
Mady Mellor87d79452017-01-10 11:52:52 -080065
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050066import java.util.List;
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
Mady Mellor87d79452017-01-10 11:52:52 -080095 private INotificationManager mINotificationManager;
Julia Reynolds437cdb12018-01-03 12:27:24 -050096 private PackageManager mPm;
Rohan Shahda5dcdd2018-04-27 17:21:50 -070097 private MetricsLogger mMetricsLogger;
Julia Reynolds437cdb12018-01-03 12:27:24 -050098
Rohan Shahca0447e2018-03-30 15:18:27 -070099 private String mPackageName;
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400100 private String mAppName;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500101 private int mAppUid;
Julia Reynolds268647a2018-10-25 16:54:27 -0400102 private String mDelegatePkg;
Rohan Shahca0447e2018-03-30 15:18:27 -0700103 private int mNumUniqueChannelsInRow;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500104 private NotificationChannel mSingleNotificationChannel;
Gus Prevas9abc5062018-10-31 16:11:04 -0400105 private int mStartingChannelImportance;
Gus Prevascaed15c2019-01-18 14:19:51 -0500106 private boolean mWasShownHighPriority;
107 /**
108 * The last importance level chosen by the user. Null if the user has not chosen an importance
109 * level; non-null once the user takes an action which indicates an explicit preference.
110 */
111 @Nullable private Integer mChosenImportance;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500112 private boolean mIsSingleDefaultChannel;
Rohan Shah63411fc2018-03-28 19:05:52 -0700113 private boolean mIsNonblockable;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500114 private StatusBarNotification mSbn;
115 private AnimatorSet mExpandAnimation;
Julia Reynolds35765d82018-08-17 11:39:19 -0400116 private boolean mIsDeviceProvisioned;
Mady Mellor87d79452017-01-10 11:52:52 -0800117
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400118 private CheckSaveListener mCheckSaveListener;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500119 private OnSettingsClickListener mOnSettingsClickListener;
Julia Reynolds3aedded2017-03-31 14:42:09 -0400120 private OnAppSettingsClickListener mAppSettingsClickListener;
Mady Mellor95d743c2017-01-10 12:05:27 -0800121 private NotificationGuts mGutsContainer;
Julia Reynolds52a27372019-04-03 11:00:17 -0400122 private GradientDrawable mSelectedBackground;
Rohan Shah524cf7b2018-03-15 14:40:02 -0700123
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700124 /** Whether this view is being shown as part of the blocking helper. */
Rohan Shah524cf7b2018-03-15 14:40:02 -0700125 private boolean mIsForBlockingHelper;
Mady Mellor87d79452017-01-10 11:52:52 -0800126
Rohan Shahdd588c72018-05-09 20:32:15 -0700127 /**
128 * String that describes how the user exit or quit out of this view, also used as a counter tag.
129 */
130 private String mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700131
Julia Reynolds52a27372019-04-03 11:00:17 -0400132 // used by standard ui
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500133 private OnClickListener mOnAlert = v -> {
134 mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
135 mChosenImportance = IMPORTANCE_DEFAULT;
Julia Reynolds52a27372019-04-03 11:00:17 -0400136 updateButtons(ACTION_ALERT);
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500137 };
138
Julia Reynolds52a27372019-04-03 11:00:17 -0400139 // used by standard ui
140 private OnClickListener mOnSilent = v -> {
141 mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY;
142 mChosenImportance = IMPORTANCE_LOW;
143 updateButtons(ACTION_TOGGLE_SILENT);
144 };
145
146 // used by standard ui
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500147 private OnClickListener mOnDismissSettings = v -> {
148 closeControls(v);
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500149 };
150
Julia Reynolds52a27372019-04-03 11:00:17 -0400151 // used by blocking helper
152 private OnClickListener mOnKeepShowing = v -> {
153 mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
154 closeControls(v);
155 mMetricsLogger.write(getLogMaker().setCategory(
156 MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
157 .setType(MetricsEvent.TYPE_ACTION)
158 .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_STAY_SILENT));
159 };
160
161 // used by blocking helper
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500162 private OnClickListener mOnDeliverSilently = v -> {
163 handleSaveImportance(
164 ACTION_DELIVER_SILENTLY, MetricsEvent.BLOCKING_HELPER_CLICK_STAY_SILENT);
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500165 };
166
167 private void handleSaveImportance(int action, int metricsSubtype) {
Gus Prevas533836a2018-09-24 17:15:32 -0400168 Runnable saveImportance = () -> {
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500169 saveImportanceAndExitReason(action);
Will Brockmana399f022019-01-30 10:59:36 -0500170 if (mIsForBlockingHelper) {
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500171 swapContent(action, true /* animate */);
Will Brockmana399f022019-01-30 10:59:36 -0500172 mMetricsLogger.write(getLogMaker()
173 .setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
174 .setType(MetricsEvent.TYPE_ACTION)
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500175 .setSubtype(metricsSubtype));
Will Brockmana399f022019-01-30 10:59:36 -0500176 }
Gus Prevas533836a2018-09-24 17:15:32 -0400177 };
178 if (mCheckSaveListener != null) {
179 mCheckSaveListener.checkSave(saveImportance, mSbn);
180 } else {
181 saveImportance.run();
182 }
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500183 }
Julia Reynolds437cdb12018-01-03 12:27:24 -0500184
185 private OnClickListener mOnUndo = v -> {
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700186 // Reset exit counter that we'll log and record an undo event separately (not an exit event)
Rohan Shahdd588c72018-05-09 20:32:15 -0700187 mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
Will Brockmana399f022019-01-30 10:59:36 -0500188 if (mIsForBlockingHelper) {
189 logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_UNDO);
190 mMetricsLogger.write(getLogMaker().setCategory(
191 MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
192 .setType(MetricsEvent.TYPE_DISMISS)
193 .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_UNDO));
194 } else {
Julia Reynolds52a27372019-04-03 11:00:17 -0400195 // TODO: this can't happen?
Will Brockmana399f022019-01-30 10:59:36 -0500196 mMetricsLogger.write(importanceChangeLogMaker().setType(MetricsEvent.TYPE_DISMISS));
197 }
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500198 saveImportanceAndExitReason(ACTION_UNDO);
Gus Prevas894d9152018-11-12 13:51:40 -0500199 swapContent(ACTION_UNDO, true /* animate */);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500200 };
201
Mady Mellor87d79452017-01-10 11:52:52 -0800202 public NotificationInfo(Context context, AttributeSet attrs) {
203 super(context, attrs);
204 }
205
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400206 // Specify a CheckSaveListener to override when/if the user's changes are committed.
207 public interface CheckSaveListener {
208 // Invoked when importance has changed and the NotificationInfo wants to try to save it.
209 // Listener should run saveImportance unless the change should be canceled.
Eliot Courtney47098cb2017-10-18 17:30:30 +0900210 void checkSave(Runnable saveImportance, StatusBarNotification sbn);
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400211 }
212
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500213 public interface OnSettingsClickListener {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500214 void onClick(View v, NotificationChannel channel, int appUid);
Mady Mellor87d79452017-01-10 11:52:52 -0800215 }
216
Julia Reynolds3aedded2017-03-31 14:42:09 -0400217 public interface OnAppSettingsClickListener {
218 void onClick(View v, Intent intent);
219 }
220
Rohan Shah524cf7b2018-03-15 14:40:02 -0700221 @VisibleForTesting
222 void bindNotification(
223 final PackageManager pm,
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500224 final INotificationManager iNotificationManager,
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500225 final String pkg,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500226 final NotificationChannel notificationChannel,
Rohan Shahca0447e2018-03-30 15:18:27 -0700227 final int numUniqueChannelsInRow,
Julia Reynolds3aedded2017-03-31 14:42:09 -0400228 final StatusBarNotification sbn,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500229 final CheckSaveListener checkSaveListener,
230 final OnSettingsClickListener onSettingsClick,
231 final OnAppSettingsClickListener onAppSettingsClick,
Julia Reynolds35765d82018-08-17 11:39:19 -0400232 boolean isDeviceProvisioned,
Gus Prevas9abc5062018-10-31 16:11:04 -0400233 boolean isNonblockable,
Gus Prevascaed15c2019-01-18 14:19:51 -0500234 int importance,
235 boolean wasShownHighPriority)
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500236 throws RemoteException {
Rohan Shahca0447e2018-03-30 15:18:27 -0700237 bindNotification(pm, iNotificationManager, pkg, notificationChannel,
238 numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
Julia Reynolds35765d82018-08-17 11:39:19 -0400239 onAppSettingsClick, isDeviceProvisioned, isNonblockable,
Julia Reynolds52a27372019-04-03 11:00:17 -0400240 false /* isBlockingHelper */,
Gus Prevascaed15c2019-01-18 14:19:51 -0500241 importance, wasShownHighPriority);
Julia Reynolds0ef7d842018-01-24 17:50:31 -0500242 }
243
Rohan Shah524cf7b2018-03-15 14:40:02 -0700244 public void bindNotification(
245 PackageManager pm,
246 INotificationManager iNotificationManager,
247 String pkg,
248 NotificationChannel notificationChannel,
Rohan Shahca0447e2018-03-30 15:18:27 -0700249 int numUniqueChannelsInRow,
Rohan Shah524cf7b2018-03-15 14:40:02 -0700250 StatusBarNotification sbn,
251 CheckSaveListener checkSaveListener,
252 OnSettingsClickListener onSettingsClick,
253 OnAppSettingsClickListener onAppSettingsClick,
Julia Reynolds35765d82018-08-17 11:39:19 -0400254 boolean isDeviceProvisioned,
Rohan Shah63411fc2018-03-28 19:05:52 -0700255 boolean isNonblockable,
Rohan Shah524cf7b2018-03-15 14:40:02 -0700256 boolean isForBlockingHelper,
Gus Prevascaed15c2019-01-18 14:19:51 -0500257 int importance,
258 boolean wasShownHighPriority)
Rohan Shah524cf7b2018-03-15 14:40:02 -0700259 throws RemoteException {
Mady Mellor87d79452017-01-10 11:52:52 -0800260 mINotificationManager = iNotificationManager;
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700261 mMetricsLogger = Dependency.get(MetricsLogger.class);
Rohan Shahca0447e2018-03-30 15:18:27 -0700262 mPackageName = pkg;
263 mNumUniqueChannelsInRow = numUniqueChannelsInRow;
Julia Reynolds3aedded2017-03-31 14:42:09 -0400264 mSbn = sbn;
265 mPm = pm;
266 mAppSettingsClickListener = onAppSettingsClick;
Rohan Shahca0447e2018-03-30 15:18:27 -0700267 mAppName = mPackageName;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500268 mCheckSaveListener = checkSaveListener;
269 mOnSettingsClickListener = onSettingsClick;
270 mSingleNotificationChannel = notificationChannel;
Gus Prevascaed15c2019-01-18 14:19:51 -0500271 mStartingChannelImportance = mSingleNotificationChannel.getImportance();
272 mWasShownHighPriority = wasShownHighPriority;
Rohan Shah63411fc2018-03-28 19:05:52 -0700273 mIsNonblockable = isNonblockable;
Rohan Shah524cf7b2018-03-15 14:40:02 -0700274 mIsForBlockingHelper = isForBlockingHelper;
Rohan Shahdbd64e72018-03-28 14:46:50 -0700275 mAppUid = mSbn.getUid();
Julia Reynolds268647a2018-10-25 16:54:27 -0400276 mDelegatePkg = mSbn.getOpPkg();
Julia Reynolds35765d82018-08-17 11:39:19 -0400277 mIsDeviceProvisioned = isDeviceProvisioned;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500278
Julia Reynolds52a27372019-04-03 11:00:17 -0400279 mSelectedBackground = new GradientDrawable();
280 mSelectedBackground.setShape(GradientDrawable.RECTANGLE);
281 mSelectedBackground.setColor(mContext.getColor(R.color.notification_guts_selection_bg));
282 final float cornerRadii = getResources().getDisplayMetrics().density * 8;
283 mSelectedBackground.setCornerRadii(new float[]{cornerRadii, cornerRadii, cornerRadii,
284 cornerRadii, cornerRadii, cornerRadii, cornerRadii, cornerRadii});
285 mSelectedBackground.setStroke((int) (getResources().getDisplayMetrics().density * 2),
286 mContext.getColor(R.color.notification_guts_selection_border));
287
Julia Reynolds437cdb12018-01-03 12:27:24 -0500288 int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400289 pkg, mAppUid, false /* includeDeleted */);
Rohan Shahca0447e2018-03-30 15:18:27 -0700290 if (mNumUniqueChannelsInRow == 0) {
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400291 throw new IllegalArgumentException("bindNotification requires at least one channel");
292 } else {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500293 // Special behavior for the Default channel if no other channels have been defined.
Rohan Shahca0447e2018-03-30 15:18:27 -0700294 mIsSingleDefaultChannel = mNumUniqueChannelsInRow == 1
Rohan Shahdbd64e72018-03-28 14:46:50 -0700295 && mSingleNotificationChannel.getId().equals(
296 NotificationChannel.DEFAULT_CHANNEL_ID)
297 && numTotalChannels == 1;
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400298 }
299
Julia Reynolds437cdb12018-01-03 12:27:24 -0500300 bindHeader();
Julia Reynolds52a27372019-04-03 11:00:17 -0400301 bindChannelDetails();
302
303 if (mIsForBlockingHelper) {
304 bindBlockingHelper();
305 } else {
306 bindInlineControls();
307 }
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200308
Will Brockmana399f022019-01-30 10:59:36 -0500309 mMetricsLogger.write(notificationControlsLogMaker());
Julia Reynolds437cdb12018-01-03 12:27:24 -0500310 }
311
Julia Reynolds52a27372019-04-03 11:00:17 -0400312 private void bindBlockingHelper() {
313 findViewById(R.id.inline_controls).setVisibility(GONE);
314 findViewById(R.id.blocking_helper).setVisibility(VISIBLE);
315
316 findViewById(R.id.undo).setOnClickListener(mOnUndo);
317
318 View turnOffButton = findViewById(R.id.blocking_helper_turn_off_notifications);
319 turnOffButton.setOnClickListener(getSettingsOnClickListener());
320 turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() ? VISIBLE : GONE);
321
322 TextView keepShowing = findViewById(R.id.keep_showing);
323 keepShowing.setOnClickListener(mOnKeepShowing);
324
325 View deliverSilently = findViewById(R.id.deliver_silently);
326 deliverSilently.setOnClickListener(mOnDeliverSilently);
327 }
328
329 private void bindInlineControls() {
330 findViewById(R.id.inline_controls).setVisibility(VISIBLE);
331 findViewById(R.id.blocking_helper).setVisibility(GONE);
332
333 if (mIsNonblockable) {
334 findViewById(R.id.non_configurable_text).setVisibility(VISIBLE);
335 findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE);
336 findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
337 } else if (mNumUniqueChannelsInRow > 1) {
338 findViewById(R.id.non_configurable_text).setVisibility(GONE);
339 findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
340 findViewById(R.id.non_configurable_multichannel_text).setVisibility(VISIBLE);
341 } else {
342 findViewById(R.id.non_configurable_text).setVisibility(GONE);
343 findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE);
344 findViewById(R.id.interruptiveness_settings).setVisibility(VISIBLE);
345 }
346
347 View turnOffButton = findViewById(R.id.turn_off_notifications);
348 turnOffButton.setOnClickListener(getSettingsOnClickListener());
349 turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() && !mIsNonblockable
350 ? VISIBLE : GONE);
351
352 View done = findViewById(R.id.done);
353 done.setOnClickListener(mOnDismissSettings);
354
355
356 View silent = findViewById(R.id.silent_row);
357 View alert = findViewById(R.id.alert_row);
358 silent.setOnClickListener(mOnSilent);
359 alert.setOnClickListener(mOnAlert);
360
361 if (mWasShownHighPriority) {
362 updateButtons(ACTION_ALERT);
363 } else {
364 updateButtons(ACTION_TOGGLE_SILENT);
365 }
366 }
367
368 private void bindHeader() {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500369 // Package name
370 Drawable pkgicon = null;
371 ApplicationInfo info;
372 try {
Rohan Shahca0447e2018-03-30 15:18:27 -0700373 info = mPm.getApplicationInfo(
374 mPackageName,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500375 PackageManager.MATCH_UNINSTALLED_PACKAGES
376 | PackageManager.MATCH_DISABLED_COMPONENTS
377 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
378 | PackageManager.MATCH_DIRECT_BOOT_AWARE);
379 if (info != null) {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500380 mAppName = String.valueOf(mPm.getApplicationLabel(info));
381 pkgicon = mPm.getApplicationIcon(info);
Geoffrey Pitschee8c81e2017-03-23 11:38:56 -0400382 }
Julia Reynolds437cdb12018-01-03 12:27:24 -0500383 } catch (PackageManager.NameNotFoundException e) {
384 // app is gone, just show package name and generic icon
385 pkgicon = mPm.getDefaultActivityIcon();
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500386 }
Julia Reynolds437cdb12018-01-03 12:27:24 -0500387 ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400388 ((TextView) findViewById(R.id.pkgname)).setText(mAppName);
Mady Mellor87d79452017-01-10 11:52:52 -0800389
Julia Reynolds268647a2018-10-25 16:54:27 -0400390 // Delegate
391 bindDelegate();
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000392
Julia Reynolds52a27372019-04-03 11:00:17 -0400393 // Set up app settings link (i.e. Customize)
394 View settingsLinkView = findViewById(R.id.app_settings);
395 Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName,
396 mSingleNotificationChannel,
397 mSbn.getId(), mSbn.getTag());
398 if (settingsIntent != null
399 && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
400 settingsLinkView.setVisibility(VISIBLE);
401 settingsLinkView.setOnClickListener((View view) -> {
402 mAppSettingsClickListener.onClick(view, settingsIntent);
403 });
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000404 } else {
Julia Reynolds52a27372019-04-03 11:00:17 -0400405 settingsLinkView.setVisibility(View.GONE);
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000406 }
Julia Reynolds52a27372019-04-03 11:00:17 -0400407
408 // System Settings button.
409 final View settingsButton = findViewById(R.id.info);
410 settingsButton.setOnClickListener(getSettingsOnClickListener());
411 settingsButton.setVisibility(settingsButton.hasOnClickListeners() ? VISIBLE : GONE);
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000412 }
413
Julia Reynolds52a27372019-04-03 11:00:17 -0400414 private OnClickListener getSettingsOnClickListener() {
415 if (mAppUid >= 0 && mOnSettingsClickListener != null && mIsDeviceProvisioned) {
416 final int appUidF = mAppUid;
417 return ((View view) -> {
418 logBlockingHelperCounter(
419 NotificationCounters.BLOCKING_HELPER_NOTIF_SETTINGS);
420 mOnSettingsClickListener.onClick(view,
421 mNumUniqueChannelsInRow > 1 ? null : mSingleNotificationChannel,
422 appUidF);
423 });
424 }
425 return null;
426 }
427
428 private void bindChannelDetails() throws RemoteException {
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000429 bindName();
Julia Reynolds268647a2018-10-25 16:54:27 -0400430 bindGroup();
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000431 }
432
433 private void bindName() {
434 final TextView channelName = findViewById(R.id.channel_name);
435 if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) {
436 channelName.setVisibility(View.GONE);
437 } else {
438 channelName.setText(mSingleNotificationChannel.getName());
439 }
Julia Reynoldsac98aea2018-10-25 16:54:27 -0400440 }
441
Julia Reynolds268647a2018-10-25 16:54:27 -0400442 private void bindDelegate() {
443 TextView delegateView = findViewById(R.id.delegate_name);
444 TextView dividerView = findViewById(R.id.pkg_divider);
445
446 CharSequence delegatePkg = null;
447 if (!TextUtils.equals(mPackageName, mDelegatePkg)) {
448 // this notification was posted by a delegate!
449 ApplicationInfo info;
450 try {
451 info = mPm.getApplicationInfo(
452 mDelegatePkg,
453 PackageManager.MATCH_UNINSTALLED_PACKAGES
454 | PackageManager.MATCH_DISABLED_COMPONENTS
455 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
456 | PackageManager.MATCH_DIRECT_BOOT_AWARE);
457 if (info != null) {
458 delegatePkg = String.valueOf(mPm.getApplicationLabel(info));
459 }
460 } catch (PackageManager.NameNotFoundException e) { }
461 }
462 if (delegatePkg != null) {
463 delegateView.setText(mContext.getResources().getString(
464 R.string.notification_delegate_header, delegatePkg));
465 delegateView.setVisibility(View.VISIBLE);
466 dividerView.setVisibility(View.VISIBLE);
467 } else {
468 delegateView.setVisibility(View.GONE);
469 dividerView.setVisibility(View.GONE);
470 }
471 }
472
473 private void bindGroup() throws RemoteException {
474 // Set group information if this channel has an associated group.
475 CharSequence groupName = null;
476 if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
477 final NotificationChannelGroup notificationChannelGroup =
478 mINotificationManager.getNotificationChannelGroupForPackage(
479 mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
480 if (notificationChannelGroup != null) {
481 groupName = notificationChannelGroup.getName();
482 }
483 }
484 TextView groupNameView = findViewById(R.id.group_name);
485 TextView groupDividerView = findViewById(R.id.pkg_group_divider);
486 if (groupName != null) {
487 groupNameView.setText(groupName);
488 groupNameView.setVisibility(View.VISIBLE);
489 groupDividerView.setVisibility(View.VISIBLE);
490 } else {
491 groupNameView.setVisibility(View.GONE);
492 groupDividerView.setVisibility(View.GONE);
493 }
494 }
495
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500496
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700497 @VisibleForTesting
498 void logBlockingHelperCounter(String counterTag) {
499 if (mIsForBlockingHelper) {
500 mMetricsLogger.count(counterTag, 1);
501 }
502 }
503
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400504 private boolean hasImportanceChanged() {
Gus Prevas9abc5062018-10-31 16:11:04 -0400505 return mSingleNotificationChannel != null
Gus Prevascaed15c2019-01-18 14:19:51 -0500506 && mChosenImportance != null
507 && (mStartingChannelImportance != mChosenImportance
508 || (mWasShownHighPriority && mChosenImportance < IMPORTANCE_DEFAULT)
509 || (!mWasShownHighPriority && mChosenImportance >= IMPORTANCE_DEFAULT));
Mady Mellor87d79452017-01-10 11:52:52 -0800510 }
511
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500512 private void saveImportance() {
Gus Prevas894d9152018-11-12 13:51:40 -0500513 if (!mIsNonblockable
514 || mExitReason != NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS) {
Gus Prevascaed15c2019-01-18 14:19:51 -0500515 if (mChosenImportance == null) {
516 mChosenImportance = mStartingChannelImportance;
517 }
Gus Prevas533836a2018-09-24 17:15:32 -0400518 updateImportance();
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500519 }
Rohan Shahca0447e2018-03-30 15:18:27 -0700520 }
521
522 /**
523 * Commits the updated importance values on the background thread.
524 */
525 private void updateImportance() {
Gus Prevascaed15c2019-01-18 14:19:51 -0500526 if (mChosenImportance != null) {
527 mMetricsLogger.write(importanceChangeLogMaker());
Rohan Shahca0447e2018-03-30 15:18:27 -0700528
Gus Prevascaed15c2019-01-18 14:19:51 -0500529 Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
530 bgHandler.post(
531 new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
532 mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
533 mStartingChannelImportance, mChosenImportance));
534 }
Mady Mellor87d79452017-01-10 11:52:52 -0800535 }
536
Julia Reynolds52a27372019-04-03 11:00:17 -0400537 private void updateButtons(int blockState) {
538 View silent = findViewById(R.id.silent_row);
539 View alert = findViewById(R.id.alert_row);
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500540 switch (blockState) {
Julia Reynolds52a27372019-04-03 11:00:17 -0400541 case ACTION_TOGGLE_SILENT:
542 silent.setBackground(mSelectedBackground);
543 alert.setBackground(null);
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500544 break;
545 case ACTION_ALERT:
Julia Reynolds52a27372019-04-03 11:00:17 -0400546 alert.setBackground(mSelectedBackground);
547 silent.setBackground(null);
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500548 break;
549 }
550 }
551
552 private void saveImportanceAndExitReason(@NotificationInfoAction int action) {
553 switch (action) {
554 case ACTION_UNDO:
555 mChosenImportance = mStartingChannelImportance;
556 break;
557 case ACTION_DELIVER_SILENTLY:
558 mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY;
559 mChosenImportance = IMPORTANCE_LOW;
560 break;
561 case ACTION_TOGGLE_SILENT:
562 mExitReason = NotificationCounters.BLOCKING_HELPER_TOGGLE_SILENT;
563 if (mWasShownHighPriority) {
564 mChosenImportance = IMPORTANCE_LOW;
565 } else {
566 mChosenImportance = IMPORTANCE_DEFAULT;
567 }
568 break;
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500569 default:
570 throw new IllegalArgumentException();
571 }
572 }
573
Julia Reynolds52a27372019-04-03 11:00:17 -0400574 // only used for blocking helper
Gus Prevas894d9152018-11-12 13:51:40 -0500575 private void swapContent(@NotificationInfoAction int action, boolean animate) {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500576 if (mExpandAnimation != null) {
577 mExpandAnimation.cancel();
578 }
Mady Mellor87d79452017-01-10 11:52:52 -0800579
Julia Reynolds52a27372019-04-03 11:00:17 -0400580 View blockingHelper = findViewById(R.id.blocking_helper);
Julia Reynoldse0341482018-03-08 14:42:50 -0500581 ViewGroup confirmation = findViewById(R.id.confirmation);
582 TextView confirmationText = findViewById(R.id.confirmation_text);
Julia Reynoldse0341482018-03-08 14:42:50 -0500583
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500584 saveImportanceAndExitReason(action);
585
Gus Prevas9abc5062018-10-31 16:11:04 -0400586 switch (action) {
Gus Prevas894d9152018-11-12 13:51:40 -0500587 case ACTION_UNDO:
Gus Prevas9abc5062018-10-31 16:11:04 -0400588 break;
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500589 case ACTION_DELIVER_SILENTLY:
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500590 confirmationText.setText(R.string.notification_channel_silenced);
591 break;
Gus Prevas9abc5062018-10-31 16:11:04 -0400592 default:
593 throw new IllegalArgumentException();
Julia Reynolds437cdb12018-01-03 12:27:24 -0500594 }
595
Gus Prevas894d9152018-11-12 13:51:40 -0500596 boolean isUndo = action == ACTION_UNDO;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500597
Julia Reynolds52a27372019-04-03 11:00:17 -0400598 blockingHelper.setVisibility(isUndo ? VISIBLE : GONE);
599 findViewById(R.id.channel_info).setVisibility(isUndo ? VISIBLE : GONE);
600 findViewById(R.id.header).setVisibility(isUndo ? VISIBLE : GONE);
Gus Prevas9abc5062018-10-31 16:11:04 -0400601 confirmation.setVisibility(isUndo ? GONE : VISIBLE);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500602
Gus Prevas894d9152018-11-12 13:51:40 -0500603 if (animate) {
Julia Reynolds52a27372019-04-03 11:00:17 -0400604 ObjectAnimator promptAnim = ObjectAnimator.ofFloat(blockingHelper, View.ALPHA,
605 blockingHelper.getAlpha(), isUndo ? 1f : 0f);
Gus Prevas894d9152018-11-12 13:51:40 -0500606 promptAnim.setInterpolator(isUndo ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
607 ObjectAnimator confirmAnim = ObjectAnimator.ofFloat(confirmation, View.ALPHA,
608 confirmation.getAlpha(), isUndo ? 0f : 1f);
609 confirmAnim.setInterpolator(isUndo ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500610
Gus Prevas894d9152018-11-12 13:51:40 -0500611 mExpandAnimation = new AnimatorSet();
612 mExpandAnimation.playTogether(promptAnim, confirmAnim);
613 mExpandAnimation.setDuration(150);
614 mExpandAnimation.addListener(new AnimatorListenerAdapter() {
615 boolean mCancelled = false;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500616
Gus Prevas894d9152018-11-12 13:51:40 -0500617 @Override
618 public void onAnimationCancel(Animator animation) {
619 mCancelled = true;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500620 }
Gus Prevas894d9152018-11-12 13:51:40 -0500621
622 @Override
623 public void onAnimationEnd(Animator animation) {
624 if (!mCancelled) {
Julia Reynolds52a27372019-04-03 11:00:17 -0400625 blockingHelper.setVisibility(isUndo ? VISIBLE : GONE);
Gus Prevas894d9152018-11-12 13:51:40 -0500626 confirmation.setVisibility(isUndo ? GONE : VISIBLE);
627 }
628 }
629 });
630 mExpandAnimation.start();
631 }
Rohan Shah142e2da2018-06-14 13:14:18 -0700632
633 // Since we're swapping/update the content, reset the timeout so the UI can't close
634 // immediately after the update.
635 if (mGutsContainer != null) {
636 mGutsContainer.resetFalsingCheck();
637 }
Mady Mellor87d79452017-01-10 11:52:52 -0800638 }
639
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400640 @Override
Gus Prevas9abc5062018-10-31 16:11:04 -0400641 public void onFinishedClosing() {
Gus Prevascaed15c2019-01-18 14:19:51 -0500642 if (mChosenImportance != null) {
643 mStartingChannelImportance = mChosenImportance;
Gus Prevas9abc5062018-10-31 16:11:04 -0400644 }
645 mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
646
Julia Reynolds52a27372019-04-03 11:00:17 -0400647 if (mIsForBlockingHelper) {
648 bindBlockingHelper();
649 } else {
650 bindInlineControls();
651 }
Will Brockmana399f022019-01-30 10:59:36 -0500652
653 mMetricsLogger.write(notificationControlsLogMaker().setType(MetricsEvent.TYPE_CLOSE));
Gus Prevas9abc5062018-10-31 16:11:04 -0400654 }
655
656 @Override
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400657 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
658 super.onInitializeAccessibilityEvent(event);
659 if (mGutsContainer != null &&
660 event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
661 if (mGutsContainer.isExposed()) {
662 event.getText().add(mContext.getString(
663 R.string.notification_channel_controls_opened_accessibility, mAppName));
664 } else {
665 event.getText().add(mContext.getString(
666 R.string.notification_channel_controls_closed_accessibility, mAppName));
667 }
668 }
669 }
670
Julia Reynolds3aedded2017-03-31 14:42:09 -0400671 private Intent getAppSettingsIntent(PackageManager pm, String packageName,
672 NotificationChannel channel, int id, String tag) {
673 Intent intent = new Intent(Intent.ACTION_MAIN)
674 .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
675 .setPackage(packageName);
676 final List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
677 intent,
678 PackageManager.MATCH_DEFAULT_ONLY
679 );
680 if (resolveInfos == null || resolveInfos.size() == 0 || resolveInfos.get(0) == null) {
681 return null;
682 }
683 final ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
684 intent.setClassName(activityInfo.packageName, activityInfo.name);
685 if (channel != null) {
686 intent.putExtra(Notification.EXTRA_CHANNEL_ID, channel.getId());
687 }
688 intent.putExtra(Notification.EXTRA_NOTIFICATION_ID, id);
689 intent.putExtra(Notification.EXTRA_NOTIFICATION_TAG, tag);
690 return intent;
691 }
692
Rohan Shahca0447e2018-03-30 15:18:27 -0700693 /**
694 * Closes the controls and commits the updated importance values (indirectly). If this view is
695 * being used to show the blocking helper, this will immediately dismiss the blocking helper and
696 * commit the updated importance.
697 *
698 * <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 -0400699 * user does not have the ability to undo the action anymore. See
700 * {@link #swapContent(boolean, boolean)} for where undo is handled.
Rohan Shahca0447e2018-03-30 15:18:27 -0700701 */
Rohan Shah524cf7b2018-03-15 14:40:02 -0700702 @VisibleForTesting
703 void closeControls(View v) {
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700704 int[] parentLoc = new int[2];
705 int[] targetLoc = new int[2];
706 mGutsContainer.getLocationOnScreen(parentLoc);
707 v.getLocationOnScreen(targetLoc);
708 final int centerX = v.getWidth() / 2;
709 final int centerY = v.getHeight() / 2;
710 final int x = targetLoc[0] - parentLoc[0] + centerX;
711 final int y = targetLoc[1] - parentLoc[1] + centerY;
712 mGutsContainer.closeControls(x, y, true /* save */, false /* force */);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500713 }
714
Mady Mellor87d79452017-01-10 11:52:52 -0800715 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800716 public void setGutsParent(NotificationGuts guts) {
717 mGutsContainer = guts;
Mady Mellor87d79452017-01-10 11:52:52 -0800718 }
719
720 @Override
Mady Mellor434180c2017-02-13 11:29:42 -0800721 public boolean willBeRemoved() {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500722 return hasImportanceChanged();
Mady Mellor434180c2017-02-13 11:29:42 -0800723 }
724
725 @Override
Lucas Dupin9b08c012018-05-16 19:53:32 -0700726 public boolean shouldBeSaved() {
727 return hasImportanceChanged();
728 }
729
730 @Override
Mady Mellor87d79452017-01-10 11:52:52 -0800731 public View getContentView() {
732 return this;
733 }
734
735 @Override
Mady Mellorc2dbe492017-03-30 13:22:03 -0700736 public boolean handleCloseControls(boolean save, boolean force) {
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500737 // Save regardless of the importance so we can lock the importance field if the user wants
738 // to keep getting notifications
739 if (save) {
Rohan Shahca0447e2018-03-30 15:18:27 -0700740 saveImportance();
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500741 }
Rohan Shahdd588c72018-05-09 20:32:15 -0700742 logBlockingHelperCounter(mExitReason);
Mady Mellor87d79452017-01-10 11:52:52 -0800743 return false;
744 }
Mady Mellore09fb702017-03-30 13:23:29 -0700745
746 @Override
747 public int getActualHeight() {
748 return getHeight();
749 }
Rohan Shahca0447e2018-03-30 15:18:27 -0700750
Gus Prevas533836a2018-09-24 17:15:32 -0400751 @VisibleForTesting
752 public boolean isAnimating() {
753 return mExpandAnimation != null && mExpandAnimation.isRunning();
754 }
755
Rohan Shahca0447e2018-03-30 15:18:27 -0700756 /**
757 * Runnable to either update the given channel (with a new importance value) or, if no channel
758 * is provided, update notifications enabled state for the package.
759 */
760 private static class UpdateImportanceRunnable implements Runnable {
761 private final INotificationManager mINotificationManager;
762 private final String mPackageName;
763 private final int mAppUid;
764 private final @Nullable NotificationChannel mChannelToUpdate;
765 private final int mCurrentImportance;
766 private final int mNewImportance;
767
768
769 public UpdateImportanceRunnable(INotificationManager notificationManager,
770 String packageName, int appUid, @Nullable NotificationChannel channelToUpdate,
771 int currentImportance, int newImportance) {
772 mINotificationManager = notificationManager;
773 mPackageName = packageName;
774 mAppUid = appUid;
775 mChannelToUpdate = channelToUpdate;
776 mCurrentImportance = currentImportance;
777 mNewImportance = newImportance;
778 }
779
780 @Override
781 public void run() {
782 try {
783 if (mChannelToUpdate != null) {
784 mChannelToUpdate.setImportance(mNewImportance);
785 mChannelToUpdate.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
786 mINotificationManager.updateNotificationChannelForPackage(
787 mPackageName, mAppUid, mChannelToUpdate);
788 } else {
789 // For notifications with more than one channel, update notification enabled
790 // state. If the importance was lowered, we disable notifications.
Rohan Shah590e1b22018-04-10 23:48:47 -0400791 mINotificationManager.setNotificationsEnabledWithImportanceLockForPackage(
Rohan Shahca0447e2018-03-30 15:18:27 -0700792 mPackageName, mAppUid, mNewImportance >= mCurrentImportance);
793 }
794 } catch (RemoteException e) {
795 Log.e(TAG, "Unable to update notification importance", e);
796 }
797 }
798 }
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200799
Will Brockmana399f022019-01-30 10:59:36 -0500800 /**
801 * Returns a LogMaker with all available notification information.
802 * Caller should set category, type, and maybe subtype, before passing it to mMetricsLogger.
803 * @return LogMaker
804 */
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200805 private LogMaker getLogMaker() {
Will Brockmana399f022019-01-30 10:59:36 -0500806 // The constructor requires a category, so also do it in the other branch for consistency.
807 return mSbn == null ? new LogMaker(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
808 : mSbn.getLogMaker().setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER);
809 }
810
811 /**
812 * Returns an initialized LogMaker for logging importance changes.
813 * The caller may override the type before passing it to mMetricsLogger.
814 * @return LogMaker
815 */
816 private LogMaker importanceChangeLogMaker() {
817 Integer chosenImportance =
818 mChosenImportance != null ? mChosenImportance : mStartingChannelImportance;
819 return getLogMaker().setCategory(MetricsEvent.ACTION_SAVE_IMPORTANCE)
820 .setType(MetricsEvent.TYPE_ACTION)
821 .setSubtype(chosenImportance - mStartingChannelImportance);
822 }
823
824 /**
825 * Returns an initialized LogMaker for logging open/close of the info display.
826 * The caller may override the type before passing it to mMetricsLogger.
827 * @return LogMaker
828 */
829 private LogMaker notificationControlsLogMaker() {
830 return getLogMaker().setCategory(MetricsEvent.ACTION_NOTE_CONTROLS)
831 .setType(MetricsEvent.TYPE_OPEN)
832 .setSubtype(mIsForBlockingHelper ? MetricsEvent.BLOCKING_HELPER_DISPLAY
833 : MetricsEvent.BLOCKING_HELPER_UNKNOWN);
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200834 }
Mady Mellor87d79452017-01-10 11:52:52 -0800835}