blob: f0c93b10578cb559b84ded2e2119e2ed6da0a0c0 [file] [log] [blame]
Mady Mellor87d79452017-01-10 11:52:52 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
Rohan Shah20790b82018-07-02 17:21:04 -070017package com.android.systemui.statusbar.notification.row;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -050018
Gus Prevas9abc5062018-10-31 16:11:04 -040019import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
Gus Prevas9abc5062018-10-31 16:11:04 -040020import static android.app.NotificationManager.IMPORTANCE_LOW;
Julia Reynolds8728d002019-04-19 15:46:13 -040021import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
Julia Reynolds3aedded2017-03-31 14:42:09 -040022
Ned Burns369f1472019-05-22 21:07:26 -040023import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN;
24
25import static java.lang.annotation.RetentionPolicy.SOURCE;
26
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;
Ned Burns369f1472019-05-22 21:07:26 -040045import android.transition.ChangeBounds;
46import android.transition.Fade;
Julia Reynolds4d1dd792019-04-29 11:48:29 -040047import android.transition.TransitionManager;
Ned Burns369f1472019-05-22 21:07:26 -040048import android.transition.TransitionSet;
Mady Mellor87d79452017-01-10 11:52:52 -080049import android.util.AttributeSet;
Rohan Shahca0447e2018-03-30 15:18:27 -070050import android.util.Log;
Mady Mellor87d79452017-01-10 11:52:52 -080051import android.view.View;
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;
Will Brockman4567a4c2020-06-21 17:19:50 -040059import com.android.internal.logging.UiEventLogger;
Mady Mellor87d79452017-01-10 11:52:52 -080060import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Rohan Shah524cf7b2018-03-15 14:40:02 -070061import com.android.systemui.Dependency;
Mady Mellor87d79452017-01-10 11:52:52 -080062import com.android.systemui.R;
Ned Burns9512e0c2019-05-30 19:36:04 -040063import com.android.systemui.statusbar.notification.VisualStabilityManager;
Mady Mellor53162c12019-10-22 17:12:59 -070064import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Mady Mellor87d79452017-01-10 11:52:52 -080065
Ned Burns369f1472019-05-22 21:07:26 -040066import java.lang.annotation.Retention;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050067import java.util.List;
Evan Laird47dc4542019-04-24 15:10:52 -040068import java.util.Set;
Mady Mellor87d79452017-01-10 11:52:52 -080069
70/**
Julia Reynolds143441a2020-03-03 08:57:42 -050071 * The guts of a notification revealed when performing a long press.
Mady Mellor87d79452017-01-10 11:52:52 -080072 */
Mady Mellor95d743c2017-01-10 12:05:27 -080073public class NotificationInfo extends LinearLayout implements NotificationGuts.GutsContent {
Mady Mellor87d79452017-01-10 11:52:52 -080074 private static final String TAG = "InfoGuts";
Selim Cineka5a4ee42020-05-21 15:38:32 -070075 private int mActualHeight;
Mady Mellor87d79452017-01-10 11:52:52 -080076
Gus Prevas894d9152018-11-12 13:51:40 -050077 @IntDef(prefix = { "ACTION_" }, value = {
78 ACTION_NONE,
Julia Reynolds143441a2020-03-03 08:57:42 -050079 ACTION_TOGGLE_ALERT,
Gus Prevas894d9152018-11-12 13:51:40 -050080 ACTION_TOGGLE_SILENT,
Gus Prevas9abc5062018-10-31 16:11:04 -040081 })
Gus Prevas894d9152018-11-12 13:51:40 -050082 public @interface NotificationInfoAction {
83 }
Gus Prevas9abc5062018-10-31 16:11:04 -040084
Gus Prevas894d9152018-11-12 13:51:40 -050085 public static final int ACTION_NONE = 0;
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 // standard controls
Julia Reynolds143441a2020-03-03 08:57:42 -050089 private static final int ACTION_TOGGLE_ALERT = 5;
Gus Prevas9abc5062018-10-31 16:11:04 -040090
Ned Burns369f1472019-05-22 21:07:26 -040091 private TextView mPriorityDescriptionView;
92 private TextView mSilentDescriptionView;
Dan Sandler0ed02292019-05-14 19:18:35 -040093
Mady Mellor87d79452017-01-10 11:52:52 -080094 private INotificationManager mINotificationManager;
Julia Reynolds437cdb12018-01-03 12:27:24 -050095 private PackageManager mPm;
Rohan Shahda5dcdd2018-04-27 17:21:50 -070096 private MetricsLogger mMetricsLogger;
Ned Burns9512e0c2019-05-30 19:36:04 -040097 private VisualStabilityManager mVisualStabilityManager;
Evan Laird47dc4542019-04-24 15:10:52 -040098 private ChannelEditorDialogController mChannelEditorDialogController;
Julia Reynolds437cdb12018-01-03 12:27:24 -050099
Rohan Shahca0447e2018-03-30 15:18:27 -0700100 private String mPackageName;
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400101 private String mAppName;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500102 private int mAppUid;
Julia Reynolds268647a2018-10-25 16:54:27 -0400103 private String mDelegatePkg;
Rohan Shahca0447e2018-03-30 15:18:27 -0700104 private int mNumUniqueChannelsInRow;
Evan Laird47dc4542019-04-24 15:10:52 -0400105 private Set<NotificationChannel> mUniqueChannelsInRow;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500106 private NotificationChannel mSingleNotificationChannel;
Gus Prevas9abc5062018-10-31 16:11:04 -0400107 private int mStartingChannelImportance;
Gus Prevascaed15c2019-01-18 14:19:51 -0500108 private boolean mWasShownHighPriority;
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400109 private boolean mPressedApply;
Evan Laird03cf3502019-05-31 16:46:48 -0400110 private boolean mPresentingChannelEditorDialog = false;
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400111
Gus Prevascaed15c2019-01-18 14:19:51 -0500112 /**
113 * The last importance level chosen by the user. Null if the user has not chosen an importance
114 * level; non-null once the user takes an action which indicates an explicit preference.
115 */
116 @Nullable private Integer mChosenImportance;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500117 private boolean mIsSingleDefaultChannel;
Rohan Shah63411fc2018-03-28 19:05:52 -0700118 private boolean mIsNonblockable;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500119 private StatusBarNotification mSbn;
Julia Reynolds35765d82018-08-17 11:39:19 -0400120 private boolean mIsDeviceProvisioned;
Mady Mellor87d79452017-01-10 11:52:52 -0800121
Julia Reynolds437cdb12018-01-03 12:27:24 -0500122 private OnSettingsClickListener mOnSettingsClickListener;
Julia Reynolds3aedded2017-03-31 14:42:09 -0400123 private OnAppSettingsClickListener mAppSettingsClickListener;
Mady Mellor95d743c2017-01-10 12:05:27 -0800124 private NotificationGuts mGutsContainer;
Evan Laird47dc4542019-04-24 15:10:52 -0400125 private Drawable mPkgIcon;
Will Brockman4567a4c2020-06-21 17:19:50 -0400126 private UiEventLogger mUiEventLogger;
Rohan Shah524cf7b2018-03-15 14:40:02 -0700127
Mady Mellor53162c12019-10-22 17:12:59 -0700128 @VisibleForTesting
129 boolean mSkipPost = false;
130
Julia Reynolds52a27372019-04-03 11:00:17 -0400131 // used by standard ui
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500132 private OnClickListener mOnAlert = v -> {
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500133 mChosenImportance = IMPORTANCE_DEFAULT;
Ned Burns369f1472019-05-22 21:07:26 -0400134 applyAlertingBehavior(BEHAVIOR_ALERTING, true /* userTriggered */);
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500135 };
136
Julia Reynolds52a27372019-04-03 11:00:17 -0400137 // used by standard ui
138 private OnClickListener mOnSilent = v -> {
Julia Reynolds52a27372019-04-03 11:00:17 -0400139 mChosenImportance = IMPORTANCE_LOW;
Ned Burns369f1472019-05-22 21:07:26 -0400140 applyAlertingBehavior(BEHAVIOR_SILENT, true /* userTriggered */);
Julia Reynolds52a27372019-04-03 11:00:17 -0400141 };
142
143 // used by standard ui
Nadia Benbernoub66516c2019-03-06 11:01:53 -0500144 private OnClickListener mOnDismissSettings = v -> {
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400145 mPressedApply = true;
Dave Mankoffc97a0e62020-06-01 12:13:05 -0400146 mGutsContainer.closeControls(v, true);
Nadia Benbernou3b4a93a2019-02-06 16:16:47 -0500147 };
148
Mady Mellor87d79452017-01-10 11:52:52 -0800149 public NotificationInfo(Context context, AttributeSet attrs) {
150 super(context, attrs);
151 }
152
Ned Burns369f1472019-05-22 21:07:26 -0400153 @Override
154 protected void onFinishInflate() {
155 super.onFinishInflate();
156
157 mPriorityDescriptionView = findViewById(R.id.alert_summary);
158 mSilentDescriptionView = findViewById(R.id.silence_summary);
159 }
160
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400161 // Specify a CheckSaveListener to override when/if the user's changes are committed.
162 public interface CheckSaveListener {
163 // Invoked when importance has changed and the NotificationInfo wants to try to save it.
164 // Listener should run saveImportance unless the change should be canceled.
Eliot Courtney47098cb2017-10-18 17:30:30 +0900165 void checkSave(Runnable saveImportance, StatusBarNotification sbn);
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400166 }
167
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500168 public interface OnSettingsClickListener {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500169 void onClick(View v, NotificationChannel channel, int appUid);
Mady Mellor87d79452017-01-10 11:52:52 -0800170 }
171
Julia Reynolds3aedded2017-03-31 14:42:09 -0400172 public interface OnAppSettingsClickListener {
173 void onClick(View v, Intent intent);
174 }
175
Rohan Shah524cf7b2018-03-15 14:40:02 -0700176 public void bindNotification(
177 PackageManager pm,
178 INotificationManager iNotificationManager,
Ned Burns9512e0c2019-05-30 19:36:04 -0400179 VisualStabilityManager visualStabilityManager,
Evan Laird18bd6e62019-08-30 16:39:25 -0400180 ChannelEditorDialogController channelEditorDialogController,
Rohan Shah524cf7b2018-03-15 14:40:02 -0700181 String pkg,
182 NotificationChannel notificationChannel,
Evan Laird47dc4542019-04-24 15:10:52 -0400183 Set<NotificationChannel> uniqueChannelsInRow,
Mady Mellor53162c12019-10-22 17:12:59 -0700184 NotificationEntry entry,
Rohan Shah524cf7b2018-03-15 14:40:02 -0700185 OnSettingsClickListener onSettingsClick,
186 OnAppSettingsClickListener onAppSettingsClick,
Will Brockman4567a4c2020-06-21 17:19:50 -0400187 UiEventLogger uiEventLogger,
Julia Reynolds35765d82018-08-17 11:39:19 -0400188 boolean isDeviceProvisioned,
Rohan Shah63411fc2018-03-28 19:05:52 -0700189 boolean isNonblockable,
Gus Prevascaed15c2019-01-18 14:19:51 -0500190 boolean wasShownHighPriority)
Rohan Shah524cf7b2018-03-15 14:40:02 -0700191 throws RemoteException {
Mady Mellor87d79452017-01-10 11:52:52 -0800192 mINotificationManager = iNotificationManager;
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700193 mMetricsLogger = Dependency.get(MetricsLogger.class);
Ned Burns9512e0c2019-05-30 19:36:04 -0400194 mVisualStabilityManager = visualStabilityManager;
Evan Laird18bd6e62019-08-30 16:39:25 -0400195 mChannelEditorDialogController = channelEditorDialogController;
Rohan Shahca0447e2018-03-30 15:18:27 -0700196 mPackageName = pkg;
Evan Laird47dc4542019-04-24 15:10:52 -0400197 mUniqueChannelsInRow = uniqueChannelsInRow;
198 mNumUniqueChannelsInRow = uniqueChannelsInRow.size();
Mady Mellor53162c12019-10-22 17:12:59 -0700199 mSbn = entry.getSbn();
Julia Reynolds3aedded2017-03-31 14:42:09 -0400200 mPm = pm;
201 mAppSettingsClickListener = onAppSettingsClick;
Rohan Shahca0447e2018-03-30 15:18:27 -0700202 mAppName = mPackageName;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500203 mOnSettingsClickListener = onSettingsClick;
204 mSingleNotificationChannel = notificationChannel;
Gus Prevascaed15c2019-01-18 14:19:51 -0500205 mStartingChannelImportance = mSingleNotificationChannel.getImportance();
206 mWasShownHighPriority = wasShownHighPriority;
Rohan Shah63411fc2018-03-28 19:05:52 -0700207 mIsNonblockable = isNonblockable;
Rohan Shahdbd64e72018-03-28 14:46:50 -0700208 mAppUid = mSbn.getUid();
Julia Reynolds268647a2018-10-25 16:54:27 -0400209 mDelegatePkg = mSbn.getOpPkg();
Julia Reynolds35765d82018-08-17 11:39:19 -0400210 mIsDeviceProvisioned = isDeviceProvisioned;
Will Brockman4567a4c2020-06-21 17:19:50 -0400211 mUiEventLogger = uiEventLogger;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500212
Julia Reynolds437cdb12018-01-03 12:27:24 -0500213 int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400214 pkg, mAppUid, false /* includeDeleted */);
Rohan Shahca0447e2018-03-30 15:18:27 -0700215 if (mNumUniqueChannelsInRow == 0) {
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400216 throw new IllegalArgumentException("bindNotification requires at least one channel");
217 } else {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500218 // Special behavior for the Default channel if no other channels have been defined.
Rohan Shahca0447e2018-03-30 15:18:27 -0700219 mIsSingleDefaultChannel = mNumUniqueChannelsInRow == 1
Rohan Shahdbd64e72018-03-28 14:46:50 -0700220 && mSingleNotificationChannel.getId().equals(
221 NotificationChannel.DEFAULT_CHANNEL_ID)
222 && numTotalChannels == 1;
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400223 }
224
Julia Reynolds437cdb12018-01-03 12:27:24 -0500225 bindHeader();
Julia Reynolds52a27372019-04-03 11:00:17 -0400226 bindChannelDetails();
227
Julia Reynolds143441a2020-03-03 08:57:42 -0500228 bindInlineControls();
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200229
Will Brockman4567a4c2020-06-21 17:19:50 -0400230 logUiEvent(NotificationControlsEvent.NOTIFICATION_CONTROLS_OPEN);
Will Brockmana399f022019-01-30 10:59:36 -0500231 mMetricsLogger.write(notificationControlsLogMaker());
Julia Reynolds437cdb12018-01-03 12:27:24 -0500232 }
233
Julia Reynolds52a27372019-04-03 11:00:17 -0400234 private void bindInlineControls() {
Julia Reynolds52a27372019-04-03 11:00:17 -0400235 if (mIsNonblockable) {
236 findViewById(R.id.non_configurable_text).setVisibility(VISIBLE);
237 findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE);
238 findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
Julia Reynolds8728d002019-04-19 15:46:13 -0400239 ((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button);
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400240 findViewById(R.id.turn_off_notifications).setVisibility(GONE);
Julia Reynolds52a27372019-04-03 11:00:17 -0400241 } else if (mNumUniqueChannelsInRow > 1) {
242 findViewById(R.id.non_configurable_text).setVisibility(GONE);
243 findViewById(R.id.interruptiveness_settings).setVisibility(GONE);
244 findViewById(R.id.non_configurable_multichannel_text).setVisibility(VISIBLE);
245 } else {
246 findViewById(R.id.non_configurable_text).setVisibility(GONE);
247 findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE);
248 findViewById(R.id.interruptiveness_settings).setVisibility(VISIBLE);
249 }
250
251 View turnOffButton = findViewById(R.id.turn_off_notifications);
Evan Laird47dc4542019-04-24 15:10:52 -0400252 turnOffButton.setOnClickListener(getTurnOffNotificationsClickListener());
Julia Reynolds52a27372019-04-03 11:00:17 -0400253 turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() && !mIsNonblockable
254 ? VISIBLE : GONE);
255
256 View done = findViewById(R.id.done);
257 done.setOnClickListener(mOnDismissSettings);
Dave Mankoffc97a0e62020-06-01 12:13:05 -0400258 done.setAccessibilityDelegate(mGutsContainer.getAccessibilityDelegate());
Julia Reynolds52a27372019-04-03 11:00:17 -0400259
Julia Reynolds8728d002019-04-19 15:46:13 -0400260 View silent = findViewById(R.id.silence);
261 View alert = findViewById(R.id.alert);
Julia Reynolds52a27372019-04-03 11:00:17 -0400262 silent.setOnClickListener(mOnSilent);
263 alert.setOnClickListener(mOnAlert);
264
Julia Reynoldsc3e922b2020-01-16 16:12:56 -0500265 int behavior = mWasShownHighPriority
Mady Mellor53162c12019-10-22 17:12:59 -0700266 ? BEHAVIOR_ALERTING
267 : BEHAVIOR_SILENT;
268 applyAlertingBehavior(behavior, false /* userTriggered */);
Julia Reynolds52a27372019-04-03 11:00:17 -0400269 }
270
271 private void bindHeader() {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500272 // Package name
Evan Laird47dc4542019-04-24 15:10:52 -0400273 mPkgIcon = null;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500274 ApplicationInfo info;
275 try {
Rohan Shahca0447e2018-03-30 15:18:27 -0700276 info = mPm.getApplicationInfo(
277 mPackageName,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500278 PackageManager.MATCH_UNINSTALLED_PACKAGES
279 | PackageManager.MATCH_DISABLED_COMPONENTS
280 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
281 | PackageManager.MATCH_DIRECT_BOOT_AWARE);
282 if (info != null) {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500283 mAppName = String.valueOf(mPm.getApplicationLabel(info));
Evan Laird47dc4542019-04-24 15:10:52 -0400284 mPkgIcon = mPm.getApplicationIcon(info);
Geoffrey Pitschee8c81e2017-03-23 11:38:56 -0400285 }
Julia Reynolds437cdb12018-01-03 12:27:24 -0500286 } catch (PackageManager.NameNotFoundException e) {
287 // app is gone, just show package name and generic icon
Evan Laird47dc4542019-04-24 15:10:52 -0400288 mPkgIcon = mPm.getDefaultActivityIcon();
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500289 }
Julia Reynolds143441a2020-03-03 08:57:42 -0500290 ((ImageView) findViewById(R.id.pkg_icon)).setImageDrawable(mPkgIcon);
291 ((TextView) findViewById(R.id.pkg_name)).setText(mAppName);
Mady Mellor87d79452017-01-10 11:52:52 -0800292
Julia Reynolds268647a2018-10-25 16:54:27 -0400293 // Delegate
294 bindDelegate();
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000295
Julia Reynolds52a27372019-04-03 11:00:17 -0400296 // Set up app settings link (i.e. Customize)
297 View settingsLinkView = findViewById(R.id.app_settings);
298 Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName,
299 mSingleNotificationChannel,
300 mSbn.getId(), mSbn.getTag());
301 if (settingsIntent != null
302 && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
303 settingsLinkView.setVisibility(VISIBLE);
304 settingsLinkView.setOnClickListener((View view) -> {
305 mAppSettingsClickListener.onClick(view, settingsIntent);
306 });
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000307 } else {
Julia Reynolds52a27372019-04-03 11:00:17 -0400308 settingsLinkView.setVisibility(View.GONE);
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000309 }
Julia Reynolds52a27372019-04-03 11:00:17 -0400310
311 // System Settings button.
312 final View settingsButton = findViewById(R.id.info);
313 settingsButton.setOnClickListener(getSettingsOnClickListener());
314 settingsButton.setVisibility(settingsButton.hasOnClickListeners() ? VISIBLE : GONE);
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000315 }
316
Julia Reynolds52a27372019-04-03 11:00:17 -0400317 private OnClickListener getSettingsOnClickListener() {
318 if (mAppUid >= 0 && mOnSettingsClickListener != null && mIsDeviceProvisioned) {
319 final int appUidF = mAppUid;
320 return ((View view) -> {
Julia Reynolds52a27372019-04-03 11:00:17 -0400321 mOnSettingsClickListener.onClick(view,
322 mNumUniqueChannelsInRow > 1 ? null : mSingleNotificationChannel,
323 appUidF);
324 });
325 }
326 return null;
327 }
328
Evan Laird47dc4542019-04-24 15:10:52 -0400329 private OnClickListener getTurnOffNotificationsClickListener() {
330 return ((View view) -> {
Evan Laird03cf3502019-05-31 16:46:48 -0400331 if (!mPresentingChannelEditorDialog && mChannelEditorDialogController != null) {
332 mPresentingChannelEditorDialog = true;
333
Evan Laird47dc4542019-04-24 15:10:52 -0400334 mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid,
335 mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener);
Evan Laird03cf3502019-05-31 16:46:48 -0400336 mChannelEditorDialogController.setOnFinishListener(() -> {
337 mPresentingChannelEditorDialog = false;
Dave Mankoffc97a0e62020-06-01 12:13:05 -0400338 mGutsContainer.closeControls(this, false);
Evan Laird03cf3502019-05-31 16:46:48 -0400339 });
Evan Laird47dc4542019-04-24 15:10:52 -0400340 mChannelEditorDialogController.show();
341 }
342 });
343 }
344
Julia Reynolds52a27372019-04-03 11:00:17 -0400345 private void bindChannelDetails() throws RemoteException {
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000346 bindName();
Julia Reynolds268647a2018-10-25 16:54:27 -0400347 bindGroup();
Daniel Sandlerd0a52b62018-10-31 20:13:22 +0000348 }
349
350 private void bindName() {
351 final TextView channelName = findViewById(R.id.channel_name);
352 if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) {
353 channelName.setVisibility(View.GONE);
354 } else {
355 channelName.setText(mSingleNotificationChannel.getName());
356 }
Julia Reynoldsac98aea2018-10-25 16:54:27 -0400357 }
358
Julia Reynolds268647a2018-10-25 16:54:27 -0400359 private void bindDelegate() {
360 TextView delegateView = findViewById(R.id.delegate_name);
Julia Reynolds268647a2018-10-25 16:54:27 -0400361
362 CharSequence delegatePkg = null;
363 if (!TextUtils.equals(mPackageName, mDelegatePkg)) {
364 // this notification was posted by a delegate!
Julia Reynolds268647a2018-10-25 16:54:27 -0400365 delegateView.setVisibility(View.VISIBLE);
Julia Reynolds268647a2018-10-25 16:54:27 -0400366 } else {
367 delegateView.setVisibility(View.GONE);
Julia Reynolds268647a2018-10-25 16:54:27 -0400368 }
369 }
370
371 private void bindGroup() throws RemoteException {
372 // Set group information if this channel has an associated group.
373 CharSequence groupName = null;
374 if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
375 final NotificationChannelGroup notificationChannelGroup =
376 mINotificationManager.getNotificationChannelGroupForPackage(
377 mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
378 if (notificationChannelGroup != null) {
379 groupName = notificationChannelGroup.getName();
380 }
381 }
382 TextView groupNameView = findViewById(R.id.group_name);
Julia Reynolds268647a2018-10-25 16:54:27 -0400383 if (groupName != null) {
384 groupNameView.setText(groupName);
Julia Reynolds143441a2020-03-03 08:57:42 -0500385 groupNameView.setVisibility(VISIBLE);
Julia Reynolds268647a2018-10-25 16:54:27 -0400386 } else {
Julia Reynolds143441a2020-03-03 08:57:42 -0500387 groupNameView.setVisibility(GONE);
Rohan Shahda5dcdd2018-04-27 17:21:50 -0700388 }
389 }
390
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500391 private void saveImportance() {
Julia Reynolds143441a2020-03-03 08:57:42 -0500392 if (!mIsNonblockable) {
Gus Prevascaed15c2019-01-18 14:19:51 -0500393 if (mChosenImportance == null) {
394 mChosenImportance = mStartingChannelImportance;
395 }
Gus Prevas533836a2018-09-24 17:15:32 -0400396 updateImportance();
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500397 }
Rohan Shahca0447e2018-03-30 15:18:27 -0700398 }
399
400 /**
401 * Commits the updated importance values on the background thread.
402 */
403 private void updateImportance() {
Gus Prevascaed15c2019-01-18 14:19:51 -0500404 if (mChosenImportance != null) {
Will Brockman4567a4c2020-06-21 17:19:50 -0400405 logUiEvent(NotificationControlsEvent.NOTIFICATION_CONTROLS_SAVE_IMPORTANCE);
Gus Prevascaed15c2019-01-18 14:19:51 -0500406 mMetricsLogger.write(importanceChangeLogMaker());
Rohan Shahca0447e2018-03-30 15:18:27 -0700407
Julia Reynolds8728d002019-04-19 15:46:13 -0400408 int newImportance = mChosenImportance;
409 if (mStartingChannelImportance != IMPORTANCE_UNSPECIFIED) {
410 if ((mWasShownHighPriority && mChosenImportance >= IMPORTANCE_DEFAULT)
411 || (!mWasShownHighPriority && mChosenImportance < IMPORTANCE_DEFAULT)) {
412 newImportance = mStartingChannelImportance;
413 }
414 }
415
Gus Prevascaed15c2019-01-18 14:19:51 -0500416 Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
417 bgHandler.post(
418 new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
419 mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
Julia Reynolds8728d002019-04-19 15:46:13 -0400420 mStartingChannelImportance, newImportance));
Ned Burns9512e0c2019-05-30 19:36:04 -0400421 mVisualStabilityManager.temporarilyAllowReordering();
Gus Prevascaed15c2019-01-18 14:19:51 -0500422 }
Mady Mellor87d79452017-01-10 11:52:52 -0800423 }
424
Mady Mellor53162c12019-10-22 17:12:59 -0700425 @Override
426 public boolean post(Runnable action) {
427 if (mSkipPost) {
428 action.run();
429 return true;
430 } else {
431 return super.post(action);
432 }
433 }
434
Ned Burns369f1472019-05-22 21:07:26 -0400435 private void applyAlertingBehavior(@AlertingBehavior int behavior, boolean userTriggered) {
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400436 if (userTriggered) {
Ned Burns369f1472019-05-22 21:07:26 -0400437 TransitionSet transition = new TransitionSet();
438 transition.setOrdering(TransitionSet.ORDERING_TOGETHER);
439 transition.addTransition(new Fade(Fade.OUT))
440 .addTransition(new ChangeBounds())
441 .addTransition(
442 new Fade(Fade.IN)
443 .setStartDelay(150)
444 .setDuration(200)
445 .setInterpolator(FAST_OUT_SLOW_IN));
446 transition.setDuration(350);
447 transition.setInterpolator(FAST_OUT_SLOW_IN);
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400448 TransitionManager.beginDelayedTransition(this, transition);
449 }
Ned Burns369f1472019-05-22 21:07:26 -0400450
451 View alert = findViewById(R.id.alert);
452 View silence = findViewById(R.id.silence);
453
454 switch (behavior) {
455 case BEHAVIOR_ALERTING:
Ned Burns369f1472019-05-22 21:07:26 -0400456 mPriorityDescriptionView.setVisibility(VISIBLE);
457 mSilentDescriptionView.setVisibility(GONE);
Julia Reynoldsdaab21a2019-06-05 14:30:05 -0400458 post(() -> {
459 alert.setSelected(true);
460 silence.setSelected(false);
461 });
Ned Burns369f1472019-05-22 21:07:26 -0400462 break;
Julia Reynoldsdaab21a2019-06-05 14:30:05 -0400463
Mady Mellor53162c12019-10-22 17:12:59 -0700464 case BEHAVIOR_SILENT:
Ned Burns369f1472019-05-22 21:07:26 -0400465 mSilentDescriptionView.setVisibility(VISIBLE);
466 mPriorityDescriptionView.setVisibility(GONE);
Julia Reynoldsdaab21a2019-06-05 14:30:05 -0400467 post(() -> {
468 alert.setSelected(false);
469 silence.setSelected(true);
Mady Mellor53162c12019-10-22 17:12:59 -0700470 });
471 break;
472
Ned Burns369f1472019-05-22 21:07:26 -0400473 default:
474 throw new IllegalArgumentException("Unrecognized alerting behavior: " + behavior);
Julia Reynolds8728d002019-04-19 15:46:13 -0400475 }
Ned Burns369f1472019-05-22 21:07:26 -0400476
477 boolean isAChange = mWasShownHighPriority != (behavior == BEHAVIOR_ALERTING);
478 TextView done = findViewById(R.id.done);
Julia Reynoldsc3e922b2020-01-16 16:12:56 -0500479 done.setText(isAChange
Mady Mellor53162c12019-10-22 17:12:59 -0700480 ? R.string.inline_ok_button
481 : R.string.inline_done_button);
Julia Reynolds8728d002019-04-19 15:46:13 -0400482 }
483
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400484 @Override
Gus Prevas9abc5062018-10-31 16:11:04 -0400485 public void onFinishedClosing() {
Gus Prevascaed15c2019-01-18 14:19:51 -0500486 if (mChosenImportance != null) {
487 mStartingChannelImportance = mChosenImportance;
Gus Prevas9abc5062018-10-31 16:11:04 -0400488 }
Gus Prevas9abc5062018-10-31 16:11:04 -0400489
Julia Reynolds143441a2020-03-03 08:57:42 -0500490 bindInlineControls();
Will Brockmana399f022019-01-30 10:59:36 -0500491
Will Brockman4567a4c2020-06-21 17:19:50 -0400492 logUiEvent(NotificationControlsEvent.NOTIFICATION_CONTROLS_CLOSE);
Will Brockmana399f022019-01-30 10:59:36 -0500493 mMetricsLogger.write(notificationControlsLogMaker().setType(MetricsEvent.TYPE_CLOSE));
Gus Prevas9abc5062018-10-31 16:11:04 -0400494 }
495
496 @Override
Julia Reynoldsbe371072020-05-28 11:32:01 -0400497 public boolean needsFalsingProtection() {
498 return true;
499 }
500
501 @Override
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400502 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
503 super.onInitializeAccessibilityEvent(event);
504 if (mGutsContainer != null &&
505 event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
506 if (mGutsContainer.isExposed()) {
507 event.getText().add(mContext.getString(
508 R.string.notification_channel_controls_opened_accessibility, mAppName));
509 } else {
510 event.getText().add(mContext.getString(
511 R.string.notification_channel_controls_closed_accessibility, mAppName));
512 }
513 }
514 }
515
Julia Reynolds3aedded2017-03-31 14:42:09 -0400516 private Intent getAppSettingsIntent(PackageManager pm, String packageName,
517 NotificationChannel channel, int id, String tag) {
518 Intent intent = new Intent(Intent.ACTION_MAIN)
519 .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
520 .setPackage(packageName);
521 final List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
522 intent,
523 PackageManager.MATCH_DEFAULT_ONLY
524 );
525 if (resolveInfos == null || resolveInfos.size() == 0 || resolveInfos.get(0) == null) {
526 return null;
527 }
528 final ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
529 intent.setClassName(activityInfo.packageName, activityInfo.name);
530 if (channel != null) {
531 intent.putExtra(Notification.EXTRA_CHANNEL_ID, channel.getId());
532 }
533 intent.putExtra(Notification.EXTRA_NOTIFICATION_ID, id);
534 intent.putExtra(Notification.EXTRA_NOTIFICATION_TAG, tag);
535 return intent;
536 }
537
Mady Mellor87d79452017-01-10 11:52:52 -0800538 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800539 public void setGutsParent(NotificationGuts guts) {
540 mGutsContainer = guts;
Mady Mellor87d79452017-01-10 11:52:52 -0800541 }
542
543 @Override
Mady Mellor434180c2017-02-13 11:29:42 -0800544 public boolean willBeRemoved() {
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400545 return false;
Mady Mellor434180c2017-02-13 11:29:42 -0800546 }
547
548 @Override
Lucas Dupin9b08c012018-05-16 19:53:32 -0700549 public boolean shouldBeSaved() {
Julia Reynolds4d1dd792019-04-29 11:48:29 -0400550 return mPressedApply;
Lucas Dupin9b08c012018-05-16 19:53:32 -0700551 }
552
553 @Override
Mady Mellor87d79452017-01-10 11:52:52 -0800554 public View getContentView() {
555 return this;
556 }
557
558 @Override
Mady Mellorc2dbe492017-03-30 13:22:03 -0700559 public boolean handleCloseControls(boolean save, boolean force) {
Evan Laird03cf3502019-05-31 16:46:48 -0400560 if (mPresentingChannelEditorDialog && mChannelEditorDialogController != null) {
561 mPresentingChannelEditorDialog = false;
562 // No need for the finish listener because we're closing
563 mChannelEditorDialogController.setOnFinishListener(null);
564 mChannelEditorDialogController.close();
565 }
566
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500567 // Save regardless of the importance so we can lock the importance field if the user wants
568 // to keep getting notifications
569 if (save) {
Rohan Shahca0447e2018-03-30 15:18:27 -0700570 saveImportance();
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500571 }
Mady Mellor87d79452017-01-10 11:52:52 -0800572 return false;
573 }
Mady Mellore09fb702017-03-30 13:23:29 -0700574
575 @Override
576 public int getActualHeight() {
Selim Cineka5a4ee42020-05-21 15:38:32 -0700577 // Because we're animating the bounds, getHeight will return the small height at the
578 // beginning of the animation. Instead we'd want it to already return the end value
579 return mActualHeight;
580 }
581
582 @Override
583 protected void onLayout(boolean changed, int l, int t, int r, int b) {
584 super.onLayout(changed, l, t, r, b);
585 mActualHeight = getHeight();
Mady Mellore09fb702017-03-30 13:23:29 -0700586 }
Rohan Shahca0447e2018-03-30 15:18:27 -0700587
Gus Prevas533836a2018-09-24 17:15:32 -0400588 @VisibleForTesting
589 public boolean isAnimating() {
Julia Reynolds143441a2020-03-03 08:57:42 -0500590 return false;
Gus Prevas533836a2018-09-24 17:15:32 -0400591 }
592
Rohan Shahca0447e2018-03-30 15:18:27 -0700593 /**
594 * Runnable to either update the given channel (with a new importance value) or, if no channel
595 * is provided, update notifications enabled state for the package.
596 */
597 private static class UpdateImportanceRunnable implements Runnable {
598 private final INotificationManager mINotificationManager;
599 private final String mPackageName;
600 private final int mAppUid;
601 private final @Nullable NotificationChannel mChannelToUpdate;
602 private final int mCurrentImportance;
603 private final int mNewImportance;
604
605
606 public UpdateImportanceRunnable(INotificationManager notificationManager,
607 String packageName, int appUid, @Nullable NotificationChannel channelToUpdate,
608 int currentImportance, int newImportance) {
609 mINotificationManager = notificationManager;
610 mPackageName = packageName;
611 mAppUid = appUid;
612 mChannelToUpdate = channelToUpdate;
613 mCurrentImportance = currentImportance;
614 mNewImportance = newImportance;
615 }
616
617 @Override
618 public void run() {
619 try {
620 if (mChannelToUpdate != null) {
621 mChannelToUpdate.setImportance(mNewImportance);
622 mChannelToUpdate.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
623 mINotificationManager.updateNotificationChannelForPackage(
624 mPackageName, mAppUid, mChannelToUpdate);
625 } else {
626 // For notifications with more than one channel, update notification enabled
627 // state. If the importance was lowered, we disable notifications.
Rohan Shah590e1b22018-04-10 23:48:47 -0400628 mINotificationManager.setNotificationsEnabledWithImportanceLockForPackage(
Rohan Shahca0447e2018-03-30 15:18:27 -0700629 mPackageName, mAppUid, mNewImportance >= mCurrentImportance);
630 }
631 } catch (RemoteException e) {
632 Log.e(TAG, "Unable to update notification importance", e);
633 }
634 }
635 }
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200636
Will Brockman4567a4c2020-06-21 17:19:50 -0400637 private void logUiEvent(NotificationControlsEvent event) {
638 if (mSbn != null) {
639 mUiEventLogger.logWithInstanceId(event,
640 mSbn.getUid(), mSbn.getPackageName(), mSbn.getInstanceId());
641 }
642 }
643
Will Brockmana399f022019-01-30 10:59:36 -0500644 /**
645 * Returns a LogMaker with all available notification information.
646 * Caller should set category, type, and maybe subtype, before passing it to mMetricsLogger.
647 * @return LogMaker
648 */
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200649 private LogMaker getLogMaker() {
Will Brockmana399f022019-01-30 10:59:36 -0500650 // The constructor requires a category, so also do it in the other branch for consistency.
651 return mSbn == null ? new LogMaker(MetricsEvent.NOTIFICATION_BLOCKING_HELPER)
652 : mSbn.getLogMaker().setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER);
653 }
654
655 /**
656 * Returns an initialized LogMaker for logging importance changes.
657 * The caller may override the type before passing it to mMetricsLogger.
658 * @return LogMaker
659 */
660 private LogMaker importanceChangeLogMaker() {
661 Integer chosenImportance =
662 mChosenImportance != null ? mChosenImportance : mStartingChannelImportance;
663 return getLogMaker().setCategory(MetricsEvent.ACTION_SAVE_IMPORTANCE)
664 .setType(MetricsEvent.TYPE_ACTION)
665 .setSubtype(chosenImportance - mStartingChannelImportance);
666 }
667
668 /**
669 * Returns an initialized LogMaker for logging open/close of the info display.
670 * The caller may override the type before passing it to mMetricsLogger.
671 * @return LogMaker
672 */
673 private LogMaker notificationControlsLogMaker() {
674 return getLogMaker().setCategory(MetricsEvent.ACTION_NOTE_CONTROLS)
675 .setType(MetricsEvent.TYPE_OPEN)
Julia Reynolds143441a2020-03-03 08:57:42 -0500676 .setSubtype(MetricsEvent.BLOCKING_HELPER_UNKNOWN);
Eyal Posenera9cf9c72018-12-18 16:23:54 +0200677 }
Ned Burns369f1472019-05-22 21:07:26 -0400678
679 @Retention(SOURCE)
Julia Reynoldsc3e922b2020-01-16 16:12:56 -0500680 @IntDef({BEHAVIOR_ALERTING, BEHAVIOR_SILENT})
Ned Burns369f1472019-05-22 21:07:26 -0400681 private @interface AlertingBehavior {}
682 private static final int BEHAVIOR_ALERTING = 0;
683 private static final int BEHAVIOR_SILENT = 1;
Mady Mellor87d79452017-01-10 11:52:52 -0800684}