blob: 806206448dcdb0da32c1efbd5ae2019d6cb3774f [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
Geoffrey Pitschdf44b602017-02-03 13:31:50 -050017package com.android.systemui.statusbar;
18
Julia Reynoldse0341482018-03-08 14:42:50 -050019import static android.app.NotificationManager.IMPORTANCE_MIN;
Julia Reynolds3aedded2017-03-31 14:42:09 -040020import static android.app.NotificationManager.IMPORTANCE_NONE;
21
Julia Reynolds437cdb12018-01-03 12:27:24 -050022import android.animation.Animator;
23import android.animation.AnimatorListenerAdapter;
24import android.animation.AnimatorSet;
25import android.animation.ObjectAnimator;
Rohan Shahca0447e2018-03-30 15:18:27 -070026import android.annotation.Nullable;
Mady Mellor87d79452017-01-10 11:52:52 -080027import android.app.INotificationManager;
Julia Reynolds3aedded2017-03-31 14:42:09 -040028import android.app.Notification;
Mady Mellor87d79452017-01-10 11:52:52 -080029import android.app.NotificationChannel;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -050030import android.app.NotificationChannelGroup;
Mady Mellor87d79452017-01-10 11:52:52 -080031import android.content.Context;
Julia Reynolds3aedded2017-03-31 14:42:09 -040032import android.content.Intent;
33import android.content.pm.ActivityInfo;
Mady Mellor87d79452017-01-10 11:52:52 -080034import android.content.pm.ApplicationInfo;
Mady Mellor87d79452017-01-10 11:52:52 -080035import android.content.pm.PackageManager;
Julia Reynolds3aedded2017-03-31 14:42:09 -040036import android.content.pm.ResolveInfo;
Mady Mellor87d79452017-01-10 11:52:52 -080037import android.graphics.drawable.Drawable;
Rohan Shahca0447e2018-03-30 15:18:27 -070038import android.os.Handler;
Mady Mellor87d79452017-01-10 11:52:52 -080039import android.os.RemoteException;
Julia Reynolds3aedded2017-03-31 14:42:09 -040040import android.service.notification.StatusBarNotification;
41import android.text.TextUtils;
Mady Mellor87d79452017-01-10 11:52:52 -080042import android.util.AttributeSet;
Rohan Shahca0447e2018-03-30 15:18:27 -070043import android.util.Log;
Mady Mellor87d79452017-01-10 11:52:52 -080044import android.view.View;
Julia Reynoldse0341482018-03-08 14:42:50 -050045import android.view.ViewGroup;
Geoffrey Pitschd94e7882017-04-06 09:52:11 -040046import android.view.accessibility.AccessibilityEvent;
Mady Mellor87d79452017-01-10 11:52:52 -080047import android.widget.ImageView;
48import android.widget.LinearLayout;
Mady Mellor87d79452017-01-10 11:52:52 -080049import android.widget.TextView;
50
Rohan Shah524cf7b2018-03-15 14:40:02 -070051import com.android.internal.annotations.VisibleForTesting;
Mady Mellor87d79452017-01-10 11:52:52 -080052import com.android.internal.logging.MetricsLogger;
53import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Rohan Shah524cf7b2018-03-15 14:40:02 -070054import com.android.systemui.Dependency;
Julia Reynolds437cdb12018-01-03 12:27:24 -050055import com.android.systemui.Interpolators;
Mady Mellor87d79452017-01-10 11:52:52 -080056import com.android.systemui.R;
Mady Mellor87d79452017-01-10 11:52:52 -080057
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050058import java.util.List;
Mady Mellor87d79452017-01-10 11:52:52 -080059
60/**
61 * The guts of a notification revealed when performing a long press.
62 */
Mady Mellor95d743c2017-01-10 12:05:27 -080063public class NotificationInfo extends LinearLayout implements NotificationGuts.GutsContent {
Mady Mellor87d79452017-01-10 11:52:52 -080064 private static final String TAG = "InfoGuts";
65
66 private INotificationManager mINotificationManager;
Julia Reynolds437cdb12018-01-03 12:27:24 -050067 private PackageManager mPm;
68
Rohan Shahca0447e2018-03-30 15:18:27 -070069 private String mPackageName;
Geoffrey Pitschd94e7882017-04-06 09:52:11 -040070 private String mAppName;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050071 private int mAppUid;
Rohan Shahca0447e2018-03-30 15:18:27 -070072 private int mNumUniqueChannelsInRow;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050073 private NotificationChannel mSingleNotificationChannel;
Mady Mellor87d79452017-01-10 11:52:52 -080074 private int mStartingUserImportance;
Julia Reynolds437cdb12018-01-03 12:27:24 -050075 private int mChosenImportance;
76 private boolean mIsSingleDefaultChannel;
Rohan Shah63411fc2018-03-28 19:05:52 -070077 private boolean mIsNonblockable;
Julia Reynolds437cdb12018-01-03 12:27:24 -050078 private StatusBarNotification mSbn;
79 private AnimatorSet mExpandAnimation;
Julia Reynoldse0341482018-03-08 14:42:50 -050080 private boolean mIsForeground;
Mady Mellor87d79452017-01-10 11:52:52 -080081
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -040082 private CheckSaveListener mCheckSaveListener;
Julia Reynolds437cdb12018-01-03 12:27:24 -050083 private OnSettingsClickListener mOnSettingsClickListener;
Julia Reynolds3aedded2017-03-31 14:42:09 -040084 private OnAppSettingsClickListener mAppSettingsClickListener;
Mady Mellor95d743c2017-01-10 12:05:27 -080085 private NotificationGuts mGutsContainer;
Rohan Shah524cf7b2018-03-15 14:40:02 -070086
87 /** Whether this view is being shown as part of the blocking helper */
88 private boolean mIsForBlockingHelper;
Julia Reynolds0ef7d842018-01-24 17:50:31 -050089 private boolean mNegativeUserSentiment;
Mady Mellor87d79452017-01-10 11:52:52 -080090
Rohan Shah524cf7b2018-03-15 14:40:02 -070091 private OnClickListener mOnKeepShowing = this::closeControls;
Julia Reynolds437cdb12018-01-03 12:27:24 -050092
Rohan Shahca0447e2018-03-30 15:18:27 -070093 private OnClickListener mOnStopOrMinimizeNotifications = v -> {
Julia Reynolds437cdb12018-01-03 12:27:24 -050094 swapContent(false);
95 };
96
97 private OnClickListener mOnUndo = v -> {
98 swapContent(true);
99 };
100
Mady Mellor87d79452017-01-10 11:52:52 -0800101 public NotificationInfo(Context context, AttributeSet attrs) {
102 super(context, attrs);
103 }
104
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400105 // Specify a CheckSaveListener to override when/if the user's changes are committed.
106 public interface CheckSaveListener {
107 // Invoked when importance has changed and the NotificationInfo wants to try to save it.
108 // Listener should run saveImportance unless the change should be canceled.
Eliot Courtney47098cb2017-10-18 17:30:30 +0900109 void checkSave(Runnable saveImportance, StatusBarNotification sbn);
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400110 }
111
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500112 public interface OnSettingsClickListener {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500113 void onClick(View v, NotificationChannel channel, int appUid);
Mady Mellor87d79452017-01-10 11:52:52 -0800114 }
115
Julia Reynolds3aedded2017-03-31 14:42:09 -0400116 public interface OnAppSettingsClickListener {
117 void onClick(View v, Intent intent);
118 }
119
Rohan Shah524cf7b2018-03-15 14:40:02 -0700120 @VisibleForTesting
121 void bindNotification(
122 final PackageManager pm,
Jason Monk2a6ea9c2017-01-26 11:14:51 -0500123 final INotificationManager iNotificationManager,
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500124 final String pkg,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500125 final NotificationChannel notificationChannel,
Rohan Shahca0447e2018-03-30 15:18:27 -0700126 final int numUniqueChannelsInRow,
Julia Reynolds3aedded2017-03-31 14:42:09 -0400127 final StatusBarNotification sbn,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500128 final CheckSaveListener checkSaveListener,
129 final OnSettingsClickListener onSettingsClick,
130 final OnAppSettingsClickListener onAppSettingsClick,
Rohan Shah63411fc2018-03-28 19:05:52 -0700131 boolean isNonblockable)
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500132 throws RemoteException {
Rohan Shahca0447e2018-03-30 15:18:27 -0700133 bindNotification(pm, iNotificationManager, pkg, notificationChannel,
134 numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
135 onAppSettingsClick, isNonblockable, false /* isBlockingHelper */,
Rohan Shah524cf7b2018-03-15 14:40:02 -0700136 false /* isUserSentimentNegative */);
Julia Reynolds0ef7d842018-01-24 17:50:31 -0500137 }
138
Rohan Shah524cf7b2018-03-15 14:40:02 -0700139 public void bindNotification(
140 PackageManager pm,
141 INotificationManager iNotificationManager,
142 String pkg,
143 NotificationChannel notificationChannel,
Rohan Shahca0447e2018-03-30 15:18:27 -0700144 int numUniqueChannelsInRow,
Rohan Shah524cf7b2018-03-15 14:40:02 -0700145 StatusBarNotification sbn,
146 CheckSaveListener checkSaveListener,
147 OnSettingsClickListener onSettingsClick,
148 OnAppSettingsClickListener onAppSettingsClick,
Rohan Shah63411fc2018-03-28 19:05:52 -0700149 boolean isNonblockable,
Rohan Shah524cf7b2018-03-15 14:40:02 -0700150 boolean isForBlockingHelper,
151 boolean isUserSentimentNegative)
152 throws RemoteException {
Mady Mellor87d79452017-01-10 11:52:52 -0800153 mINotificationManager = iNotificationManager;
Rohan Shahca0447e2018-03-30 15:18:27 -0700154 mPackageName = pkg;
155 mNumUniqueChannelsInRow = numUniqueChannelsInRow;
Julia Reynolds3aedded2017-03-31 14:42:09 -0400156 mSbn = sbn;
157 mPm = pm;
158 mAppSettingsClickListener = onAppSettingsClick;
Rohan Shahca0447e2018-03-30 15:18:27 -0700159 mAppName = mPackageName;
Julia Reynolds437cdb12018-01-03 12:27:24 -0500160 mCheckSaveListener = checkSaveListener;
161 mOnSettingsClickListener = onSettingsClick;
162 mSingleNotificationChannel = notificationChannel;
163 mStartingUserImportance = mChosenImportance = mSingleNotificationChannel.getImportance();
Rohan Shah524cf7b2018-03-15 14:40:02 -0700164 mNegativeUserSentiment = isUserSentimentNegative;
Rohan Shah63411fc2018-03-28 19:05:52 -0700165 mIsNonblockable = isNonblockable;
Julia Reynoldse0341482018-03-08 14:42:50 -0500166 mIsForeground =
167 (mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
Rohan Shah524cf7b2018-03-15 14:40:02 -0700168 mIsForBlockingHelper = isForBlockingHelper;
Rohan Shahdbd64e72018-03-28 14:46:50 -0700169 mAppUid = mSbn.getUid();
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500170
Julia Reynolds437cdb12018-01-03 12:27:24 -0500171 int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400172 pkg, mAppUid, false /* includeDeleted */);
Rohan Shahca0447e2018-03-30 15:18:27 -0700173 if (mNumUniqueChannelsInRow == 0) {
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400174 throw new IllegalArgumentException("bindNotification requires at least one channel");
175 } else {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500176 // Special behavior for the Default channel if no other channels have been defined.
Rohan Shahca0447e2018-03-30 15:18:27 -0700177 mIsSingleDefaultChannel = mNumUniqueChannelsInRow == 1
Rohan Shahdbd64e72018-03-28 14:46:50 -0700178 && mSingleNotificationChannel.getId().equals(
179 NotificationChannel.DEFAULT_CHANNEL_ID)
180 && numTotalChannels == 1;
Geoffrey Pitschd034d292017-05-12 11:59:20 -0400181 }
182
Julia Reynolds437cdb12018-01-03 12:27:24 -0500183 bindHeader();
184 bindPrompt();
185 bindButtons();
186 }
187
188 private void bindHeader() throws RemoteException {
189 // Package name
190 Drawable pkgicon = null;
191 ApplicationInfo info;
192 try {
Rohan Shahca0447e2018-03-30 15:18:27 -0700193 info = mPm.getApplicationInfo(
194 mPackageName,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500195 PackageManager.MATCH_UNINSTALLED_PACKAGES
196 | PackageManager.MATCH_DISABLED_COMPONENTS
197 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
198 | PackageManager.MATCH_DIRECT_BOOT_AWARE);
199 if (info != null) {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500200 mAppName = String.valueOf(mPm.getApplicationLabel(info));
201 pkgicon = mPm.getApplicationIcon(info);
Geoffrey Pitschee8c81e2017-03-23 11:38:56 -0400202 }
Julia Reynolds437cdb12018-01-03 12:27:24 -0500203 } catch (PackageManager.NameNotFoundException e) {
204 // app is gone, just show package name and generic icon
205 pkgicon = mPm.getDefaultActivityIcon();
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500206 }
Julia Reynolds437cdb12018-01-03 12:27:24 -0500207 ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400208 ((TextView) findViewById(R.id.pkgname)).setText(mAppName);
Mady Mellor87d79452017-01-10 11:52:52 -0800209
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500210 // Set group information if this channel has an associated group.
211 CharSequence groupName = null;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500212 if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
213 final NotificationChannelGroup notificationChannelGroup =
Julia Reynolds437cdb12018-01-03 12:27:24 -0500214 mINotificationManager.getNotificationChannelGroupForPackage(
Rohan Shahca0447e2018-03-30 15:18:27 -0700215 mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500216 if (notificationChannelGroup != null) {
217 groupName = notificationChannelGroup.getName();
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500218 }
Mady Mellor87d79452017-01-10 11:52:52 -0800219 }
Julia Reynolds437cdb12018-01-03 12:27:24 -0500220 TextView groupNameView = findViewById(R.id.group_name);
221 TextView groupDividerView = findViewById(R.id.pkg_group_divider);
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500222 if (groupName != null) {
223 groupNameView.setText(groupName);
224 groupNameView.setVisibility(View.VISIBLE);
225 groupDividerView.setVisibility(View.VISIBLE);
226 } else {
227 groupNameView.setVisibility(View.GONE);
228 groupDividerView.setVisibility(View.GONE);
229 }
Mady Mellor87d79452017-01-10 11:52:52 -0800230
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500231 // Settings button.
Julia Reynolds437cdb12018-01-03 12:27:24 -0500232 final View settingsButton = findViewById(R.id.info);
233 if (mAppUid >= 0 && mOnSettingsClickListener != null) {
Geoffrey Pitsch029a3fa2017-03-30 13:01:57 -0400234 settingsButton.setVisibility(View.VISIBLE);
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500235 final int appUidF = mAppUid;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500236 settingsButton.setOnClickListener(
237 (View view) -> {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500238 mOnSettingsClickListener.onClick(view,
Rohan Shahca0447e2018-03-30 15:18:27 -0700239 mNumUniqueChannelsInRow > 1 ? null : mSingleNotificationChannel,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500240 appUidF);
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500241 });
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500242 } else {
243 settingsButton.setVisibility(View.GONE);
244 }
Julia Reynolds437cdb12018-01-03 12:27:24 -0500245 }
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500246
Julia Reynolds437cdb12018-01-03 12:27:24 -0500247 private void bindPrompt() {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500248 final TextView blockPrompt = findViewById(R.id.block_prompt);
Julia Reynolds0ef7d842018-01-24 17:50:31 -0500249 bindName();
Rohan Shah63411fc2018-03-28 19:05:52 -0700250 if (mIsNonblockable) {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500251 blockPrompt.setText(R.string.notification_unblockable_desc);
252 } else {
Julia Reynolds0ef7d842018-01-24 17:50:31 -0500253 if (mNegativeUserSentiment) {
254 blockPrompt.setText(R.string.inline_blocking_helper);
Rohan Shahca0447e2018-03-30 15:18:27 -0700255 } else if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500256 blockPrompt.setText(R.string.inline_keep_showing_app);
257 } else {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500258 blockPrompt.setText(R.string.inline_keep_showing);
259 }
260 }
Mady Mellor87d79452017-01-10 11:52:52 -0800261 }
262
Julia Reynolds0ef7d842018-01-24 17:50:31 -0500263 private void bindName() {
264 final TextView channelName = findViewById(R.id.channel_name);
Rohan Shahca0447e2018-03-30 15:18:27 -0700265 if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) {
Julia Reynolds0ef7d842018-01-24 17:50:31 -0500266 channelName.setVisibility(View.GONE);
267 } else {
268 channelName.setText(mSingleNotificationChannel.getName());
269 }
270 }
271
Geoffrey Pitsch5278d3d2017-03-29 13:39:10 -0400272 private boolean hasImportanceChanged() {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500273 return mSingleNotificationChannel != null && mStartingUserImportance != mChosenImportance;
Mady Mellor87d79452017-01-10 11:52:52 -0800274 }
275
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500276 private void saveImportance() {
Rohan Shahca0447e2018-03-30 15:18:27 -0700277 if (!mIsNonblockable) {
278 if (mCheckSaveListener != null) {
279 mCheckSaveListener.checkSave(this::updateImportance, mSbn);
280 } else {
281 updateImportance();
282 }
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500283 }
Rohan Shahca0447e2018-03-30 15:18:27 -0700284 }
285
286 /**
287 * Commits the updated importance values on the background thread.
288 */
289 private void updateImportance() {
Mady Mellor87d79452017-01-10 11:52:52 -0800290 MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500291 mChosenImportance - mStartingUserImportance);
Rohan Shahca0447e2018-03-30 15:18:27 -0700292
293 Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
294 bgHandler.post(new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
295 mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
296 mStartingUserImportance, mChosenImportance));
Mady Mellor87d79452017-01-10 11:52:52 -0800297 }
298
Julia Reynolds437cdb12018-01-03 12:27:24 -0500299 private void bindButtons() {
Rohan Shah524cf7b2018-03-15 14:40:02 -0700300 // Set up stay-in-notification actions
Julia Reynolds437cdb12018-01-03 12:27:24 -0500301 View block = findViewById(R.id.block);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500302 TextView keep = findViewById(R.id.keep);
Julia Reynoldse0341482018-03-08 14:42:50 -0500303 View minimize = findViewById(R.id.minimize);
Rohan Shah524cf7b2018-03-15 14:40:02 -0700304
305 findViewById(R.id.undo).setOnClickListener(mOnUndo);
Rohan Shahca0447e2018-03-30 15:18:27 -0700306 block.setOnClickListener(mOnStopOrMinimizeNotifications);
Rohan Shah524cf7b2018-03-15 14:40:02 -0700307 keep.setOnClickListener(mOnKeepShowing);
Rohan Shahca0447e2018-03-30 15:18:27 -0700308 minimize.setOnClickListener(mOnStopOrMinimizeNotifications);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500309
Rohan Shah63411fc2018-03-28 19:05:52 -0700310 if (mIsNonblockable) {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500311 keep.setText(R.string.notification_done);
312 block.setVisibility(GONE);
Julia Reynoldse0341482018-03-08 14:42:50 -0500313 minimize.setVisibility(GONE);
314 } else if (mIsForeground) {
315 block.setVisibility(GONE);
316 minimize.setVisibility(VISIBLE);
317 } else if (!mIsForeground) {
318 block.setVisibility(VISIBLE);
319 minimize.setVisibility(GONE);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500320 }
321
Rohan Shah7c6b37a2018-03-30 21:07:45 +0000322 // Set up app settings link (i.e. Customize)
Julia Reynolds437cdb12018-01-03 12:27:24 -0500323 TextView settingsLinkView = findViewById(R.id.app_settings);
Rohan Shahca0447e2018-03-30 15:18:27 -0700324 Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName, mSingleNotificationChannel,
Julia Reynolds437cdb12018-01-03 12:27:24 -0500325 mSbn.getId(), mSbn.getTag());
Rohan Shah7c6b37a2018-03-30 21:07:45 +0000326 if (!mIsForBlockingHelper
327 && settingsIntent != null
Julia Reynolds437cdb12018-01-03 12:27:24 -0500328 && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
Julia Reynoldse0341482018-03-08 14:42:50 -0500329 settingsLinkView.setVisibility(VISIBLE);
Rohan Shah7c6b37a2018-03-30 21:07:45 +0000330 settingsLinkView.setText(mContext.getString(R.string.notification_app_settings));
Julia Reynolds437cdb12018-01-03 12:27:24 -0500331 settingsLinkView.setOnClickListener((View view) -> {
332 mAppSettingsClickListener.onClick(view, settingsIntent);
333 });
Mady Mellor87d79452017-01-10 11:52:52 -0800334 } else {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500335 settingsLinkView.setVisibility(View.GONE);
Mady Mellor87d79452017-01-10 11:52:52 -0800336 }
337 }
338
Julia Reynolds437cdb12018-01-03 12:27:24 -0500339 private void swapContent(boolean showPrompt) {
340 if (mExpandAnimation != null) {
341 mExpandAnimation.cancel();
342 }
Mady Mellor87d79452017-01-10 11:52:52 -0800343
Julia Reynoldse0341482018-03-08 14:42:50 -0500344 View prompt = findViewById(R.id.prompt);
345 ViewGroup confirmation = findViewById(R.id.confirmation);
346 TextView confirmationText = findViewById(R.id.confirmation_text);
347 View header = findViewById(R.id.header);
348
Julia Reynolds437cdb12018-01-03 12:27:24 -0500349 if (showPrompt) {
350 mChosenImportance = mStartingUserImportance;
Julia Reynoldse0341482018-03-08 14:42:50 -0500351 } else if (mIsForeground) {
352 mChosenImportance = IMPORTANCE_MIN;
353 confirmationText.setText(R.string.notification_channel_minimized);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500354 } else {
355 mChosenImportance = IMPORTANCE_NONE;
Julia Reynoldse0341482018-03-08 14:42:50 -0500356 confirmationText.setText(R.string.notification_channel_disabled);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500357 }
358
Julia Reynolds437cdb12018-01-03 12:27:24 -0500359 ObjectAnimator promptAnim = ObjectAnimator.ofFloat(prompt, View.ALPHA,
360 prompt.getAlpha(), showPrompt ? 1f : 0f);
361 promptAnim.setInterpolator(showPrompt ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
362 ObjectAnimator confirmAnim = ObjectAnimator.ofFloat(confirmation, View.ALPHA,
363 confirmation.getAlpha(), showPrompt ? 0f : 1f);
364 confirmAnim.setInterpolator(showPrompt ? Interpolators.ALPHA_OUT : Interpolators.ALPHA_IN);
365
366 prompt.setVisibility(showPrompt ? VISIBLE : GONE);
367 confirmation.setVisibility(showPrompt ? GONE : VISIBLE);
Julia Reynoldse0341482018-03-08 14:42:50 -0500368 header.setVisibility(showPrompt ? VISIBLE : GONE);
Julia Reynolds437cdb12018-01-03 12:27:24 -0500369
370 mExpandAnimation = new AnimatorSet();
371 mExpandAnimation.playTogether(promptAnim, confirmAnim);
372 mExpandAnimation.setDuration(150);
373 mExpandAnimation.addListener(new AnimatorListenerAdapter() {
374 boolean cancelled = false;
375
376 @Override
377 public void onAnimationCancel(Animator animation) {
378 cancelled = true;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500379 }
Julia Reynolds437cdb12018-01-03 12:27:24 -0500380
381 @Override
382 public void onAnimationEnd(Animator animation) {
383 if (!cancelled) {
384 prompt.setVisibility(showPrompt ? VISIBLE : GONE);
385 confirmation.setVisibility(showPrompt ? GONE : VISIBLE);
386 }
387 }
Mady Mellor87d79452017-01-10 11:52:52 -0800388 });
Julia Reynolds437cdb12018-01-03 12:27:24 -0500389 mExpandAnimation.start();
Mady Mellor87d79452017-01-10 11:52:52 -0800390 }
391
Geoffrey Pitschd94e7882017-04-06 09:52:11 -0400392 @Override
393 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
394 super.onInitializeAccessibilityEvent(event);
395 if (mGutsContainer != null &&
396 event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
397 if (mGutsContainer.isExposed()) {
398 event.getText().add(mContext.getString(
399 R.string.notification_channel_controls_opened_accessibility, mAppName));
400 } else {
401 event.getText().add(mContext.getString(
402 R.string.notification_channel_controls_closed_accessibility, mAppName));
403 }
404 }
405 }
406
Julia Reynolds3aedded2017-03-31 14:42:09 -0400407 private Intent getAppSettingsIntent(PackageManager pm, String packageName,
408 NotificationChannel channel, int id, String tag) {
409 Intent intent = new Intent(Intent.ACTION_MAIN)
410 .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
411 .setPackage(packageName);
412 final List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
413 intent,
414 PackageManager.MATCH_DEFAULT_ONLY
415 );
416 if (resolveInfos == null || resolveInfos.size() == 0 || resolveInfos.get(0) == null) {
417 return null;
418 }
419 final ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
420 intent.setClassName(activityInfo.packageName, activityInfo.name);
421 if (channel != null) {
422 intent.putExtra(Notification.EXTRA_CHANNEL_ID, channel.getId());
423 }
424 intent.putExtra(Notification.EXTRA_NOTIFICATION_ID, id);
425 intent.putExtra(Notification.EXTRA_NOTIFICATION_TAG, tag);
426 return intent;
427 }
428
Rohan Shahca0447e2018-03-30 15:18:27 -0700429 /**
430 * Closes the controls and commits the updated importance values (indirectly). If this view is
431 * being used to show the blocking helper, this will immediately dismiss the blocking helper and
432 * commit the updated importance.
433 *
434 * <p><b>Note,</b> this will only get called once the view is dismissing. This means that the
435 * user does not have the ability to undo the action anymore. See {@link #swapContent(boolean)}
436 * for where undo is handled.
437 */
Rohan Shah524cf7b2018-03-15 14:40:02 -0700438 @VisibleForTesting
439 void closeControls(View v) {
440 if (mIsForBlockingHelper) {
441 NotificationBlockingHelperManager manager =
442 Dependency.get(NotificationBlockingHelperManager.class);
443 manager.dismissCurrentBlockingHelper();
Rohan Shahca0447e2018-03-30 15:18:27 -0700444
445 // Since this won't get a callback via gutsContainer.closeControls, save the new
446 // importance values immediately.
447 saveImportance();
Rohan Shah524cf7b2018-03-15 14:40:02 -0700448 } else {
449 int[] parentLoc = new int[2];
450 int[] targetLoc = new int[2];
451 mGutsContainer.getLocationOnScreen(parentLoc);
452 v.getLocationOnScreen(targetLoc);
453 final int centerX = v.getWidth() / 2;
454 final int centerY = v.getHeight() / 2;
455 final int x = targetLoc[0] - parentLoc[0] + centerX;
456 final int y = targetLoc[1] - parentLoc[1] + centerY;
457 mGutsContainer.closeControls(x, y, true /* save */, false /* force */);
458 }
Julia Reynolds437cdb12018-01-03 12:27:24 -0500459 }
460
Mady Mellor87d79452017-01-10 11:52:52 -0800461 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800462 public void setGutsParent(NotificationGuts guts) {
463 mGutsContainer = guts;
Mady Mellor87d79452017-01-10 11:52:52 -0800464 }
465
466 @Override
Mady Mellor434180c2017-02-13 11:29:42 -0800467 public boolean willBeRemoved() {
Julia Reynolds437cdb12018-01-03 12:27:24 -0500468 return hasImportanceChanged();
Mady Mellor434180c2017-02-13 11:29:42 -0800469 }
470
471 @Override
Mady Mellor87d79452017-01-10 11:52:52 -0800472 public View getContentView() {
473 return this;
474 }
475
476 @Override
Mady Mellorc2dbe492017-03-30 13:22:03 -0700477 public boolean handleCloseControls(boolean save, boolean force) {
Julia Reynoldsc65656a2018-02-12 09:55:14 -0500478 // Save regardless of the importance so we can lock the importance field if the user wants
479 // to keep getting notifications
480 if (save) {
Rohan Shahca0447e2018-03-30 15:18:27 -0700481 saveImportance();
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500482 }
Mady Mellor87d79452017-01-10 11:52:52 -0800483 return false;
484 }
Mady Mellore09fb702017-03-30 13:23:29 -0700485
486 @Override
487 public int getActualHeight() {
488 return getHeight();
489 }
Rohan Shahca0447e2018-03-30 15:18:27 -0700490
491 /**
492 * Runnable to either update the given channel (with a new importance value) or, if no channel
493 * is provided, update notifications enabled state for the package.
494 */
495 private static class UpdateImportanceRunnable implements Runnable {
496 private final INotificationManager mINotificationManager;
497 private final String mPackageName;
498 private final int mAppUid;
499 private final @Nullable NotificationChannel mChannelToUpdate;
500 private final int mCurrentImportance;
501 private final int mNewImportance;
502
503
504 public UpdateImportanceRunnable(INotificationManager notificationManager,
505 String packageName, int appUid, @Nullable NotificationChannel channelToUpdate,
506 int currentImportance, int newImportance) {
507 mINotificationManager = notificationManager;
508 mPackageName = packageName;
509 mAppUid = appUid;
510 mChannelToUpdate = channelToUpdate;
511 mCurrentImportance = currentImportance;
512 mNewImportance = newImportance;
513 }
514
515 @Override
516 public void run() {
517 try {
518 if (mChannelToUpdate != null) {
519 mChannelToUpdate.setImportance(mNewImportance);
520 mChannelToUpdate.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
521 mINotificationManager.updateNotificationChannelForPackage(
522 mPackageName, mAppUid, mChannelToUpdate);
523 } else {
524 // For notifications with more than one channel, update notification enabled
525 // state. If the importance was lowered, we disable notifications.
Rohan Shah590e1b22018-04-10 23:48:47 -0400526 mINotificationManager.setNotificationsEnabledWithImportanceLockForPackage(
Rohan Shahca0447e2018-03-30 15:18:27 -0700527 mPackageName, mAppUid, mNewImportance >= mCurrentImportance);
528 }
529 } catch (RemoteException e) {
530 Log.e(TAG, "Unable to update notification importance", e);
531 }
532 }
533 }
Mady Mellor87d79452017-01-10 11:52:52 -0800534}