blob: 54921a7108c64d237ed9e8790e5fb9f52692c1bf [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
Mady Mellor87d79452017-01-10 11:52:52 -080019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.app.INotificationManager;
22import android.app.NotificationChannel;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -050023import android.app.NotificationChannelGroup;
Mady Mellor87d79452017-01-10 11:52:52 -080024import android.app.NotificationManager;
25import android.content.Context;
26import android.content.pm.ApplicationInfo;
27import android.content.pm.PackageInfo;
28import android.content.pm.PackageManager;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -050029import android.content.pm.ParceledListSlice;
Mady Mellor87d79452017-01-10 11:52:52 -080030import android.content.res.ColorStateList;
31import android.content.res.TypedArray;
32import android.graphics.Canvas;
33import android.graphics.drawable.Drawable;
34import android.os.Handler;
35import android.os.RemoteException;
36import android.os.ServiceManager;
37import android.service.notification.NotificationListenerService;
Mady Mellor87d79452017-01-10 11:52:52 -080038import android.util.AttributeSet;
39import android.util.Log;
40import android.view.View;
41import android.view.ViewAnimationUtils;
42import android.view.ViewGroup;
43import android.view.View.OnClickListener;
44import android.widget.CompoundButton;
45import android.widget.ImageView;
46import android.widget.LinearLayout;
Mady Mellor87d79452017-01-10 11:52:52 -080047import android.widget.SeekBar;
48import android.widget.Switch;
49import android.widget.TextView;
50
51import com.android.internal.logging.MetricsLogger;
52import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
53import com.android.settingslib.Utils;
54import com.android.systemui.Interpolators;
55import com.android.systemui.R;
56import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.GutsContent;
57import com.android.systemui.plugins.statusbar.NotificationMenuRowProvider.GutsInteractionListener;
58import com.android.systemui.statusbar.NotificationGuts.OnSettingsClickListener;
59import com.android.systemui.statusbar.stack.StackStateAnimator;
60
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050061import java.lang.IllegalArgumentException;
62import java.util.List;
Mady Mellor87d79452017-01-10 11:52:52 -080063import java.util.Set;
64
65/**
66 * The guts of a notification revealed when performing a long press.
67 */
68public class NotificationInfo extends LinearLayout implements GutsContent {
69 private static final String TAG = "InfoGuts";
70
71 private INotificationManager mINotificationManager;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050072 private String mPkg;
73 private int mAppUid;
74 private List<NotificationChannel> mNotificationChannels;
75 private NotificationChannel mSingleNotificationChannel;
Mady Mellor87d79452017-01-10 11:52:52 -080076 private int mStartingUserImportance;
Mady Mellor87d79452017-01-10 11:52:52 -080077
Geoffrey Pitschdf44b602017-02-03 13:31:50 -050078 private TextView mNumChannelsView;
79 private View mChannelDisabledView;
Mady Mellor87d79452017-01-10 11:52:52 -080080 private Switch mChannelEnabledSwitch;
Mady Mellor87d79452017-01-10 11:52:52 -080081
82 private GutsInteractionListener mGutsInteractionListener;
83
84 public NotificationInfo(Context context, AttributeSet attrs) {
85 super(context, attrs);
86 }
87
Jason Monk2a6ea9c2017-01-26 11:14:51 -050088 public interface OnSettingsClickListener {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050089 void onClick(View v, NotificationChannel channel, int appUid);
Mady Mellor87d79452017-01-10 11:52:52 -080090 }
91
Jason Monk2a6ea9c2017-01-26 11:14:51 -050092 public void bindNotification(final PackageManager pm,
93 final INotificationManager iNotificationManager,
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050094 final String pkg,
95 final List<NotificationChannel> notificationChannels,
Mady Mellor87d79452017-01-10 11:52:52 -080096 OnSettingsClickListener onSettingsClick,
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050097 OnClickListener onDoneClick, final Set<String> nonBlockablePkgs)
98 throws RemoteException {
Mady Mellor87d79452017-01-10 11:52:52 -080099 mINotificationManager = iNotificationManager;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500100 mPkg = pkg;
101 mNotificationChannels = notificationChannels;
Geoffrey Pitschee8c81e2017-03-23 11:38:56 -0400102 boolean isSingleDefaultChannel = false;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500103 if (mNotificationChannels.isEmpty()) {
104 throw new IllegalArgumentException("bindNotification requires at least one channel");
105 } else if (mNotificationChannels.size() == 1) {
106 mSingleNotificationChannel = mNotificationChannels.get(0);
107 mStartingUserImportance = mSingleNotificationChannel.getImportance();
Geoffrey Pitschee8c81e2017-03-23 11:38:56 -0400108 isSingleDefaultChannel = mSingleNotificationChannel.getId()
109 .equals(NotificationChannel.DEFAULT_CHANNEL_ID);
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500110 } else {
111 mSingleNotificationChannel = null;
112 }
Mady Mellor87d79452017-01-10 11:52:52 -0800113
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500114 String appName = mPkg;
Mady Mellor87d79452017-01-10 11:52:52 -0800115 Drawable pkgicon = null;
Julia Reynolds5a311932017-03-01 16:33:44 -0500116 CharSequence channelNameText = "";
117 ApplicationInfo info = null;
Mady Mellor87d79452017-01-10 11:52:52 -0800118 try {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500119 info = pm.getApplicationInfo(mPkg,
Mady Mellor87d79452017-01-10 11:52:52 -0800120 PackageManager.MATCH_UNINSTALLED_PACKAGES
121 | PackageManager.MATCH_DISABLED_COMPONENTS
122 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
123 | PackageManager.MATCH_DIRECT_BOOT_AWARE);
124 if (info != null) {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500125 mAppUid = info.uid;
Geoffrey Pitsch1f7fb1a2017-02-13 16:57:40 -0500126 appName = String.valueOf(pm.getApplicationLabel(info));
Mady Mellor87d79452017-01-10 11:52:52 -0800127 pkgicon = pm.getApplicationIcon(info);
128 }
129 } catch (PackageManager.NameNotFoundException e) {
130 // app is gone, just show package name and generic icon
131 pkgicon = pm.getDefaultActivityIcon();
132 }
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500133 ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
134
135 int numChannels = 1;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500136 numChannels = iNotificationManager.getNumNotificationChannelsForPackage(
137 pkg, mAppUid, false /* includeDeleted */);
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500138
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500139 String channelsDescText;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500140 mNumChannelsView = (TextView) (findViewById(R.id.num_channels_desc));
Geoffrey Pitschee8c81e2017-03-23 11:38:56 -0400141 if (isSingleDefaultChannel) {
142 channelsDescText = mContext.getString(R.string.notification_default_channel_desc);
143 } else {
144 switch (mNotificationChannels.size()) {
145 case 1:
146 channelsDescText = String.format(mContext.getResources().getQuantityString(
147 R.plurals.notification_num_channels_desc, numChannels), numChannels);
148 break;
149 case 2:
150 channelsDescText = mContext.getString(
151 R.string.notification_channels_list_desc_2,
152 mNotificationChannels.get(0).getName(),
153 mNotificationChannels.get(1).getName());
154 break;
155 default:
156 final int numOthers = mNotificationChannels.size() - 2;
157 channelsDescText = String.format(
158 mContext.getResources().getQuantityString(
159 R.plurals.notification_channels_list_desc_2_and_others,
160 numOthers),
161 mNotificationChannels.get(0).getName(),
162 mNotificationChannels.get(1).getName(),
163 numOthers);
164 }
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500165 }
166 mNumChannelsView.setText(channelsDescText);
Mady Mellor87d79452017-01-10 11:52:52 -0800167
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500168 if (mSingleNotificationChannel == null) {
169 // Multiple channels don't use a channel name for the title.
170 channelNameText = mContext.getString(R.string.notification_num_channels,
171 mNotificationChannels.size());
Geoffrey Pitschee8c81e2017-03-23 11:38:56 -0400172 } else if (isSingleDefaultChannel) {
173 // If this is the default channel, don't use our channel-specific text.
Mady Mellor87d79452017-01-10 11:52:52 -0800174 channelNameText = mContext.getString(R.string.notification_header_default_channel);
175 } else {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500176 channelNameText = mSingleNotificationChannel.getName();
Mady Mellor87d79452017-01-10 11:52:52 -0800177 }
Geoffrey Pitsch1f7fb1a2017-02-13 16:57:40 -0500178 ((TextView) findViewById(R.id.pkgname)).setText(appName);
Mady Mellor87d79452017-01-10 11:52:52 -0800179 ((TextView) findViewById(R.id.channel_name)).setText(channelNameText);
180
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500181 // Set group information if this channel has an associated group.
182 CharSequence groupName = null;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500183 if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
184 final NotificationChannelGroup notificationChannelGroup =
185 iNotificationManager.getNotificationChannelGroupForPackage(
186 mSingleNotificationChannel.getGroup(), pkg, mAppUid);
187 if (notificationChannelGroup != null) {
188 groupName = notificationChannelGroup.getName();
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500189 }
Mady Mellor87d79452017-01-10 11:52:52 -0800190 }
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500191 TextView groupNameView = ((TextView) findViewById(R.id.group_name));
192 TextView groupDividerView = ((TextView) findViewById(R.id.pkg_group_divider));
193 if (groupName != null) {
194 groupNameView.setText(groupName);
195 groupNameView.setVisibility(View.VISIBLE);
196 groupDividerView.setVisibility(View.VISIBLE);
197 } else {
198 groupNameView.setVisibility(View.GONE);
199 groupDividerView.setVisibility(View.GONE);
200 }
Mady Mellor87d79452017-01-10 11:52:52 -0800201
202 boolean nonBlockable = false;
203 try {
Julia Reynolds5a311932017-03-01 16:33:44 -0500204 final PackageInfo pkgInfo = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
205 nonBlockable = Utils.isSystemPackage(getResources(), pm, pkgInfo);
Mady Mellor87d79452017-01-10 11:52:52 -0800206 } catch (PackageManager.NameNotFoundException e) {
207 // unlikely.
208 }
209 if (nonBlockablePkgs != null) {
210 nonBlockable |= nonBlockablePkgs.contains(pkg);
211 }
212
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500213 bindButtons(nonBlockable);
Mady Mellor87d79452017-01-10 11:52:52 -0800214
215 // Top-level importance group
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500216 mChannelDisabledView = findViewById(R.id.channel_disabled);
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500217 updateSecondaryText();
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500218
219 // Settings button.
220 final TextView settingsButton = (TextView) findViewById(R.id.more_settings);
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500221 if (mAppUid >= 0 && onSettingsClick != null) {
222 final int appUidF = mAppUid;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500223 settingsButton.setOnClickListener(
224 (View view) -> {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500225 onSettingsClick.onClick(view, mSingleNotificationChannel, appUidF);
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500226 });
227 if (numChannels > 1) {
228 settingsButton.setText(R.string.notification_all_categories);
229 } else {
230 settingsButton.setText(R.string.notification_more_settings);
231 }
232
233 } else {
234 settingsButton.setVisibility(View.GONE);
235 }
236
237 // Done button.
238 final TextView doneButton = (TextView) findViewById(R.id.done);
239 doneButton.setText(R.string.notification_done);
240 doneButton.setOnClickListener(onDoneClick);
Mady Mellor87d79452017-01-10 11:52:52 -0800241 }
242
243 public boolean hasImportanceChanged() {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500244 return mSingleNotificationChannel != null &&
245 mStartingUserImportance != getSelectedImportance();
Mady Mellor87d79452017-01-10 11:52:52 -0800246 }
247
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500248 private void saveImportance() {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500249 if (mSingleNotificationChannel == null) {
250 return;
251 }
Mady Mellor87d79452017-01-10 11:52:52 -0800252 int selectedImportance = getSelectedImportance();
253 if (selectedImportance == mStartingUserImportance) {
254 return;
255 }
256 MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
257 selectedImportance - mStartingUserImportance);
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500258 mSingleNotificationChannel.setImportance(selectedImportance);
Mady Mellor87d79452017-01-10 11:52:52 -0800259 try {
260 mINotificationManager.updateNotificationChannelForPackage(
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500261 mPkg, mAppUid, mSingleNotificationChannel);
Mady Mellor87d79452017-01-10 11:52:52 -0800262 } catch (RemoteException e) {
263 // :(
264 }
265 }
266
267 private int getSelectedImportance() {
268 if (!mChannelEnabledSwitch.isChecked()) {
269 return NotificationManager.IMPORTANCE_NONE;
Mady Mellor87d79452017-01-10 11:52:52 -0800270 } else {
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500271 return mStartingUserImportance;
Mady Mellor87d79452017-01-10 11:52:52 -0800272 }
273 }
274
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500275 private void bindButtons(final boolean nonBlockable) {
Mady Mellor87d79452017-01-10 11:52:52 -0800276 // Enabled Switch
277 mChannelEnabledSwitch = (Switch) findViewById(R.id.channel_enabled_switch);
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500278 mChannelEnabledSwitch.setChecked(
279 mStartingUserImportance != NotificationManager.IMPORTANCE_NONE);
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500280 final boolean visible = !nonBlockable && mSingleNotificationChannel != null;
281 mChannelEnabledSwitch.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
Mady Mellor87d79452017-01-10 11:52:52 -0800282
Mady Mellor87d79452017-01-10 11:52:52 -0800283 // Callback when checked.
284 mChannelEnabledSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500285 if (mGutsInteractionListener != null) {
286 mGutsInteractionListener.onInteraction(NotificationInfo.this);
287 }
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500288 updateSecondaryText();
Mady Mellor87d79452017-01-10 11:52:52 -0800289 });
Mady Mellor87d79452017-01-10 11:52:52 -0800290 }
291
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500292 private void updateSecondaryText() {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500293 final boolean disabled = mSingleNotificationChannel != null &&
294 getSelectedImportance() == NotificationManager.IMPORTANCE_NONE;
Geoffrey Pitschee8c81e2017-03-23 11:38:56 -0400295 if (disabled) {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500296 mChannelDisabledView.setVisibility(View.VISIBLE);
297 mNumChannelsView.setVisibility(View.GONE);
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500298 } else {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500299 mChannelDisabledView.setVisibility(View.GONE);
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500300 mNumChannelsView.setVisibility(View.VISIBLE);
Mady Mellor87d79452017-01-10 11:52:52 -0800301 }
302 }
303
304 @Override
305 public void setInteractionListener(GutsInteractionListener listener) {
306 mGutsInteractionListener = listener;
307 }
308
309 @Override
Mady Mellor434180c2017-02-13 11:29:42 -0800310 public boolean willBeRemoved() {
311 return !mChannelEnabledSwitch.isChecked();
312 }
313
314 @Override
Mady Mellor87d79452017-01-10 11:52:52 -0800315 public View getContentView() {
316 return this;
317 }
318
319 @Override
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500320 public boolean handleCloseControls(boolean save) {
321 if (save) {
322 saveImportance();
323 }
Mady Mellor87d79452017-01-10 11:52:52 -0800324 return false;
325 }
326}