1 /*
2  * Copyright (C) 2020 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 
17 package com.android.systemui.statusbar.notification.row;
18 
19 import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
20 import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
21 import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
22 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
23 import static android.app.NotificationManager.IMPORTANCE_LOW;
24 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
25 import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
26 
27 import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN;
28 
29 import static java.lang.annotation.RetentionPolicy.SOURCE;
30 
31 import android.annotation.IntDef;
32 import android.annotation.NonNull;
33 import android.annotation.Nullable;
34 import android.app.INotificationManager;
35 import android.app.Notification;
36 import android.app.NotificationChannel;
37 import android.app.NotificationChannelGroup;
38 import android.app.NotificationManager;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.pm.ApplicationInfo;
42 import android.content.pm.PackageManager;
43 import android.content.pm.ShortcutInfo;
44 import android.content.pm.ShortcutManager;
45 import android.os.Handler;
46 import android.os.RemoteException;
47 import android.os.UserHandle;
48 import android.provider.Settings;
49 import android.service.notification.StatusBarNotification;
50 import android.text.TextUtils;
51 import android.transition.ChangeBounds;
52 import android.transition.Fade;
53 import android.transition.TransitionManager;
54 import android.transition.TransitionSet;
55 import android.util.AttributeSet;
56 import android.util.Log;
57 import android.view.LayoutInflater;
58 import android.view.View;
59 import android.view.accessibility.AccessibilityEvent;
60 import android.widget.ImageView;
61 import android.widget.LinearLayout;
62 import android.widget.TextView;
63 
64 import com.android.internal.annotations.VisibleForTesting;
65 import com.android.settingslib.notification.ConversationIconFactory;
66 import com.android.systemui.Prefs;
67 import com.android.systemui.R;
68 import com.android.systemui.bubbles.BubbleController;
69 import com.android.systemui.dagger.qualifiers.Background;
70 import com.android.systemui.dagger.qualifiers.Main;
71 import com.android.systemui.statusbar.notification.NotificationChannelHelper;
72 import com.android.systemui.statusbar.notification.VisualStabilityManager;
73 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
74 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
75 
76 import java.lang.annotation.Retention;
77 
78 import javax.inject.Provider;
79 
80 /**
81  * The guts of a conversation notification revealed when performing a long press.
82  */
83 public class NotificationConversationInfo extends LinearLayout implements
84         NotificationGuts.GutsContent {
85     private static final String TAG = "ConversationGuts";
86 
87 
88     private INotificationManager mINotificationManager;
89     private ShortcutManager mShortcutManager;
90     private PackageManager mPm;
91     private ConversationIconFactory mIconFactory;
92     private VisualStabilityManager mVisualStabilityManager;
93     private Handler mMainHandler;
94     private Handler mBgHandler;
95     private BubbleController mBubbleController;
96     private String mPackageName;
97     private String mAppName;
98     private int mAppUid;
99     private String mDelegatePkg;
100     private NotificationChannel mNotificationChannel;
101     private ShortcutInfo mShortcutInfo;
102     private NotificationEntry mEntry;
103     private StatusBarNotification mSbn;
104     @Nullable private Notification.BubbleMetadata mBubbleMetadata;
105     private Context mUserContext;
106     private Provider<PriorityOnboardingDialogController.Builder> mBuilderProvider;
107     private boolean mIsDeviceProvisioned;
108     private int mAppBubble;
109 
110     private TextView mPriorityDescriptionView;
111     private TextView mDefaultDescriptionView;
112     private TextView mSilentDescriptionView;
113 
114     private @Action int mSelectedAction = -1;
115     private boolean mPressedApply;
116 
117     private OnSnoozeClickListener mOnSnoozeClickListener;
118     private OnSettingsClickListener mOnSettingsClickListener;
119     private NotificationGuts mGutsContainer;
120     private OnConversationSettingsClickListener mOnConversationSettingsClickListener;
121 
122     @VisibleForTesting
123     boolean mSkipPost = false;
124     private int mActualHeight;
125 
126     @Retention(SOURCE)
127     @IntDef({ACTION_DEFAULT, ACTION_HOME, ACTION_FAVORITE, ACTION_SNOOZE, ACTION_MUTE,
128             ACTION_SETTINGS})
129     private @interface Action {}
130     static final int ACTION_DEFAULT = 0;
131     static final int ACTION_HOME = 1;
132     static final int ACTION_FAVORITE = 2;
133     static final int ACTION_SNOOZE = 3;
134     static final int ACTION_MUTE = 4;
135     static final int ACTION_SETTINGS = 5;
136 
137     // TODO: b/152050825
138     /*
139     private OnClickListener mOnHomeClick = v -> {
140         mSelectedAction = ACTION_HOME;
141         mShortcutManager.requestPinShortcut(mShortcutInfo, null);
142         mShadeController.animateCollapsePanels();
143         mGutsContainer.closeControls(v, true);
144     };
145 
146     private OnClickListener mOnSnoozeClick = v -> {
147         mSelectedAction = ACTION_SNOOZE;
148         mOnSnoozeClickListener.onClick(v, 1);
149         mGutsContainer.closeControls(v, true);
150     };
151     */
152 
153     private OnClickListener mOnFavoriteClick = v -> {
154         setSelectedAction(ACTION_FAVORITE);
155         updateToggleActions(mSelectedAction, true);
156     };
157 
158     private OnClickListener mOnDefaultClick = v -> {
159         setSelectedAction(ACTION_DEFAULT);
160         updateToggleActions(mSelectedAction, true);
161     };
162 
163     private OnClickListener mOnMuteClick = v -> {
164         setSelectedAction(ACTION_MUTE);
165         updateToggleActions(mSelectedAction, true);
166     };
167 
168     private OnClickListener mOnDone = v -> {
169         mPressedApply = true;
170         // If the user selected Priority, maybe show the priority onboarding
171         if (mSelectedAction == ACTION_FAVORITE && shouldShowPriorityOnboarding()) {
172             showPriorityOnboarding();
173         }
174         mGutsContainer.closeControls(v, true);
175     };
176 
NotificationConversationInfo(Context context, AttributeSet attrs)177     public NotificationConversationInfo(Context context, AttributeSet attrs) {
178         super(context, attrs);
179     }
180 
181     public interface OnSettingsClickListener {
onClick(View v, NotificationChannel channel, int appUid)182         void onClick(View v, NotificationChannel channel, int appUid);
183     }
184 
185     public interface OnConversationSettingsClickListener {
onClick()186         void onClick();
187     }
188 
189     public interface OnAppSettingsClickListener {
onClick(View v, Intent intent)190         void onClick(View v, Intent intent);
191     }
192 
193     public interface OnSnoozeClickListener {
onClick(View v, int hoursToSnooze)194         void onClick(View v, int hoursToSnooze);
195     }
196 
197     @VisibleForTesting
setSelectedAction(int selectedAction)198     void setSelectedAction(int selectedAction) {
199         if (mSelectedAction == selectedAction) {
200             return;
201         }
202 
203         mSelectedAction = selectedAction;
204     }
205 
bindNotification( ShortcutManager shortcutManager, PackageManager pm, INotificationManager iNotificationManager, VisualStabilityManager visualStabilityManager, String pkg, NotificationChannel notificationChannel, NotificationEntry entry, Notification.BubbleMetadata bubbleMetadata, OnSettingsClickListener onSettingsClick, OnSnoozeClickListener onSnoozeClickListener, ConversationIconFactory conversationIconFactory, Context userContext, Provider<PriorityOnboardingDialogController.Builder> builderProvider, boolean isDeviceProvisioned, @Main Handler mainHandler, @Background Handler bgHandler, OnConversationSettingsClickListener onConversationSettingsClickListener, BubbleController bubbleController)206     public void bindNotification(
207             ShortcutManager shortcutManager,
208             PackageManager pm,
209             INotificationManager iNotificationManager,
210             VisualStabilityManager visualStabilityManager,
211             String pkg,
212             NotificationChannel notificationChannel,
213             NotificationEntry entry,
214             Notification.BubbleMetadata bubbleMetadata,
215             OnSettingsClickListener onSettingsClick,
216             OnSnoozeClickListener onSnoozeClickListener,
217             ConversationIconFactory conversationIconFactory,
218             Context userContext,
219             Provider<PriorityOnboardingDialogController.Builder> builderProvider,
220             boolean isDeviceProvisioned,
221             @Main Handler mainHandler,
222             @Background Handler bgHandler,
223             OnConversationSettingsClickListener onConversationSettingsClickListener,
224             BubbleController bubbleController) {
225         mSelectedAction = -1;
226         mINotificationManager = iNotificationManager;
227         mVisualStabilityManager = visualStabilityManager;
228         mPackageName = pkg;
229         mEntry = entry;
230         mSbn = entry.getSbn();
231         mPm = pm;
232         mAppName = mPackageName;
233         mOnSettingsClickListener = onSettingsClick;
234         mNotificationChannel = notificationChannel;
235         mAppUid = mSbn.getUid();
236         mDelegatePkg = mSbn.getOpPkg();
237         mIsDeviceProvisioned = isDeviceProvisioned;
238         mOnSnoozeClickListener = onSnoozeClickListener;
239         mOnConversationSettingsClickListener = onConversationSettingsClickListener;
240         mIconFactory = conversationIconFactory;
241         mUserContext = userContext;
242         mBubbleMetadata = bubbleMetadata;
243         mBubbleController = bubbleController;
244         mBuilderProvider = builderProvider;
245         mMainHandler = mainHandler;
246         mBgHandler = bgHandler;
247         mShortcutManager = shortcutManager;
248         mShortcutInfo = entry.getRanking().getShortcutInfo();
249         if (mShortcutInfo == null) {
250             throw new IllegalArgumentException("Does not have required information");
251         }
252 
253         mNotificationChannel = NotificationChannelHelper.createConversationChannelIfNeeded(
254                 getContext(), mINotificationManager, entry, mNotificationChannel);
255 
256         try {
257             mAppBubble = mINotificationManager.getBubblePreferenceForPackage(mPackageName, mAppUid);
258         } catch (RemoteException e) {
259             Log.e(TAG, "can't reach OS", e);
260             mAppBubble = BUBBLE_PREFERENCE_SELECTED;
261         }
262 
263         bindHeader();
264         bindActions();
265 
266         View done = findViewById(R.id.done);
267         done.setOnClickListener(mOnDone);
268         done.setAccessibilityDelegate(mGutsContainer.getAccessibilityDelegate());
269     }
270 
bindActions()271     private void bindActions() {
272 
273         // TODO: b/152050825
274         /*
275         Button home = findViewById(R.id.home);
276         home.setOnClickListener(mOnHomeClick);
277         home.setVisibility(mShortcutInfo != null
278                 && mShortcutManager.isRequestPinShortcutSupported()
279                 ? VISIBLE : GONE);
280 
281         Button snooze = findViewById(R.id.snooze);
282         snooze.setOnClickListener(mOnSnoozeClick);
283         */
284 
285         if (mAppBubble == BUBBLE_PREFERENCE_ALL) {
286             ((TextView) findViewById(R.id.default_summary)).setText(getResources().getString(
287                     R.string.notification_channel_summary_default_with_bubbles, mAppName));
288         }
289 
290         findViewById(R.id.priority).setOnClickListener(mOnFavoriteClick);
291         findViewById(R.id.default_behavior).setOnClickListener(mOnDefaultClick);
292         findViewById(R.id.silence).setOnClickListener(mOnMuteClick);
293 
294         final View settingsButton = findViewById(R.id.info);
295         settingsButton.setOnClickListener(getSettingsOnClickListener());
296         settingsButton.setVisibility(settingsButton.hasOnClickListeners() ? VISIBLE : GONE);
297 
298         updateToggleActions(getSelectedAction(), false);
299     }
300 
bindHeader()301     private void bindHeader() {
302         bindConversationDetails();
303 
304         // Delegate
305         bindDelegate();
306     }
307 
getSettingsOnClickListener()308     private OnClickListener getSettingsOnClickListener() {
309         if (mAppUid >= 0 && mOnSettingsClickListener != null && mIsDeviceProvisioned) {
310             final int appUidF = mAppUid;
311             return ((View view) -> {
312                 mOnSettingsClickListener.onClick(view, mNotificationChannel, appUidF);
313             });
314         }
315         return null;
316     }
317 
bindConversationDetails()318     private void bindConversationDetails() {
319         final TextView channelName = findViewById(R.id.parent_channel_name);
320         channelName.setText(mNotificationChannel.getName());
321 
322         bindGroup();
323         // TODO: bring back when channel name does not include name
324         // bindName();
325         bindPackage();
326         bindIcon(mNotificationChannel.isImportantConversation());
327     }
328 
bindIcon(boolean important)329     private void bindIcon(boolean important) {
330         ImageView image = findViewById(R.id.conversation_icon);
331         image.setImageDrawable(mIconFactory.getConversationDrawable(
332                 mShortcutInfo, mPackageName, mAppUid, important));
333     }
334 
bindPackage()335     private void bindPackage() {
336         ApplicationInfo info;
337         try {
338             info = mPm.getApplicationInfo(
339                     mPackageName,
340                     PackageManager.MATCH_UNINSTALLED_PACKAGES
341                             | PackageManager.MATCH_DISABLED_COMPONENTS
342                             | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
343                             | PackageManager.MATCH_DIRECT_BOOT_AWARE);
344             if (info != null) {
345                 mAppName = String.valueOf(mPm.getApplicationLabel(info));
346             }
347         } catch (PackageManager.NameNotFoundException e) {
348         }
349         ((TextView) findViewById(R.id.pkg_name)).setText(mAppName);
350     }
351 
bindDelegate()352     private void bindDelegate() {
353         TextView delegateView = findViewById(R.id.delegate_name);
354 
355         if (!TextUtils.equals(mPackageName, mDelegatePkg)) {
356             // this notification was posted by a delegate!
357             delegateView.setVisibility(View.VISIBLE);
358         } else {
359             delegateView.setVisibility(View.GONE);
360         }
361     }
362 
bindGroup()363     private void bindGroup() {
364         // Set group information if this channel has an associated group.
365         CharSequence groupName = null;
366         if (mNotificationChannel != null && mNotificationChannel.getGroup() != null) {
367             try {
368                 final NotificationChannelGroup notificationChannelGroup =
369                         mINotificationManager.getNotificationChannelGroupForPackage(
370                                 mNotificationChannel.getGroup(), mPackageName, mAppUid);
371                 if (notificationChannelGroup != null) {
372                     groupName = notificationChannelGroup.getName();
373                 }
374             } catch (RemoteException e) {
375             }
376         }
377         TextView groupNameView = findViewById(R.id.group_name);
378         if (groupName != null) {
379             groupNameView.setText(groupName);
380             groupNameView.setVisibility(VISIBLE);
381         } else {
382             groupNameView.setVisibility(GONE);
383         }
384     }
385 
386     @Override
post(Runnable action)387     public boolean post(Runnable action) {
388         if (mSkipPost) {
389             action.run();
390             return true;
391         } else {
392             return super.post(action);
393         }
394     }
395 
396     @Override
onFinishInflate()397     protected void onFinishInflate() {
398         super.onFinishInflate();
399 
400         mPriorityDescriptionView = findViewById(R.id.priority_summary);
401         mDefaultDescriptionView = findViewById(R.id.default_summary);
402         mSilentDescriptionView = findViewById(R.id.silence_summary);
403     }
404 
405     @Override
onFinishedClosing()406     public void onFinishedClosing() {
407         // TODO: do we need to do anything here?
408     }
409 
410     @Override
needsFalsingProtection()411     public boolean needsFalsingProtection() {
412         return true;
413     }
414 
415     @Override
onInitializeAccessibilityEvent(AccessibilityEvent event)416     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
417         super.onInitializeAccessibilityEvent(event);
418         if (mGutsContainer != null &&
419                 event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
420             if (mGutsContainer.isExposed()) {
421                 event.getText().add(mContext.getString(
422                         R.string.notification_channel_controls_opened_accessibility, mAppName));
423             } else {
424                 event.getText().add(mContext.getString(
425                         R.string.notification_channel_controls_closed_accessibility, mAppName));
426             }
427         }
428     }
429 
updateToggleActions(int selectedAction, boolean userTriggered)430     private void updateToggleActions(int selectedAction, boolean userTriggered) {
431         if (userTriggered) {
432             TransitionSet transition = new TransitionSet();
433             transition.setOrdering(TransitionSet.ORDERING_TOGETHER);
434             transition.addTransition(new Fade(Fade.OUT))
435                     .addTransition(new ChangeBounds())
436                     .addTransition(
437                             new Fade(Fade.IN)
438                                     .setStartDelay(150)
439                                     .setDuration(200)
440                                     .setInterpolator(FAST_OUT_SLOW_IN));
441             transition.setDuration(350);
442             transition.setInterpolator(FAST_OUT_SLOW_IN);
443             TransitionManager.beginDelayedTransition(this, transition);
444         }
445 
446         View priority = findViewById(R.id.priority);
447         View defaultBehavior = findViewById(R.id.default_behavior);
448         View silence = findViewById(R.id.silence);
449 
450         switch (selectedAction) {
451             case ACTION_FAVORITE:
452                 mPriorityDescriptionView.setVisibility(VISIBLE);
453                 mDefaultDescriptionView.setVisibility(GONE);
454                 mSilentDescriptionView.setVisibility(GONE);
455                 post(() -> {
456                     priority.setSelected(true);
457                     defaultBehavior.setSelected(false);
458                     silence.setSelected(false);
459                 });
460                 break;
461 
462             case ACTION_MUTE:
463                 mSilentDescriptionView.setVisibility(VISIBLE);
464                 mDefaultDescriptionView.setVisibility(GONE);
465                 mPriorityDescriptionView.setVisibility(GONE);
466                 post(() -> {
467                     priority.setSelected(false);
468                     defaultBehavior.setSelected(false);
469                     silence.setSelected(true);
470                 });
471                 break;
472 
473             case ACTION_DEFAULT:
474                 mDefaultDescriptionView.setVisibility(VISIBLE);
475                 mSilentDescriptionView.setVisibility(GONE);
476                 mPriorityDescriptionView.setVisibility(GONE);
477                 post(() -> {
478                     priority.setSelected(false);
479                     defaultBehavior.setSelected(true);
480                     silence.setSelected(false);
481                 });
482                 break;
483 
484             default:
485                 throw new IllegalArgumentException("Unrecognized behavior: " + mSelectedAction);
486         }
487 
488         boolean isAChange = getSelectedAction() != selectedAction;
489         TextView done = findViewById(R.id.done);
490         done.setText(isAChange
491                 ? R.string.inline_ok_button
492                 : R.string.inline_done_button);
493 
494         // update icon in case importance has changed
495         bindIcon(selectedAction == ACTION_FAVORITE);
496     }
497 
getSelectedAction()498     int getSelectedAction() {
499         if (mNotificationChannel.getImportance() <= IMPORTANCE_LOW
500                 && mNotificationChannel.getImportance() > IMPORTANCE_UNSPECIFIED) {
501             return ACTION_MUTE;
502         } else {
503             if (mNotificationChannel.isImportantConversation()) {
504                 return ACTION_FAVORITE;
505             }
506         }
507         return ACTION_DEFAULT;
508     }
509 
updateChannel()510     private void updateChannel() {
511         mBgHandler.post(
512                 new UpdateChannelRunnable(mINotificationManager, mPackageName,
513                         mAppUid, mSelectedAction, mNotificationChannel));
514         mEntry.markForUserTriggeredMovement(true);
515         mMainHandler.postDelayed(
516                 mVisualStabilityManager::temporarilyAllowReordering,
517                 StackStateAnimator.ANIMATION_DURATION_STANDARD);
518     }
519 
shouldShowPriorityOnboarding()520     private boolean shouldShowPriorityOnboarding() {
521         return !Prefs.getBoolean(mUserContext, Prefs.Key.HAS_SEEN_PRIORITY_ONBOARDING, false);
522     }
523 
showPriorityOnboarding()524     private void showPriorityOnboarding() {
525         View onboardingView = LayoutInflater.from(mContext)
526                 .inflate(R.layout.priority_onboarding_half_shell, null);
527 
528         boolean ignoreDnd = false;
529         try {
530             ignoreDnd = mINotificationManager
531                     .getConsolidatedNotificationPolicy().priorityConversationSenders ==
532                     NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
533         } catch (RemoteException e) {
534             Log.e(TAG, "Could not check conversation senders", e);
535         }
536 
537         boolean showAsBubble = mBubbleMetadata != null
538                 && mBubbleMetadata.getAutoExpandBubble()
539                 && Settings.Global.getInt(mContext.getContentResolver(),
540                         NOTIFICATION_BUBBLES, 0) == 1;
541 
542         PriorityOnboardingDialogController controller = mBuilderProvider.get()
543                 .setContext(mUserContext)
544                 .setView(onboardingView)
545                 .setIgnoresDnd(ignoreDnd)
546                 .setShowsAsBubble(showAsBubble)
547                 .setIcon(mIconFactory.getBaseIconDrawable(mShortcutInfo))
548                 .setBadge(mIconFactory.getAppBadge(
549                         mPackageName, UserHandle.getUserId(mSbn.getUid())))
550                 .setOnSettingsClick(mOnConversationSettingsClickListener)
551                 .build();
552 
553         controller.init();
554         controller.show();
555     }
556 
557     @Override
setGutsParent(NotificationGuts guts)558     public void setGutsParent(NotificationGuts guts) {
559         mGutsContainer = guts;
560     }
561 
562     @Override
willBeRemoved()563     public boolean willBeRemoved() {
564         return false;
565     }
566 
567     @Override
shouldBeSaved()568     public boolean shouldBeSaved() {
569         return mPressedApply;
570     }
571 
572     @Override
getContentView()573     public View getContentView() {
574         return this;
575     }
576 
577     @Override
handleCloseControls(boolean save, boolean force)578     public boolean handleCloseControls(boolean save, boolean force) {
579         if (save && mSelectedAction > -1) {
580             updateChannel();
581         }
582         return false;
583     }
584 
585     @Override
getActualHeight()586     public int getActualHeight() {
587         // Because we're animating the bounds, getHeight will return the small height at the
588         // beginning of the animation. Instead we'd want it to already return the end value
589         return mActualHeight;
590     }
591 
592     @Override
onLayout(boolean changed, int l, int t, int r, int b)593     protected void onLayout(boolean changed, int l, int t, int r, int b) {
594         super.onLayout(changed, l, t, r, b);
595         mActualHeight = getHeight();
596     }
597 
598     @VisibleForTesting
isAnimating()599     public boolean isAnimating() {
600         return false;
601     }
602 
603     class UpdateChannelRunnable implements Runnable {
604 
605         private final INotificationManager mINotificationManager;
606         private final String mAppPkg;
607         private final int mAppUid;
608         private  NotificationChannel mChannelToUpdate;
609         private final @Action int mAction;
610 
UpdateChannelRunnable(INotificationManager notificationManager, String packageName, int appUid, @Action int action, @NonNull NotificationChannel channelToUpdate)611         public UpdateChannelRunnable(INotificationManager notificationManager,
612                 String packageName, int appUid, @Action int action,
613                 @NonNull NotificationChannel channelToUpdate) {
614             mINotificationManager = notificationManager;
615             mAppPkg = packageName;
616             mAppUid = appUid;
617             mChannelToUpdate = channelToUpdate;
618             mAction = action;
619         }
620 
621         @Override
run()622         public void run() {
623             try {
624                 switch (mAction) {
625                     case ACTION_FAVORITE:
626                         mChannelToUpdate.setImportantConversation(true);
627                         if (mChannelToUpdate.isImportantConversation()) {
628                             mChannelToUpdate.setAllowBubbles(true);
629                             if (mAppBubble == BUBBLE_PREFERENCE_NONE) {
630                                 mINotificationManager.setBubblesAllowed(mAppPkg, mAppUid,
631                                         BUBBLE_PREFERENCE_SELECTED);
632                             }
633                             post(() -> {
634                                 mBubbleController.onUserChangedImportance(mEntry);
635                             });
636                         }
637                         mChannelToUpdate.setImportance(Math.max(
638                                 mChannelToUpdate.getOriginalImportance(), IMPORTANCE_DEFAULT));
639                         break;
640                     case ACTION_DEFAULT:
641                         mChannelToUpdate.setImportance(Math.max(
642                                 mChannelToUpdate.getOriginalImportance(), IMPORTANCE_DEFAULT));
643                         if (mChannelToUpdate.isImportantConversation()) {
644                             mChannelToUpdate.setImportantConversation(false);
645                             mChannelToUpdate.setAllowBubbles(false);
646                         }
647                         break;
648                     case ACTION_MUTE:
649                         if (mChannelToUpdate.getImportance() == IMPORTANCE_UNSPECIFIED
650                                 || mChannelToUpdate.getImportance() >= IMPORTANCE_DEFAULT) {
651                             mChannelToUpdate.setImportance(IMPORTANCE_LOW);
652                         }
653                         if (mChannelToUpdate.isImportantConversation()) {
654                             mChannelToUpdate.setImportantConversation(false);
655                             mChannelToUpdate.setAllowBubbles(false);
656                         }
657                         break;
658                 }
659 
660                 mINotificationManager.updateNotificationChannelForPackage(
661                             mAppPkg, mAppUid, mChannelToUpdate);
662             } catch (RemoteException e) {
663                 Log.e(TAG, "Unable to update notification channel", e);
664             }
665         }
666     }
667 }
668