blob: a9043e4c83bb147906093171b5b8bef99b3876fb [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;
102 if (mNotificationChannels.isEmpty()) {
103 throw new IllegalArgumentException("bindNotification requires at least one channel");
104 } else if (mNotificationChannels.size() == 1) {
105 mSingleNotificationChannel = mNotificationChannels.get(0);
106 mStartingUserImportance = mSingleNotificationChannel.getImportance();
107 } else {
108 mSingleNotificationChannel = null;
109 }
Mady Mellor87d79452017-01-10 11:52:52 -0800110
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500111 String appName = mPkg;
Mady Mellor87d79452017-01-10 11:52:52 -0800112 Drawable pkgicon = null;
Julia Reynolds5a311932017-03-01 16:33:44 -0500113 CharSequence channelNameText = "";
114 ApplicationInfo info = null;
Mady Mellor87d79452017-01-10 11:52:52 -0800115 try {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500116 info = pm.getApplicationInfo(mPkg,
Mady Mellor87d79452017-01-10 11:52:52 -0800117 PackageManager.MATCH_UNINSTALLED_PACKAGES
118 | PackageManager.MATCH_DISABLED_COMPONENTS
119 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
120 | PackageManager.MATCH_DIRECT_BOOT_AWARE);
121 if (info != null) {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500122 mAppUid = info.uid;
Geoffrey Pitsch1f7fb1a2017-02-13 16:57:40 -0500123 appName = String.valueOf(pm.getApplicationLabel(info));
Mady Mellor87d79452017-01-10 11:52:52 -0800124 pkgicon = pm.getApplicationIcon(info);
125 }
126 } catch (PackageManager.NameNotFoundException e) {
127 // app is gone, just show package name and generic icon
128 pkgicon = pm.getDefaultActivityIcon();
129 }
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500130 ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
131
132 int numChannels = 1;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500133 numChannels = iNotificationManager.getNumNotificationChannelsForPackage(
134 pkg, mAppUid, false /* includeDeleted */);
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500135
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500136 String channelsDescText;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500137 mNumChannelsView = (TextView) (findViewById(R.id.num_channels_desc));
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500138 switch (mNotificationChannels.size()) {
139 case 1:
140 channelsDescText = String.format(mContext.getResources().getQuantityString(
141 R.plurals.notification_num_channels_desc, numChannels), numChannels);
142 break;
143 case 2:
144 channelsDescText = mContext.getString(R.string.notification_channels_list_desc_2,
145 mNotificationChannels.get(0).getName(),
146 mNotificationChannels.get(1).getName());
147 break;
148 default:
149 final int numOthers = mNotificationChannels.size() - 2;
150 channelsDescText = String.format(
151 mContext.getResources().getQuantityString(
152 R.plurals.notification_channels_list_desc_2_and_others, numOthers),
153 mNotificationChannels.get(0).getName(),
154 mNotificationChannels.get(1).getName(),
155 numOthers);
156 }
157 mNumChannelsView.setText(channelsDescText);
Mady Mellor87d79452017-01-10 11:52:52 -0800158
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500159 if (mSingleNotificationChannel == null) {
160 // Multiple channels don't use a channel name for the title.
161 channelNameText = mContext.getString(R.string.notification_num_channels,
162 mNotificationChannels.size());
163 } else if (mSingleNotificationChannel.getId()
164 .equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
165 // If this is the placeholder channel, don't use our channel-specific text.
Mady Mellor87d79452017-01-10 11:52:52 -0800166 channelNameText = mContext.getString(R.string.notification_header_default_channel);
167 } else {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500168 channelNameText = mSingleNotificationChannel.getName();
Mady Mellor87d79452017-01-10 11:52:52 -0800169 }
Geoffrey Pitsch1f7fb1a2017-02-13 16:57:40 -0500170 ((TextView) findViewById(R.id.pkgname)).setText(appName);
Mady Mellor87d79452017-01-10 11:52:52 -0800171 ((TextView) findViewById(R.id.channel_name)).setText(channelNameText);
172
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500173 // Set group information if this channel has an associated group.
174 CharSequence groupName = null;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500175 if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
176 final NotificationChannelGroup notificationChannelGroup =
177 iNotificationManager.getNotificationChannelGroupForPackage(
178 mSingleNotificationChannel.getGroup(), pkg, mAppUid);
179 if (notificationChannelGroup != null) {
180 groupName = notificationChannelGroup.getName();
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500181 }
Mady Mellor87d79452017-01-10 11:52:52 -0800182 }
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500183 TextView groupNameView = ((TextView) findViewById(R.id.group_name));
184 TextView groupDividerView = ((TextView) findViewById(R.id.pkg_group_divider));
185 if (groupName != null) {
186 groupNameView.setText(groupName);
187 groupNameView.setVisibility(View.VISIBLE);
188 groupDividerView.setVisibility(View.VISIBLE);
189 } else {
190 groupNameView.setVisibility(View.GONE);
191 groupDividerView.setVisibility(View.GONE);
192 }
Mady Mellor87d79452017-01-10 11:52:52 -0800193
194 boolean nonBlockable = false;
195 try {
Julia Reynolds5a311932017-03-01 16:33:44 -0500196 final PackageInfo pkgInfo = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
197 nonBlockable = Utils.isSystemPackage(getResources(), pm, pkgInfo);
Mady Mellor87d79452017-01-10 11:52:52 -0800198 } catch (PackageManager.NameNotFoundException e) {
199 // unlikely.
200 }
201 if (nonBlockablePkgs != null) {
202 nonBlockable |= nonBlockablePkgs.contains(pkg);
203 }
204
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500205 bindButtons(nonBlockable);
Mady Mellor87d79452017-01-10 11:52:52 -0800206
207 // Top-level importance group
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500208 mChannelDisabledView = findViewById(R.id.channel_disabled);
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500209 updateSecondaryText();
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500210
211 // Settings button.
212 final TextView settingsButton = (TextView) findViewById(R.id.more_settings);
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500213 if (mAppUid >= 0 && onSettingsClick != null) {
214 final int appUidF = mAppUid;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500215 settingsButton.setOnClickListener(
216 (View view) -> {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500217 onSettingsClick.onClick(view, mSingleNotificationChannel, appUidF);
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500218 });
219 if (numChannels > 1) {
220 settingsButton.setText(R.string.notification_all_categories);
221 } else {
222 settingsButton.setText(R.string.notification_more_settings);
223 }
224
225 } else {
226 settingsButton.setVisibility(View.GONE);
227 }
228
229 // Done button.
230 final TextView doneButton = (TextView) findViewById(R.id.done);
231 doneButton.setText(R.string.notification_done);
232 doneButton.setOnClickListener(onDoneClick);
Mady Mellor87d79452017-01-10 11:52:52 -0800233 }
234
235 public boolean hasImportanceChanged() {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500236 return mSingleNotificationChannel != null &&
237 mStartingUserImportance != getSelectedImportance();
Mady Mellor87d79452017-01-10 11:52:52 -0800238 }
239
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500240 private void saveImportance() {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500241 if (mSingleNotificationChannel == null) {
242 return;
243 }
Mady Mellor87d79452017-01-10 11:52:52 -0800244 int selectedImportance = getSelectedImportance();
245 if (selectedImportance == mStartingUserImportance) {
246 return;
247 }
248 MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
249 selectedImportance - mStartingUserImportance);
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500250 mSingleNotificationChannel.setImportance(selectedImportance);
Mady Mellor87d79452017-01-10 11:52:52 -0800251 try {
252 mINotificationManager.updateNotificationChannelForPackage(
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500253 mPkg, mAppUid, mSingleNotificationChannel);
Mady Mellor87d79452017-01-10 11:52:52 -0800254 } catch (RemoteException e) {
255 // :(
256 }
257 }
258
259 private int getSelectedImportance() {
260 if (!mChannelEnabledSwitch.isChecked()) {
261 return NotificationManager.IMPORTANCE_NONE;
Mady Mellor87d79452017-01-10 11:52:52 -0800262 } else {
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500263 return mStartingUserImportance;
Mady Mellor87d79452017-01-10 11:52:52 -0800264 }
265 }
266
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500267 private void bindButtons(final boolean nonBlockable) {
Mady Mellor87d79452017-01-10 11:52:52 -0800268 // Enabled Switch
269 mChannelEnabledSwitch = (Switch) findViewById(R.id.channel_enabled_switch);
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500270 mChannelEnabledSwitch.setChecked(
271 mStartingUserImportance != NotificationManager.IMPORTANCE_NONE);
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500272 final boolean visible = !nonBlockable && mSingleNotificationChannel != null;
273 mChannelEnabledSwitch.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
Mady Mellor87d79452017-01-10 11:52:52 -0800274
Mady Mellor87d79452017-01-10 11:52:52 -0800275 // Callback when checked.
276 mChannelEnabledSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500277 if (mGutsInteractionListener != null) {
278 mGutsInteractionListener.onInteraction(NotificationInfo.this);
279 }
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500280 updateSecondaryText();
Mady Mellor87d79452017-01-10 11:52:52 -0800281 });
Mady Mellor87d79452017-01-10 11:52:52 -0800282 }
283
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500284 private void updateSecondaryText() {
285 final boolean defaultChannel = mSingleNotificationChannel != null &&
286 mSingleNotificationChannel.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID);
287 final boolean disabled = mSingleNotificationChannel != null &&
288 getSelectedImportance() == NotificationManager.IMPORTANCE_NONE;
289 if (defaultChannel) {
290 // Don't show any secondary text if this is from the default channel.
291 mChannelDisabledView.setVisibility(View.GONE);
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500292 mNumChannelsView.setVisibility(View.GONE);
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500293 } else if (disabled) {
294 mChannelDisabledView.setVisibility(View.VISIBLE);
295 mNumChannelsView.setVisibility(View.GONE);
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500296 } else {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500297 mChannelDisabledView.setVisibility(View.GONE);
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500298 mNumChannelsView.setVisibility(View.VISIBLE);
Mady Mellor87d79452017-01-10 11:52:52 -0800299 }
300 }
301
302 @Override
303 public void setInteractionListener(GutsInteractionListener listener) {
304 mGutsInteractionListener = listener;
305 }
306
307 @Override
Mady Mellor434180c2017-02-13 11:29:42 -0800308 public boolean willBeRemoved() {
309 return !mChannelEnabledSwitch.isChecked();
310 }
311
312 @Override
Mady Mellor87d79452017-01-10 11:52:52 -0800313 public View getContentView() {
314 return this;
315 }
316
317 @Override
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500318 public boolean handleCloseControls(boolean save) {
319 if (save) {
320 saveImportance();
321 }
Mady Mellor87d79452017-01-10 11:52:52 -0800322 return false;
323 }
324}