blob: e6875e4d8dc30fafea6702a148541145d75870f1 [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 Reynolds3aedded2017-03-31 14:42:09 -040043import android.service.notification.StatusBarNotification;
44import android.text.TextUtils;
Mady Mellor87d79452017-01-10 11:52:52 -080045import android.util.AttributeSet;
Rohan Shahca0447e2018-03-30 15:18:27 -070046import android.util.Log;
Mady Mellor87d79452017-01-10 11:52:52 -080047import android.view.View;
Julia Reynoldse0341482018-03-08 14:42:50 -050048import android.view.ViewGroup;
Geoffrey Pitschd94e7882017-04-06 09:52:11 -040049import android.view.accessibility.AccessibilityEvent;
Mady Mellor87d79452017-01-10 11:52:52 -080050import android.widget.ImageView;
51import android.widget.LinearLayout;
Mady Mellor87d79452017-01-10 11:52:52 -080052import android.widget.TextView;
53
Rohan Shah524cf7b2018-03-15 14:40:02 -070054import com.android.internal.annotations.VisibleForTesting;
Mady Mellor87d79452017-01-10 11:52:52 -080055import com.android.internal.logging.MetricsLogger;
56import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Rohan Shah524cf7b2018-03-15 14:40:02 -070057import com.android.systemui.Dependency;
Julia Reynolds437cdb12018-01-03 12:27:24 -050058import com.android.systemui.Interpolators;
Mady Mellor87d79452017-01-10 11:52:52 -080059import com.android.systemui.R;
Rohan Shah20790b82018-07-02 17:21:04 -070060import com.android.systemui.statusbar.notification.logging.NotificationCounters;
Mady Mellor87d79452017-01-10 11:52:52 -080061
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050062import java.util.List;
Mady Mellor87d79452017-01-10 11:52:52 -080063
64/**
Rohan Shahda5dcdd2018-04-27 17:21:50 -070065 * The guts of a notification revealed when performing a long press. This also houses the blocking
66 * helper affordance that allows a user to keep/stop notifications after swiping one away.
Mady Mellor87d79452017-01-10 11:52:52 -080067 */
Mady Mellor95d743c2017-01-10 12:05:27 -080068public class NotificationInfo extends LinearLayout implements NotificationGuts.GutsContent {
Mady Mellor87d79452017-01-10 11:52:52 -080069 private static final String TAG = "InfoGuts";
70
Gus Prevas894d9152018-11-12 13:51:40 -050071 @IntDef(prefix = { "ACTION_" }, value = {
72 ACTION_NONE,
73 ACTION_UNDO,
74 ACTION_TOGGLE_SILENT,
75 ACTION_BLOCK,
Gus Prevas9abc5062018-10-31 16:11:04 -040076 })
Gus Prevas894d9152018-11-12 13:51:40 -050077 public @interface NotificationInfoAction {
78 }
Gus Prevas9abc5062018-10-31 16:11:04 -040079
Gus Prevas894d9152018-11-12 13:51:40 -050080 public static final int ACTION_NONE = 0;
Nadia Benbernoub66516c2019-03-06 11:01:53 -050081 static final int ACTION_UNDO = 1;
Julia Reynolds52a27372019-04-03 11:00:17 -040082 // standard controls
Nadia Benbernoub66516c2019-03-06 11:01:53 -050083 static final int ACTION_TOGGLE_SILENT = 2;
Julia Reynolds52a27372019-04-03 11:00:17 -040084 // unused
Nadia Benbernoub66516c2019-03-06 11:01:53 -050085 static final int ACTION_BLOCK = 3;
Julia Reynolds52a27372019-04-03 11:00:17 -040086 // blocking helper
Nadia Benbernoub66516c2019-03-06 11:01:53 -050087 static final int ACTION_DELIVER_SILENTLY = 4;
Julia Reynolds52a27372019-04-03 11:00:17 -040088 // standard controls
Nadia Benbernoub66516c2019-03-06 11:01:53 -050089 private static final int ACTION_ALERT = 5;
Gus Prevas9abc5062018-10-31 16:11:04 -040090
Mady Mellor87d79452017-01-10 11:52:52 -080091 private INotificationManager mINotificationManager;
Julia Reynolds437cdb12018-01-03 12:27:24 -050092 private PackageManager mPm;
Rohan Shahda5dcdd2018-04-27 17:21:50 -070093 private MetricsLogger mMetricsLogger;
Julia Reynolds437cdb12018-01-03 12:27:24 -050094
Rohan Shahca0447e2018-03-30 15:18:27 -070095 private String mPackageName;
Geoffrey Pitschd94e7882017-04-06 09:52:11 -040096 private String mAppName;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050097 private int mAppUid;
Julia Reynolds268647a2018-10-25 16:54:27 -040098 private String mDelegatePkg;
Rohan Shahca0447e2018-03-30 15:18:27 -070099 private int mNumUniqueChannelsInRow;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500100 private NotificationChannel mSingleNotificationChannel;
Gus Prevas9abc5062018-10-31 16:11:04 -0400101 private int mStartingChannelImportance;
Gus Prevascaed15c2019-01-18 14:19:51 -0500102 private boolean mWasShownHighPriority;
Julia Reynolds8728d002019-04-19 15:46:13 -0400103 private boolean mShowOnLockscreen;
104 private boolean mShowInStatusBar;
Gus Prevascaed15c2019-01-18 14:19:51 -0500105 /**
106 * The last importance level chosen by the user. Null if the user has not chosen an importance
107 * level; non-null once the user takes an action which indicates an explicit preference.
108 */
109 @Nullable private Integer mChosenImportance;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500110 private boolean mIsSingleDefaultChannel;
Rohan Shah63411fc2018-03-28 19:05:52 -0700111 private boolean mIsNonblockable;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500112 private StatusBarNotification mSbn;
113 private AnimatorSet mExpandAnimation;
Julia Reynolds35765d82018-08-17 11:39:19 -0400114 private boolean mIsDeviceProvisioned;
Mady Mellor87d79452017-01-10 11:52:52 -0800115
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400116 private CheckSaveListener mCheckSaveListener;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500117 private OnSettingsClickListener mOnSettingsClickListener;
Julia Reynolds3aedded2017-03-31 14:42:09 -0400118 private OnAppSettingsClickListener mAppSettingsClickListener;
Mady Mellor95d743c2017-01-10 12:05:27 -0800119 private NotificationGuts mGutsContainer;
Julia Reynolds8728d002019-04-19 15:46:13 -0400120 private Drawable mSelectedBackground;
121 private Drawable mUnselectedBackground;
Rohan Shah524cf7b2018-03-15 14:40:02 -0700122
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700123 /** Whether this view is being shown as part of the blocking helper. */
Rohan Shah524cf7b2018-03-15 14:40:02 -0700124 private boolean mIsForBlockingHelper;
Mady Mellor87d79452017-01-10 11:52:52 -0800125
Rohan Shahdd588c72018-05-09 20:32:15 -0700126 /**
127 * String that describes how the user exit or quit out of this view, also used as a counter tag.
128 */
129 private String mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700130
Julia Reynolds52a27372019-04-03 11:00:17 -0400131 // used by standard ui
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500132 private OnClickListener mOnAlert = v -> {
133 mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
134 mChosenImportance = IMPORTANCE_DEFAULT;
Julia Reynolds8728d002019-04-19 15:46:13 -0400135 setImportanceSummary(ACTION_ALERT);
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;
Julia Reynolds8728d002019-04-19 15:46:13 -0400143 setImportanceSummary(ACTION_TOGGLE_SILENT);
Julia Reynolds52a27372019-04-03 11:00:17 -0400144 updateButtons(ACTION_TOGGLE_SILENT);
145 };
146
147 // used by standard ui
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500148 private OnClickListener mOnDismissSettings = v -> {
149 closeControls(v);
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500150 };
151
Julia Reynolds52a27372019-04-03 11:00:17 -0400152 // used by blocking helper
153 private OnClickListener mOnKeepShowing = v -> {
154 mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
155 closeControls(v);
156 mMetricsLogger.write(getLogMaker().setCategory(
157 MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
158 .setType(MetricsEvent.TYPE_ACTION)
159 .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_STAY_SILENT));
160 };
161
162 // used by blocking helper
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500163 private OnClickListener mOnDeliverSilently = v -> {
164 handleSaveImportance(
165 ACTION_DELIVER_SILENTLY, MetricsEvent.BLOCKING_HELPER_CLICK_STAY_SILENT);
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500166 };
167
168 private void handleSaveImportance(int action, int metricsSubtype) {
Gus Prevas533836a2018-09-24 17:15:32 -0400169 Runnable saveImportance = () -> {
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500170 saveImportanceAndExitReason(action);
Will Brockmana399f022019-01-30 10:59:36 -0500171 if (mIsForBlockingHelper) {
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500172 swapContent(action, true /* animate */);
Will Brockmana399f022019-01-30 10:59:36 -0500173 mMetricsLogger.write(getLogMaker()
174 .setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
175 .setType(MetricsEvent.TYPE_ACTION)
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500176 .setSubtype(metricsSubtype));
Will Brockmana399f022019-01-30 10:59:36 -0500177 }
Gus Prevas533836a2018-09-24 17:15:32 -0400178 };
179 if (mCheckSaveListener != null) {
180 mCheckSaveListener.checkSave(saveImportance, mSbn);
181 } else {
182 saveImportance.run();
183 }
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500184 }
Julia Reynolds437cdb12018-01-03 12:27:24 -0500185
186 private OnClickListener mOnUndo = v -> {
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700187 // Reset exit counter that we'll log and record an undo event separately (not an exit event)
Rohan Shahdd588c72018-05-09 20:32:15 -0700188 mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
Will Brockmana399f022019-01-30 10:59:36 -0500189 if (mIsForBlockingHelper) {
190 logBlockingHelperCounter(NotificationCounters.BLOCKING_HELPER_UNDO);
191 mMetricsLogger.write(getLogMaker().setCategory(
192 MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
193 .setType(MetricsEvent.TYPE_DISMISS)
194 .setSubtype(MetricsEvent.BLOCKING_HELPER_CLICK_UNDO));
195 } else {
Julia Reynolds52a27372019-04-03 11:00:17 -0400196 // TODO: this can't happen?
Will Brockmana399f022019-01-30 10:59:36 -0500197 mMetricsLogger.write(importanceChangeLogMaker().setType(MetricsEvent.TYPE_DISMISS));
198 }
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500199 saveImportanceAndExitReason(ACTION_UNDO);
Gus Prevas894d9152018-11-12 13:51:40 -0500200 swapContent(ACTION_UNDO, true /* animate */);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500201 };
202
Mady Mellor87d79452017-01-10 11:52:52 -0800203 public NotificationInfo(Context context, AttributeSet attrs) {
204 super(context, attrs);
205 }
206
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400207 // Specify a CheckSaveListener to override when/if the user's changes are committed.
208 public interface CheckSaveListener {
209 // Invoked when importance has changed and the NotificationInfo wants to try to save it.
210 // Listener should run saveImportance unless the change should be canceled.
Eliot Courtney47098cb2017-10-18 17:30:30 +0900211 void checkSave(Runnable saveImportance, StatusBarNotification sbn);
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400212 }
213
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500214 public interface OnSettingsClickListener {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500215 void onClick(View v, NotificationChannel channel, int appUid);
Mady Mellor87d79452017-01-10 11:52:52 -0800216 }
217
Julia Reynolds3aedded2017-03-31 14:42:09 -0400218 public interface OnAppSettingsClickListener {
219 void onClick(View v, Intent intent);
220 }
221
Rohan Shah524cf7b2018-03-15 14:40:02 -0700222 @VisibleForTesting
223 void bindNotification(
224 final PackageManager pm,
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500225 final INotificationManager iNotificationManager,
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500226 final String pkg,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500227 final NotificationChannel notificationChannel,
Rohan Shahca0447e2018-03-30 15:18:27 -0700228 final int numUniqueChannelsInRow,
Julia Reynolds3aedded2017-03-31 14:42:09 -0400229 final StatusBarNotification sbn,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500230 final CheckSaveListener checkSaveListener,
231 final OnSettingsClickListener onSettingsClick,
232 final OnAppSettingsClickListener onAppSettingsClick,
Julia Reynolds35765d82018-08-17 11:39:19 -0400233 boolean isDeviceProvisioned,
Gus Prevas9abc5062018-10-31 16:11:04 -0400234 boolean isNonblockable,
Gus Prevascaed15c2019-01-18 14:19:51 -0500235 int importance,
236 boolean wasShownHighPriority)
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500237 throws RemoteException {
Rohan Shahca0447e2018-03-30 15:18:27 -0700238 bindNotification(pm, iNotificationManager, pkg, notificationChannel,
239 numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
Julia Reynolds35765d82018-08-17 11:39:19 -0400240 onAppSettingsClick, isDeviceProvisioned, isNonblockable,
Julia Reynolds52a27372019-04-03 11:00:17 -0400241 false /* isBlockingHelper */,
Gus Prevascaed15c2019-01-18 14:19:51 -0500242 importance, wasShownHighPriority);
Julia Reynolds0ef7d842018-01-24 17:50:31 -0500243 }
244
Rohan Shah524cf7b2018-03-15 14:40:02 -0700245 public void bindNotification(
246 PackageManager pm,
247 INotificationManager iNotificationManager,
248 String pkg,
249 NotificationChannel notificationChannel,
Rohan Shahca0447e2018-03-30 15:18:27 -0700250 int numUniqueChannelsInRow,
Rohan Shah524cf7b2018-03-15 14:40:02 -0700251 StatusBarNotification sbn,
252 CheckSaveListener checkSaveListener,
253 OnSettingsClickListener onSettingsClick,
254 OnAppSettingsClickListener onAppSettingsClick,
Julia Reynolds35765d82018-08-17 11:39:19 -0400255 boolean isDeviceProvisioned,
Rohan Shah63411fc2018-03-28 19:05:52 -0700256 boolean isNonblockable,
Rohan Shah524cf7b2018-03-15 14:40:02 -0700257 boolean isForBlockingHelper,
Gus Prevascaed15c2019-01-18 14:19:51 -0500258 int importance,
259 boolean wasShownHighPriority)
Rohan Shah524cf7b2018-03-15 14:40:02 -0700260 throws RemoteException {
Mady Mellor87d79452017-01-10 11:52:52 -0800261 mINotificationManager = iNotificationManager;
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700262 mMetricsLogger = Dependency.get(MetricsLogger.class);
Rohan Shahca0447e2018-03-30 15:18:27 -0700263 mPackageName = pkg;
264 mNumUniqueChannelsInRow = numUniqueChannelsInRow;
Julia Reynolds3aedded2017-03-31 14:42:09 -0400265 mSbn = sbn;
266 mPm = pm;
267 mAppSettingsClickListener = onAppSettingsClick;
Rohan Shahca0447e2018-03-30 15:18:27 -0700268 mAppName = mPackageName;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500269 mCheckSaveListener = checkSaveListener;
270 mOnSettingsClickListener = onSettingsClick;
271 mSingleNotificationChannel = notificationChannel;
Gus Prevascaed15c2019-01-18 14:19:51 -0500272 mStartingChannelImportance = mSingleNotificationChannel.getImportance();
273 mWasShownHighPriority = wasShownHighPriority;
Rohan Shah63411fc2018-03-28 19:05:52 -0700274 mIsNonblockable = isNonblockable;
Rohan Shah524cf7b2018-03-15 14:40:02 -0700275 mIsForBlockingHelper = isForBlockingHelper;
Rohan Shahdbd64e72018-03-28 14:46:50 -0700276 mAppUid = mSbn.getUid();
Julia Reynolds268647a2018-10-25 16:54:27 -0400277 mDelegatePkg = mSbn.getOpPkg();
Julia Reynolds35765d82018-08-17 11:39:19 -0400278 mIsDeviceProvisioned = isDeviceProvisioned;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500279
Julia Reynolds8728d002019-04-19 15:46:13 -0400280 mSelectedBackground = mContext.getDrawable(R.drawable.button_border_selected);
281 mUnselectedBackground = mContext.getDrawable(R.drawable.button_border_unselected);
Julia Reynolds52a27372019-04-03 11:00:17 -0400282
Julia Reynolds437cdb12018-01-03 12:27:24 -0500283 int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400284 pkg, mAppUid, false /* includeDeleted */);
Rohan Shahca0447e2018-03-30 15:18:27 -0700285 if (mNumUniqueChannelsInRow == 0) {
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400286 throw new IllegalArgumentException("bindNotification requires at least one channel");
287 } else {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500288 // Special behavior for the Default channel if no other channels have been defined.
Rohan Shahca0447e2018-03-30 15:18:27 -0700289 mIsSingleDefaultChannel = mNumUniqueChannelsInRow == 1
Rohan Shahdbd64e72018-03-28 14:46:50 -0700290 && mSingleNotificationChannel.getId().equals(
291 NotificationChannel.DEFAULT_CHANNEL_ID)
292 && numTotalChannels == 1;
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400293 }
294
Julia Reynolds8728d002019-04-19 15:46:13 -0400295 mShowInStatusBar = !mINotificationManager.shouldHideSilentStatusIcons(
296 mContext.getPackageName());
297 // TODO: b/128445911 use show on lockscreen setting
298
Julia Reynolds437cdb12018-01-03 12:27:24 -0500299 bindHeader();
Julia Reynolds52a27372019-04-03 11:00:17 -0400300 bindChannelDetails();
301
302 if (mIsForBlockingHelper) {
303 bindBlockingHelper();
304 } else {
305 bindInlineControls();
306 }
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200307
Will Brockmana399f022019-01-30 10:59:36 -0500308 mMetricsLogger.write(notificationControlsLogMaker());
Julia Reynolds437cdb12018-01-03 12:27:24 -0500309 }
310
Julia Reynolds52a27372019-04-03 11:00:17 -0400311 private void bindBlockingHelper() {
312 findViewById(R.id.inline_controls).setVisibility(GONE);
313 findViewById(R.id.blocking_helper).setVisibility(VISIBLE);
314
315 findViewById(R.id.undo).setOnClickListener(mOnUndo);
316
317 View turnOffButton = findViewById(R.id.blocking_helper_turn_off_notifications);
318 turnOffButton.setOnClickListener(getSettingsOnClickListener());
319 turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() ? VISIBLE : GONE);
320
321 TextView keepShowing = findViewById(R.id.keep_showing);
322 keepShowing.setOnClickListener(mOnKeepShowing);
323
324 View deliverSilently = findViewById(R.id.deliver_silently);
325 deliverSilently.setOnClickListener(mOnDeliverSilently);
326 }
327
328 private void bindInlineControls() {
329 findViewById(R.id.inline_controls).setVisibility(VISIBLE);
330 findViewById(R.id.blocking_helper).setVisibility(GONE);
331
332 if (mIsNonblockable) {
333 findViewById(R.id.non_configurable_text).setVisibility(VISIBLE);
334 findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE);
335 findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
Julia Reynolds8728d002019-04-19 15:46:13 -0400336 ((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button);
Julia Reynolds52a27372019-04-03 11:00:17 -0400337 } 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
Julia Reynolds8728d002019-04-19 15:46:13 -0400356 View silent = findViewById(R.id.silence);
357 View alert = findViewById(R.id.alert);
Julia Reynolds52a27372019-04-03 11:00:17 -0400358 silent.setOnClickListener(mOnSilent);
359 alert.setOnClickListener(mOnAlert);
360
361 if (mWasShownHighPriority) {
362 updateButtons(ACTION_ALERT);
Julia Reynolds8728d002019-04-19 15:46:13 -0400363 setImportanceSummary(ACTION_ALERT);
Julia Reynolds52a27372019-04-03 11:00:17 -0400364 } else {
365 updateButtons(ACTION_TOGGLE_SILENT);
Julia Reynolds8728d002019-04-19 15:46:13 -0400366 setImportanceSummary(ACTION_TOGGLE_SILENT);
Julia Reynolds52a27372019-04-03 11:00:17 -0400367 }
368 }
369
370 private void bindHeader() {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500371 // Package name
372 Drawable pkgicon = null;
373 ApplicationInfo info;
374 try {
Rohan Shahca0447e2018-03-30 15:18:27 -0700375 info = mPm.getApplicationInfo(
376 mPackageName,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500377 PackageManager.MATCH_UNINSTALLED_PACKAGES
378 | PackageManager.MATCH_DISABLED_COMPONENTS
379 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
380 | PackageManager.MATCH_DIRECT_BOOT_AWARE);
381 if (info != null) {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500382 mAppName = String.valueOf(mPm.getApplicationLabel(info));
383 pkgicon = mPm.getApplicationIcon(info);
Geoffrey Pitschee8c81e2017-03-23 11:38:56 -0400384 }
Julia Reynolds437cdb12018-01-03 12:27:24 -0500385 } catch (PackageManager.NameNotFoundException e) {
386 // app is gone, just show package name and generic icon
387 pkgicon = mPm.getDefaultActivityIcon();
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500388 }
Julia Reynolds437cdb12018-01-03 12:27:24 -0500389 ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400390 ((TextView) findViewById(R.id.pkgname)).setText(mAppName);
Mady Mellor87d79452017-01-10 11:52:52 -0800391
Julia Reynolds268647a2018-10-25 16:54:27 -0400392 // Delegate
393 bindDelegate();
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000394
Julia Reynolds52a27372019-04-03 11:00:17 -0400395 // Set up app settings link (i.e. Customize)
396 View settingsLinkView = findViewById(R.id.app_settings);
397 Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName,
398 mSingleNotificationChannel,
399 mSbn.getId(), mSbn.getTag());
400 if (settingsIntent != null
401 && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
402 settingsLinkView.setVisibility(VISIBLE);
403 settingsLinkView.setOnClickListener((View view) -> {
404 mAppSettingsClickListener.onClick(view, settingsIntent);
405 });
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000406 } else {
Julia Reynolds52a27372019-04-03 11:00:17 -0400407 settingsLinkView.setVisibility(View.GONE);
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000408 }
Julia Reynolds52a27372019-04-03 11:00:17 -0400409
410 // System Settings button.
411 final View settingsButton = findViewById(R.id.info);
412 settingsButton.setOnClickListener(getSettingsOnClickListener());
413 settingsButton.setVisibility(settingsButton.hasOnClickListeners() ? VISIBLE : GONE);
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000414 }
415
Julia Reynolds52a27372019-04-03 11:00:17 -0400416 private OnClickListener getSettingsOnClickListener() {
417 if (mAppUid >= 0 && mOnSettingsClickListener != null && mIsDeviceProvisioned) {
418 final int appUidF = mAppUid;
419 return ((View view) -> {
420 logBlockingHelperCounter(
421 NotificationCounters.BLOCKING_HELPER_NOTIF_SETTINGS);
422 mOnSettingsClickListener.onClick(view,
423 mNumUniqueChannelsInRow > 1 ? null : mSingleNotificationChannel,
424 appUidF);
425 });
426 }
427 return null;
428 }
429
430 private void bindChannelDetails() throws RemoteException {
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000431 bindName();
Julia Reynolds268647a2018-10-25 16:54:27 -0400432 bindGroup();
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000433 }
434
435 private void bindName() {
436 final TextView channelName = findViewById(R.id.channel_name);
437 if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) {
438 channelName.setVisibility(View.GONE);
439 } else {
440 channelName.setText(mSingleNotificationChannel.getName());
441 }
Julia Reynoldsac98aea2018-10-25 16:54:27 -0400442 }
443
Julia Reynolds268647a2018-10-25 16:54:27 -0400444 private void bindDelegate() {
445 TextView delegateView = findViewById(R.id.delegate_name);
446 TextView dividerView = findViewById(R.id.pkg_divider);
447
448 CharSequence delegatePkg = null;
449 if (!TextUtils.equals(mPackageName, mDelegatePkg)) {
450 // this notification was posted by a delegate!
451 ApplicationInfo info;
452 try {
453 info = mPm.getApplicationInfo(
454 mDelegatePkg,
455 PackageManager.MATCH_UNINSTALLED_PACKAGES
456 | PackageManager.MATCH_DISABLED_COMPONENTS
457 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
458 | PackageManager.MATCH_DIRECT_BOOT_AWARE);
459 if (info != null) {
460 delegatePkg = String.valueOf(mPm.getApplicationLabel(info));
461 }
462 } catch (PackageManager.NameNotFoundException e) { }
463 }
464 if (delegatePkg != null) {
465 delegateView.setText(mContext.getResources().getString(
466 R.string.notification_delegate_header, delegatePkg));
467 delegateView.setVisibility(View.VISIBLE);
468 dividerView.setVisibility(View.VISIBLE);
469 } else {
470 delegateView.setVisibility(View.GONE);
471 dividerView.setVisibility(View.GONE);
472 }
473 }
474
475 private void bindGroup() throws RemoteException {
476 // Set group information if this channel has an associated group.
477 CharSequence groupName = null;
478 if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
479 final NotificationChannelGroup notificationChannelGroup =
480 mINotificationManager.getNotificationChannelGroupForPackage(
481 mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
482 if (notificationChannelGroup != null) {
483 groupName = notificationChannelGroup.getName();
484 }
485 }
486 TextView groupNameView = findViewById(R.id.group_name);
Julia Reynolds268647a2018-10-25 16:54:27 -0400487 if (groupName != null) {
488 groupNameView.setText(groupName);
489 groupNameView.setVisibility(View.VISIBLE);
Julia Reynolds268647a2018-10-25 16:54:27 -0400490 } else {
491 groupNameView.setVisibility(View.GONE);
Julia Reynolds268647a2018-10-25 16:54:27 -0400492 }
493 }
494
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500495
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700496 @VisibleForTesting
497 void logBlockingHelperCounter(String counterTag) {
498 if (mIsForBlockingHelper) {
499 mMetricsLogger.count(counterTag, 1);
500 }
501 }
502
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400503 private boolean hasImportanceChanged() {
Gus Prevas9abc5062018-10-31 16:11:04 -0400504 return mSingleNotificationChannel != null
Gus Prevascaed15c2019-01-18 14:19:51 -0500505 && mChosenImportance != null
Julia Reynolds8728d002019-04-19 15:46:13 -0400506 && (mStartingChannelImportance == IMPORTANCE_UNSPECIFIED
507 || (mWasShownHighPriority && mChosenImportance < IMPORTANCE_DEFAULT)
508 || (!mWasShownHighPriority && mChosenImportance >= IMPORTANCE_DEFAULT));
Mady Mellor87d79452017-01-10 11:52:52 -0800509 }
510
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500511 private void saveImportance() {
Gus Prevas894d9152018-11-12 13:51:40 -0500512 if (!mIsNonblockable
513 || mExitReason != NotificationCounters.BLOCKING_HELPER_STOP_NOTIFICATIONS) {
Gus Prevascaed15c2019-01-18 14:19:51 -0500514 if (mChosenImportance == null) {
515 mChosenImportance = mStartingChannelImportance;
516 }
Gus Prevas533836a2018-09-24 17:15:32 -0400517 updateImportance();
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500518 }
Rohan Shahca0447e2018-03-30 15:18:27 -0700519 }
520
521 /**
522 * Commits the updated importance values on the background thread.
523 */
524 private void updateImportance() {
Gus Prevascaed15c2019-01-18 14:19:51 -0500525 if (mChosenImportance != null) {
526 mMetricsLogger.write(importanceChangeLogMaker());
Rohan Shahca0447e2018-03-30 15:18:27 -0700527
Julia Reynolds8728d002019-04-19 15:46:13 -0400528 int newImportance = mChosenImportance;
529 if (mStartingChannelImportance != IMPORTANCE_UNSPECIFIED) {
530 if ((mWasShownHighPriority && mChosenImportance >= IMPORTANCE_DEFAULT)
531 || (!mWasShownHighPriority && mChosenImportance < IMPORTANCE_DEFAULT)) {
532 newImportance = mStartingChannelImportance;
533 }
534 }
535
Gus Prevascaed15c2019-01-18 14:19:51 -0500536 Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
537 bgHandler.post(
538 new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
539 mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
Julia Reynolds8728d002019-04-19 15:46:13 -0400540 mStartingChannelImportance, newImportance));
Gus Prevascaed15c2019-01-18 14:19:51 -0500541 }
Mady Mellor87d79452017-01-10 11:52:52 -0800542 }
543
Julia Reynolds52a27372019-04-03 11:00:17 -0400544 private void updateButtons(int blockState) {
Julia Reynolds8728d002019-04-19 15:46:13 -0400545 TextView silence = findViewById(R.id.silence);
546 TextView alert = findViewById(R.id.alert);
547 TextView done = findViewById(R.id.done);
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500548 switch (blockState) {
Julia Reynolds52a27372019-04-03 11:00:17 -0400549 case ACTION_TOGGLE_SILENT:
Julia Reynolds8728d002019-04-19 15:46:13 -0400550 updateButtons(silence, alert);
551 if (mWasShownHighPriority) {
552 done.setText(R.string.inline_ok_button);
553 } else {
554 done.setText(R.string.inline_done_button);
555 }
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500556 break;
557 case ACTION_ALERT:
Julia Reynolds8728d002019-04-19 15:46:13 -0400558 updateButtons(alert, silence);
559 if (mWasShownHighPriority) {
560 done.setText(R.string.inline_done_button);
561 } else {
562 done.setText(R.string.inline_ok_button);
563 }
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500564 break;
565 }
566 }
567
Julia Reynolds8728d002019-04-19 15:46:13 -0400568 private void updateButtons(TextView selected, TextView unselected) {
569 selected.setBackground(mSelectedBackground);
570 selected.setSelected(true);
571 selected.setTextAppearance(
572 R.style.TextAppearance_NotificationImportanceButton_Selected);
573 unselected.setBackground(mUnselectedBackground);
574 unselected.setSelected(false);
575 unselected.setTextAppearance(
576 R.style.TextAppearance_NotificationImportanceButton_Unselected);
577 }
578
579 void setImportanceSummary(int blockState) {
580 TextView view = findViewById(R.id.description);
581 if (blockState == ACTION_ALERT) {
582 view.setText(R.string.notification_channel_summary_default);
583 } else {
584 if (mShowInStatusBar) {
585 if (mShowOnLockscreen) {
586 view.setText(R.string.notification_channel_summary_low_status_lock);
587 } else {
588 view.setText(R.string.notification_channel_summary_low_status);
589 }
590 } else if (mShowOnLockscreen) {
591 view.setText(R.string.notification_channel_summary_low_lock);
592 } else {
593 view.setText(R.string.notification_channel_summary_low);
594 }
595 }
596 }
597
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500598 private void saveImportanceAndExitReason(@NotificationInfoAction int action) {
599 switch (action) {
600 case ACTION_UNDO:
601 mChosenImportance = mStartingChannelImportance;
602 break;
603 case ACTION_DELIVER_SILENTLY:
604 mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY;
Julia Reynolds8728d002019-04-19 15:46:13 -0400605 mChosenImportance = mWasShownHighPriority
606 ? IMPORTANCE_LOW : mStartingChannelImportance;
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500607 break;
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500608 default:
609 throw new IllegalArgumentException();
610 }
611 }
612
Julia Reynolds52a27372019-04-03 11:00:17 -0400613 // only used for blocking helper
Gus Prevas894d9152018-11-12 13:51:40 -0500614 private void swapContent(@NotificationInfoAction int action, boolean animate) {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500615 if (mExpandAnimation != null) {
616 mExpandAnimation.cancel();
617 }
Mady Mellor87d79452017-01-10 11:52:52 -0800618
Julia Reynolds52a27372019-04-03 11:00:17 -0400619 View blockingHelper = findViewById(R.id.blocking_helper);
Julia Reynoldse0341482018-03-08 14:42:50 -0500620 ViewGroup confirmation = findViewById(R.id.confirmation);
621 TextView confirmationText = findViewById(R.id.confirmation_text);
Julia Reynoldse0341482018-03-08 14:42:50 -0500622
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500623 saveImportanceAndExitReason(action);
624
Gus Prevas9abc5062018-10-31 16:11:04 -0400625 switch (action) {
Gus Prevas894d9152018-11-12 13:51:40 -0500626 case ACTION_UNDO:
Gus Prevas9abc5062018-10-31 16:11:04 -0400627 break;
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500628 case ACTION_DELIVER_SILENTLY:
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500629 confirmationText.setText(R.string.notification_channel_silenced);
630 break;
Gus Prevas9abc5062018-10-31 16:11:04 -0400631 default:
632 throw new IllegalArgumentException();
Julia Reynolds437cdb12018-01-03 12:27:24 -0500633 }
634
Gus Prevas894d9152018-11-12 13:51:40 -0500635 boolean isUndo = action == ACTION_UNDO;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500636
Julia Reynolds52a27372019-04-03 11:00:17 -0400637 blockingHelper.setVisibility(isUndo ? VISIBLE : GONE);
638 findViewById(R.id.channel_info).setVisibility(isUndo ? VISIBLE : GONE);
639 findViewById(R.id.header).setVisibility(isUndo ? VISIBLE : GONE);
Gus Prevas9abc5062018-10-31 16:11:04 -0400640 confirmation.setVisibility(isUndo ? GONE : VISIBLE);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500641
Gus Prevas894d9152018-11-12 13:51:40 -0500642 if (animate) {
Julia Reynolds52a27372019-04-03 11:00:17 -0400643 ObjectAnimator promptAnim = ObjectAnimator.ofFloat(blockingHelper, View.ALPHA,
644 blockingHelper.getAlpha(), isUndo ? 1f : 0f);
Gus Prevas894d9152018-11-12 13:51:40 -0500645 promptAnim.setInterpolator(isUndo ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
646 ObjectAnimator confirmAnim = ObjectAnimator.ofFloat(confirmation, View.ALPHA,
647 confirmation.getAlpha(), isUndo ? 0f : 1f);
648 confirmAnim.setInterpolator(isUndo ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500649
Gus Prevas894d9152018-11-12 13:51:40 -0500650 mExpandAnimation = new AnimatorSet();
651 mExpandAnimation.playTogether(promptAnim, confirmAnim);
652 mExpandAnimation.setDuration(150);
653 mExpandAnimation.addListener(new AnimatorListenerAdapter() {
654 boolean mCancelled = false;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500655
Gus Prevas894d9152018-11-12 13:51:40 -0500656 @Override
657 public void onAnimationCancel(Animator animation) {
658 mCancelled = true;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500659 }
Gus Prevas894d9152018-11-12 13:51:40 -0500660
661 @Override
662 public void onAnimationEnd(Animator animation) {
663 if (!mCancelled) {
Julia Reynolds52a27372019-04-03 11:00:17 -0400664 blockingHelper.setVisibility(isUndo ? VISIBLE : GONE);
Gus Prevas894d9152018-11-12 13:51:40 -0500665 confirmation.setVisibility(isUndo ? GONE : VISIBLE);
666 }
667 }
668 });
669 mExpandAnimation.start();
670 }
Rohan Shah142e2da2018-06-14 13:14:18 -0700671
672 // Since we're swapping/update the content, reset the timeout so the UI can't close
673 // immediately after the update.
674 if (mGutsContainer != null) {
675 mGutsContainer.resetFalsingCheck();
676 }
Mady Mellor87d79452017-01-10 11:52:52 -0800677 }
678
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400679 @Override
Gus Prevas9abc5062018-10-31 16:11:04 -0400680 public void onFinishedClosing() {
Gus Prevascaed15c2019-01-18 14:19:51 -0500681 if (mChosenImportance != null) {
682 mStartingChannelImportance = mChosenImportance;
Gus Prevas9abc5062018-10-31 16:11:04 -0400683 }
684 mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
685
Julia Reynolds52a27372019-04-03 11:00:17 -0400686 if (mIsForBlockingHelper) {
687 bindBlockingHelper();
688 } else {
689 bindInlineControls();
690 }
Will Brockmana399f022019-01-30 10:59:36 -0500691
692 mMetricsLogger.write(notificationControlsLogMaker().setType(MetricsEvent.TYPE_CLOSE));
Gus Prevas9abc5062018-10-31 16:11:04 -0400693 }
694
695 @Override
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400696 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
697 super.onInitializeAccessibilityEvent(event);
698 if (mGutsContainer != null &&
699 event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
700 if (mGutsContainer.isExposed()) {
701 event.getText().add(mContext.getString(
702 R.string.notification_channel_controls_opened_accessibility, mAppName));
703 } else {
704 event.getText().add(mContext.getString(
705 R.string.notification_channel_controls_closed_accessibility, mAppName));
706 }
707 }
708 }
709
Julia Reynolds3aedded2017-03-31 14:42:09 -0400710 private Intent getAppSettingsIntent(PackageManager pm, String packageName,
711 NotificationChannel channel, int id, String tag) {
712 Intent intent = new Intent(Intent.ACTION_MAIN)
713 .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
714 .setPackage(packageName);
715 final List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
716 intent,
717 PackageManager.MATCH_DEFAULT_ONLY
718 );
719 if (resolveInfos == null || resolveInfos.size() == 0 || resolveInfos.get(0) == null) {
720 return null;
721 }
722 final ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
723 intent.setClassName(activityInfo.packageName, activityInfo.name);
724 if (channel != null) {
725 intent.putExtra(Notification.EXTRA_CHANNEL_ID, channel.getId());
726 }
727 intent.putExtra(Notification.EXTRA_NOTIFICATION_ID, id);
728 intent.putExtra(Notification.EXTRA_NOTIFICATION_TAG, tag);
729 return intent;
730 }
731
Rohan Shahca0447e2018-03-30 15:18:27 -0700732 /**
733 * Closes the controls and commits the updated importance values (indirectly). If this view is
734 * being used to show the blocking helper, this will immediately dismiss the blocking helper and
735 * commit the updated importance.
736 *
737 * <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 -0400738 * user does not have the ability to undo the action anymore. See
739 * {@link #swapContent(boolean, boolean)} for where undo is handled.
Rohan Shahca0447e2018-03-30 15:18:27 -0700740 */
Rohan Shah524cf7b2018-03-15 14:40:02 -0700741 @VisibleForTesting
742 void closeControls(View v) {
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700743 int[] parentLoc = new int[2];
744 int[] targetLoc = new int[2];
745 mGutsContainer.getLocationOnScreen(parentLoc);
746 v.getLocationOnScreen(targetLoc);
747 final int centerX = v.getWidth() / 2;
748 final int centerY = v.getHeight() / 2;
749 final int x = targetLoc[0] - parentLoc[0] + centerX;
750 final int y = targetLoc[1] - parentLoc[1] + centerY;
751 mGutsContainer.closeControls(x, y, true /* save */, false /* force */);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500752 }
753
Mady Mellor87d79452017-01-10 11:52:52 -0800754 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800755 public void setGutsParent(NotificationGuts guts) {
756 mGutsContainer = guts;
Mady Mellor87d79452017-01-10 11:52:52 -0800757 }
758
759 @Override
Mady Mellor434180c2017-02-13 11:29:42 -0800760 public boolean willBeRemoved() {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500761 return hasImportanceChanged();
Mady Mellor434180c2017-02-13 11:29:42 -0800762 }
763
764 @Override
Lucas Dupin9b08c012018-05-16 19:53:32 -0700765 public boolean shouldBeSaved() {
766 return hasImportanceChanged();
767 }
768
769 @Override
Mady Mellor87d79452017-01-10 11:52:52 -0800770 public View getContentView() {
771 return this;
772 }
773
774 @Override
Mady Mellorc2dbe492017-03-30 13:22:03 -0700775 public boolean handleCloseControls(boolean save, boolean force) {
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500776 // Save regardless of the importance so we can lock the importance field if the user wants
777 // to keep getting notifications
778 if (save) {
Rohan Shahca0447e2018-03-30 15:18:27 -0700779 saveImportance();
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500780 }
Rohan Shahdd588c72018-05-09 20:32:15 -0700781 logBlockingHelperCounter(mExitReason);
Mady Mellor87d79452017-01-10 11:52:52 -0800782 return false;
783 }
Mady Mellore09fb702017-03-30 13:23:29 -0700784
785 @Override
786 public int getActualHeight() {
787 return getHeight();
788 }
Rohan Shahca0447e2018-03-30 15:18:27 -0700789
Gus Prevas533836a2018-09-24 17:15:32 -0400790 @VisibleForTesting
791 public boolean isAnimating() {
792 return mExpandAnimation != null && mExpandAnimation.isRunning();
793 }
794
Rohan Shahca0447e2018-03-30 15:18:27 -0700795 /**
796 * Runnable to either update the given channel (with a new importance value) or, if no channel
797 * is provided, update notifications enabled state for the package.
798 */
799 private static class UpdateImportanceRunnable implements Runnable {
800 private final INotificationManager mINotificationManager;
801 private final String mPackageName;
802 private final int mAppUid;
803 private final @Nullable NotificationChannel mChannelToUpdate;
804 private final int mCurrentImportance;
805 private final int mNewImportance;
806
807
808 public UpdateImportanceRunnable(INotificationManager notificationManager,
809 String packageName, int appUid, @Nullable NotificationChannel channelToUpdate,
810 int currentImportance, int newImportance) {
811 mINotificationManager = notificationManager;
812 mPackageName = packageName;
813 mAppUid = appUid;
814 mChannelToUpdate = channelToUpdate;
815 mCurrentImportance = currentImportance;
816 mNewImportance = newImportance;
817 }
818
819 @Override
820 public void run() {
821 try {
822 if (mChannelToUpdate != null) {
823 mChannelToUpdate.setImportance(mNewImportance);
824 mChannelToUpdate.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
825 mINotificationManager.updateNotificationChannelForPackage(
826 mPackageName, mAppUid, mChannelToUpdate);
827 } else {
828 // For notifications with more than one channel, update notification enabled
829 // state. If the importance was lowered, we disable notifications.
Rohan Shah590e1b22018-04-10 23:48:47 -0400830 mINotificationManager.setNotificationsEnabledWithImportanceLockForPackage(
Rohan Shahca0447e2018-03-30 15:18:27 -0700831 mPackageName, mAppUid, mNewImportance >= mCurrentImportance);
832 }
833 } catch (RemoteException e) {
834 Log.e(TAG, "Unable to update notification importance", e);
835 }
836 }
837 }
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200838
Will Brockmana399f022019-01-30 10:59:36 -0500839 /**
840 * Returns a LogMaker with all available notification information.
841 * Caller should set category, type, and maybe subtype, before passing it to mMetricsLogger.
842 * @return LogMaker
843 */
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200844 private LogMaker getLogMaker() {
Will Brockmana399f022019-01-30 10:59:36 -0500845 // The constructor requires a category, so also do it in the other branch for consistency.
846 return mSbn == null ? new LogMaker(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
847 : mSbn.getLogMaker().setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER);
848 }
849
850 /**
851 * Returns an initialized LogMaker for logging importance changes.
852 * The caller may override the type before passing it to mMetricsLogger.
853 * @return LogMaker
854 */
855 private LogMaker importanceChangeLogMaker() {
856 Integer chosenImportance =
857 mChosenImportance != null ? mChosenImportance : mStartingChannelImportance;
858 return getLogMaker().setCategory(MetricsEvent.ACTION_SAVE_IMPORTANCE)
859 .setType(MetricsEvent.TYPE_ACTION)
860 .setSubtype(chosenImportance - mStartingChannelImportance);
861 }
862
863 /**
864 * Returns an initialized LogMaker for logging open/close of the info display.
865 * The caller may override the type before passing it to mMetricsLogger.
866 * @return LogMaker
867 */
868 private LogMaker notificationControlsLogMaker() {
869 return getLogMaker().setCategory(MetricsEvent.ACTION_NOTE_CONTROLS)
870 .setType(MetricsEvent.TYPE_OPEN)
871 .setSubtype(mIsForBlockingHelper ? MetricsEvent.BLOCKING_HELPER_DISPLAY
872 : MetricsEvent.BLOCKING_HELPER_UNKNOWN);
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200873 }
Mady Mellor87d79452017-01-10 11:52:52 -0800874}