blob: 8d432c4767f0c85b730b5c3d187da53d621548c2 [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;
Mady Mellor87d79452017-01-10 11:52:52 -080056import com.android.systemui.statusbar.NotificationGuts.OnSettingsClickListener;
57import com.android.systemui.statusbar.stack.StackStateAnimator;
58
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050059import java.lang.IllegalArgumentException;
60import java.util.List;
Mady Mellor87d79452017-01-10 11:52:52 -080061import java.util.Set;
62
63/**
64 * The guts of a notification revealed when performing a long press.
65 */
Mady Mellor95d743c2017-01-10 12:05:27 -080066public class NotificationInfo extends LinearLayout implements NotificationGuts.GutsContent {
Mady Mellor87d79452017-01-10 11:52:52 -080067 private static final String TAG = "InfoGuts";
68
69 private INotificationManager mINotificationManager;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050070 private String mPkg;
71 private int mAppUid;
72 private List<NotificationChannel> mNotificationChannels;
73 private NotificationChannel mSingleNotificationChannel;
Mady Mellor87d79452017-01-10 11:52:52 -080074 private int mStartingUserImportance;
Mady Mellor87d79452017-01-10 11:52:52 -080075
Geoffrey Pitschdf44b602017-02-03 13:31:50 -050076 private TextView mNumChannelsView;
77 private View mChannelDisabledView;
Mady Mellor87d79452017-01-10 11:52:52 -080078 private Switch mChannelEnabledSwitch;
Mady Mellor87d79452017-01-10 11:52:52 -080079
Mady Mellor95d743c2017-01-10 12:05:27 -080080 private NotificationGuts mGutsContainer;
Mady Mellor87d79452017-01-10 11:52:52 -080081
82 public NotificationInfo(Context context, AttributeSet attrs) {
83 super(context, attrs);
84 }
85
Jason Monk2a6ea9c2017-01-26 11:14:51 -050086 public interface OnSettingsClickListener {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050087 void onClick(View v, NotificationChannel channel, int appUid);
Mady Mellor87d79452017-01-10 11:52:52 -080088 }
89
Jason Monk2a6ea9c2017-01-26 11:14:51 -050090 public void bindNotification(final PackageManager pm,
91 final INotificationManager iNotificationManager,
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050092 final String pkg,
93 final List<NotificationChannel> notificationChannels,
Mady Mellor87d79452017-01-10 11:52:52 -080094 OnSettingsClickListener onSettingsClick,
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050095 OnClickListener onDoneClick, final Set<String> nonBlockablePkgs)
96 throws RemoteException {
Mady Mellor87d79452017-01-10 11:52:52 -080097 mINotificationManager = iNotificationManager;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -050098 mPkg = pkg;
99 mNotificationChannels = notificationChannels;
100 if (mNotificationChannels.isEmpty()) {
101 throw new IllegalArgumentException("bindNotification requires at least one channel");
102 } else if (mNotificationChannels.size() == 1) {
103 mSingleNotificationChannel = mNotificationChannels.get(0);
104 mStartingUserImportance = mSingleNotificationChannel.getImportance();
105 } else {
106 mSingleNotificationChannel = null;
107 }
Mady Mellor87d79452017-01-10 11:52:52 -0800108
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500109 String appName = mPkg;
Mady Mellor87d79452017-01-10 11:52:52 -0800110 Drawable pkgicon = null;
Julia Reynolds5a311932017-03-01 16:33:44 -0500111 CharSequence channelNameText = "";
112 ApplicationInfo info = null;
Mady Mellor87d79452017-01-10 11:52:52 -0800113 try {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500114 info = pm.getApplicationInfo(mPkg,
Mady Mellor87d79452017-01-10 11:52:52 -0800115 PackageManager.MATCH_UNINSTALLED_PACKAGES
116 | PackageManager.MATCH_DISABLED_COMPONENTS
117 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
118 | PackageManager.MATCH_DIRECT_BOOT_AWARE);
119 if (info != null) {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500120 mAppUid = info.uid;
Geoffrey Pitsch1f7fb1a2017-02-13 16:57:40 -0500121 appName = String.valueOf(pm.getApplicationLabel(info));
Mady Mellor87d79452017-01-10 11:52:52 -0800122 pkgicon = pm.getApplicationIcon(info);
123 }
124 } catch (PackageManager.NameNotFoundException e) {
125 // app is gone, just show package name and generic icon
126 pkgicon = pm.getDefaultActivityIcon();
127 }
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500128 ((ImageView) findViewById(R.id.pkgicon)).setImageDrawable(pkgicon);
129
130 int numChannels = 1;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500131 numChannels = iNotificationManager.getNumNotificationChannelsForPackage(
132 pkg, mAppUid, false /* includeDeleted */);
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500133
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500134 String channelsDescText;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500135 mNumChannelsView = (TextView) (findViewById(R.id.num_channels_desc));
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500136 switch (mNotificationChannels.size()) {
137 case 1:
138 channelsDescText = String.format(mContext.getResources().getQuantityString(
139 R.plurals.notification_num_channels_desc, numChannels), numChannels);
140 break;
141 case 2:
142 channelsDescText = mContext.getString(R.string.notification_channels_list_desc_2,
143 mNotificationChannels.get(0).getName(),
144 mNotificationChannels.get(1).getName());
145 break;
146 default:
147 final int numOthers = mNotificationChannels.size() - 2;
148 channelsDescText = String.format(
149 mContext.getResources().getQuantityString(
150 R.plurals.notification_channels_list_desc_2_and_others, numOthers),
151 mNotificationChannels.get(0).getName(),
152 mNotificationChannels.get(1).getName(),
153 numOthers);
154 }
155 mNumChannelsView.setText(channelsDescText);
Mady Mellor87d79452017-01-10 11:52:52 -0800156
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500157 if (mSingleNotificationChannel == null) {
158 // Multiple channels don't use a channel name for the title.
159 channelNameText = mContext.getString(R.string.notification_num_channels,
160 mNotificationChannels.size());
161 } else if (mSingleNotificationChannel.getId()
162 .equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
163 // If this is the placeholder channel, don't use our channel-specific text.
Mady Mellor87d79452017-01-10 11:52:52 -0800164 channelNameText = mContext.getString(R.string.notification_header_default_channel);
165 } else {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500166 channelNameText = mSingleNotificationChannel.getName();
Mady Mellor87d79452017-01-10 11:52:52 -0800167 }
Geoffrey Pitsch1f7fb1a2017-02-13 16:57:40 -0500168 ((TextView) findViewById(R.id.pkgname)).setText(appName);
Mady Mellor87d79452017-01-10 11:52:52 -0800169 ((TextView) findViewById(R.id.channel_name)).setText(channelNameText);
170
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500171 // Set group information if this channel has an associated group.
172 CharSequence groupName = null;
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500173 if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
174 final NotificationChannelGroup notificationChannelGroup =
175 iNotificationManager.getNotificationChannelGroupForPackage(
176 mSingleNotificationChannel.getGroup(), pkg, mAppUid);
177 if (notificationChannelGroup != null) {
178 groupName = notificationChannelGroup.getName();
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500179 }
Mady Mellor87d79452017-01-10 11:52:52 -0800180 }
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500181 TextView groupNameView = ((TextView) findViewById(R.id.group_name));
182 TextView groupDividerView = ((TextView) findViewById(R.id.pkg_group_divider));
183 if (groupName != null) {
184 groupNameView.setText(groupName);
185 groupNameView.setVisibility(View.VISIBLE);
186 groupDividerView.setVisibility(View.VISIBLE);
187 } else {
188 groupNameView.setVisibility(View.GONE);
189 groupDividerView.setVisibility(View.GONE);
190 }
Mady Mellor87d79452017-01-10 11:52:52 -0800191
192 boolean nonBlockable = false;
193 try {
Julia Reynolds5a311932017-03-01 16:33:44 -0500194 final PackageInfo pkgInfo = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
195 nonBlockable = Utils.isSystemPackage(getResources(), pm, pkgInfo);
Mady Mellor87d79452017-01-10 11:52:52 -0800196 } catch (PackageManager.NameNotFoundException e) {
197 // unlikely.
198 }
199 if (nonBlockablePkgs != null) {
200 nonBlockable |= nonBlockablePkgs.contains(pkg);
201 }
202
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500203 bindButtons(nonBlockable);
Mady Mellor87d79452017-01-10 11:52:52 -0800204
205 // Top-level importance group
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500206 mChannelDisabledView = findViewById(R.id.channel_disabled);
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500207 updateSecondaryText();
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500208
209 // Settings button.
210 final TextView settingsButton = (TextView) findViewById(R.id.more_settings);
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500211 if (mAppUid >= 0 && onSettingsClick != null) {
212 final int appUidF = mAppUid;
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500213 settingsButton.setOnClickListener(
214 (View view) -> {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500215 onSettingsClick.onClick(view, mSingleNotificationChannel, appUidF);
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500216 });
217 if (numChannels > 1) {
218 settingsButton.setText(R.string.notification_all_categories);
219 } else {
220 settingsButton.setText(R.string.notification_more_settings);
221 }
222
223 } else {
224 settingsButton.setVisibility(View.GONE);
225 }
226
227 // Done button.
228 final TextView doneButton = (TextView) findViewById(R.id.done);
229 doneButton.setText(R.string.notification_done);
230 doneButton.setOnClickListener(onDoneClick);
Mady Mellor87d79452017-01-10 11:52:52 -0800231 }
232
233 public boolean hasImportanceChanged() {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500234 return mSingleNotificationChannel != null &&
235 mStartingUserImportance != getSelectedImportance();
Mady Mellor87d79452017-01-10 11:52:52 -0800236 }
237
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500238 private void saveImportance() {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500239 if (mSingleNotificationChannel == null) {
240 return;
241 }
Mady Mellor87d79452017-01-10 11:52:52 -0800242 int selectedImportance = getSelectedImportance();
243 if (selectedImportance == mStartingUserImportance) {
244 return;
245 }
246 MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
247 selectedImportance - mStartingUserImportance);
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500248 mSingleNotificationChannel.setImportance(selectedImportance);
Mady Mellor87d79452017-01-10 11:52:52 -0800249 try {
250 mINotificationManager.updateNotificationChannelForPackage(
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500251 mPkg, mAppUid, mSingleNotificationChannel);
Mady Mellor87d79452017-01-10 11:52:52 -0800252 } catch (RemoteException e) {
253 // :(
254 }
255 }
256
257 private int getSelectedImportance() {
258 if (!mChannelEnabledSwitch.isChecked()) {
259 return NotificationManager.IMPORTANCE_NONE;
Mady Mellor87d79452017-01-10 11:52:52 -0800260 } else {
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500261 return mStartingUserImportance;
Mady Mellor87d79452017-01-10 11:52:52 -0800262 }
263 }
264
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500265 private void bindButtons(final boolean nonBlockable) {
Mady Mellor87d79452017-01-10 11:52:52 -0800266 // Enabled Switch
267 mChannelEnabledSwitch = (Switch) findViewById(R.id.channel_enabled_switch);
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500268 mChannelEnabledSwitch.setChecked(
269 mStartingUserImportance != NotificationManager.IMPORTANCE_NONE);
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500270 final boolean visible = !nonBlockable && mSingleNotificationChannel != null;
271 mChannelEnabledSwitch.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
Mady Mellor87d79452017-01-10 11:52:52 -0800272
Mady Mellor87d79452017-01-10 11:52:52 -0800273 // Callback when checked.
274 mChannelEnabledSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
Mady Mellor95d743c2017-01-10 12:05:27 -0800275 if (mGutsContainer != null) {
276 mGutsContainer.resetFalsingCheck();
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500277 }
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500278 updateSecondaryText();
Mady Mellor87d79452017-01-10 11:52:52 -0800279 });
Mady Mellor87d79452017-01-10 11:52:52 -0800280 }
281
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500282 private void updateSecondaryText() {
283 final boolean defaultChannel = mSingleNotificationChannel != null &&
284 mSingleNotificationChannel.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID);
285 final boolean disabled = mSingleNotificationChannel != null &&
286 getSelectedImportance() == NotificationManager.IMPORTANCE_NONE;
287 if (defaultChannel) {
288 // Don't show any secondary text if this is from the default channel.
289 mChannelDisabledView.setVisibility(View.GONE);
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500290 mNumChannelsView.setVisibility(View.GONE);
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500291 } else if (disabled) {
292 mChannelDisabledView.setVisibility(View.VISIBLE);
293 mNumChannelsView.setVisibility(View.GONE);
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500294 } else {
Geoffrey Pitschd0856f02017-02-16 10:51:18 -0500295 mChannelDisabledView.setVisibility(View.GONE);
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500296 mNumChannelsView.setVisibility(View.VISIBLE);
Mady Mellor87d79452017-01-10 11:52:52 -0800297 }
298 }
299
300 @Override
Mady Mellor95d743c2017-01-10 12:05:27 -0800301 public void setGutsParent(NotificationGuts guts) {
302 mGutsContainer = guts;
Mady Mellor87d79452017-01-10 11:52:52 -0800303 }
304
305 @Override
Mady Mellor434180c2017-02-13 11:29:42 -0800306 public boolean willBeRemoved() {
307 return !mChannelEnabledSwitch.isChecked();
308 }
309
310 @Override
Mady Mellor87d79452017-01-10 11:52:52 -0800311 public View getContentView() {
312 return this;
313 }
314
315 @Override
Geoffrey Pitschdf44b602017-02-03 13:31:50 -0500316 public boolean handleCloseControls(boolean save) {
317 if (save) {
318 saveImportance();
319 }
Mady Mellor87d79452017-01-10 11:52:52 -0800320 return false;
321 }
322}