• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Notification.EXTRA_BUILDER_APPLICATION_INFO;
20 import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
21 import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
22 import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
23 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
24 import static android.app.NotificationManager.IMPORTANCE_LOW;
25 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
26 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
27 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
28 
29 import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN;
30 
31 import static java.lang.annotation.RetentionPolicy.SOURCE;
32 
33 import android.annotation.IntDef;
34 import android.annotation.NonNull;
35 import android.annotation.Nullable;
36 import android.app.INotificationManager;
37 import android.app.Notification;
38 import android.app.NotificationChannel;
39 import android.app.NotificationChannelGroup;
40 import android.content.Context;
41 import android.content.Intent;
42 import android.content.pm.ApplicationInfo;
43 import android.content.pm.PackageManager;
44 import android.content.pm.ShortcutInfo;
45 import android.content.pm.ShortcutManager;
46 import android.graphics.drawable.Drawable;
47 import android.os.Bundle;
48 import android.os.Handler;
49 import android.os.RemoteException;
50 import android.os.UserHandle;
51 import android.os.UserManager;
52 import android.service.notification.NotificationListenerService;
53 import android.service.notification.StatusBarNotification;
54 import android.text.TextUtils;
55 import android.transition.ChangeBounds;
56 import android.transition.Fade;
57 import android.transition.TransitionManager;
58 import android.transition.TransitionSet;
59 import android.util.AttributeSet;
60 import android.util.Log;
61 import android.view.View;
62 import android.view.accessibility.AccessibilityEvent;
63 import android.widget.ImageView;
64 import android.widget.LinearLayout;
65 import android.widget.TextView;
66 
67 import com.android.internal.annotations.VisibleForTesting;
68 import com.android.settingslib.notification.ConversationIconFactory;
69 import com.android.systemui.dagger.qualifiers.Background;
70 import com.android.systemui.dagger.qualifiers.Main;
71 import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
72 import com.android.systemui.res.R;
73 import com.android.systemui.shade.ShadeController;
74 import com.android.systemui.statusbar.notification.NmSummarizationUiFlag;
75 import com.android.systemui.statusbar.notification.NotificationChannelHelper;
76 import com.android.systemui.statusbar.notification.collection.EntryAdapter;
77 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
78 import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
79 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
80 import com.android.systemui.wmshell.BubblesManager;
81 
82 import java.lang.annotation.Retention;
83 import java.util.Optional;
84 
85 /**
86  * The guts of a conversation notification revealed when performing a long press.
87  */
88 public class NotificationConversationInfo extends LinearLayout implements
89         NotificationGuts.GutsContent {
90     private static final String TAG = "ConversationGuts";
91 
92     private INotificationManager mINotificationManager;
93     private ShortcutManager mShortcutManager;
94     private PackageManager mPm;
95     private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager;
96     private ConversationIconFactory mIconFactory;
97     private OnUserInteractionCallback mOnUserInteractionCallback;
98     private Handler mMainHandler;
99     private Handler mBgHandler;
100     private Optional<BubblesManager> mBubblesManagerOptional;
101     private ShadeController mShadeController;
102     private String mPackageName;
103     private String mAppName;
104     private int mAppUid;
105     private String mDelegatePkg;
106     private NotificationChannel mNotificationChannel;
107     private ShortcutInfo mShortcutInfo;
108     private NotificationEntry mEntry;
109     private StatusBarNotification mSbn;
110     private EntryAdapter mEntryAdapter;
111     private NotificationListenerService.Ranking mRanking;
112     @Nullable private Notification.BubbleMetadata mBubbleMetadata;
113     private Context mUserContext;
114     private boolean mIsDeviceProvisioned;
115     private int mAppBubble;
116 
117     private TextView mPriorityDescriptionView;
118     private TextView mDefaultDescriptionView;
119     private TextView mSilentDescriptionView;
120 
121     private @Action int mSelectedAction = -1;
122     private boolean mPressedApply;
123 
124     private OnSettingsClickListener mOnSettingsClickListener;
125     private NotificationGuts mGutsContainer;
126     private OnConversationSettingsClickListener mOnConversationSettingsClickListener;
127     private NotificationInfo.OnFeedbackClickListener mFeedbackClickListener;
128 
129     private UserManager mUm;
130 
131     @VisibleForTesting
132     boolean mSkipPost = false;
133     private int mActualHeight;
134 
135     @Retention(SOURCE)
136     @IntDef({ACTION_DEFAULT, ACTION_HOME, ACTION_FAVORITE, ACTION_SNOOZE, ACTION_MUTE,
137             ACTION_SETTINGS})
138     private @interface Action {}
139     static final int ACTION_DEFAULT = 0;
140     static final int ACTION_HOME = 1;
141     static final int ACTION_FAVORITE = 2;
142     static final int ACTION_SNOOZE = 3;
143     static final int ACTION_MUTE = 4;
144     static final int ACTION_SETTINGS = 5;
145 
146     private OnClickListener mOnFavoriteClick = v -> {
147         setSelectedAction(ACTION_FAVORITE);
148         updateToggleActions(mSelectedAction, true);
149     };
150 
151     private OnClickListener mOnDefaultClick = v -> {
152         setSelectedAction(ACTION_DEFAULT);
153         updateToggleActions(mSelectedAction, true);
154     };
155 
156     private OnClickListener mOnMuteClick = v -> {
157         setSelectedAction(ACTION_MUTE);
158         updateToggleActions(mSelectedAction, true);
159     };
160 
161     private OnClickListener mOnDone = v -> {
162         mPressedApply = true;
163 
164         // If the user selected Priority and the previous selection was not priority, show a
165         // People Tile add request.
166         if (mSelectedAction == ACTION_FAVORITE && getPriority() != mSelectedAction) {
167             mShadeController.animateCollapseShade();
168             if (mUm.isSameProfileGroup(UserHandle.USER_SYSTEM, mSbn.getNormalizedUserId())) {
169                 mPeopleSpaceWidgetManager.requestPinAppWidget(mShortcutInfo, new Bundle());
170             }
171         }
172         mGutsContainer.closeControls(v, /* save= */ true);
173     };
174 
NotificationConversationInfo(Context context, AttributeSet attrs)175     public NotificationConversationInfo(Context context, AttributeSet attrs) {
176         super(context, attrs);
177     }
178 
179     public interface OnSettingsClickListener {
onClick(View v, NotificationChannel channel, int appUid)180         void onClick(View v, NotificationChannel channel, int appUid);
181     }
182 
183     public interface OnConversationSettingsClickListener {
onClick()184         void onClick();
185     }
186 
187     public interface OnAppSettingsClickListener {
onClick(View v, Intent intent)188         void onClick(View v, Intent intent);
189     }
190 
191     @VisibleForTesting
setSelectedAction(int selectedAction)192     void setSelectedAction(int selectedAction) {
193         if (mSelectedAction == selectedAction) {
194             return;
195         }
196 
197         mSelectedAction = selectedAction;
198     }
199 
bindNotification( ShortcutManager shortcutManager, PackageManager pm, UserManager um, PeopleSpaceWidgetManager peopleSpaceWidgetManager, INotificationManager iNotificationManager, OnUserInteractionCallback onUserInteractionCallback, String pkg, NotificationEntry entry, EntryAdapter entryAdapter, NotificationListenerService.Ranking ranking, StatusBarNotification sbn, OnSettingsClickListener onSettingsClick, NotificationInfo.OnFeedbackClickListener onFeedbackClickListener, ConversationIconFactory conversationIconFactory, Context userContext, boolean isDeviceProvisioned, @Main Handler mainHandler, @Background Handler bgHandler, OnConversationSettingsClickListener onConversationSettingsClickListener, Optional<BubblesManager> bubblesManagerOptional, ShadeController shadeController, boolean isDismissable, OnClickListener onCloseClick)200     public void bindNotification(
201             ShortcutManager shortcutManager,
202             PackageManager pm,
203             UserManager um,
204             PeopleSpaceWidgetManager peopleSpaceWidgetManager,
205             INotificationManager iNotificationManager,
206             OnUserInteractionCallback onUserInteractionCallback,
207             String pkg,
208             NotificationEntry entry,
209             EntryAdapter entryAdapter,
210             NotificationListenerService.Ranking ranking,
211             StatusBarNotification sbn,
212             OnSettingsClickListener onSettingsClick,
213             NotificationInfo.OnFeedbackClickListener onFeedbackClickListener,
214             ConversationIconFactory conversationIconFactory,
215             Context userContext,
216             boolean isDeviceProvisioned,
217             @Main Handler mainHandler,
218             @Background Handler bgHandler,
219             OnConversationSettingsClickListener onConversationSettingsClickListener,
220             Optional<BubblesManager> bubblesManagerOptional,
221             ShadeController shadeController, boolean isDismissable, OnClickListener onCloseClick) {
222         mINotificationManager = iNotificationManager;
223         mPeopleSpaceWidgetManager = peopleSpaceWidgetManager;
224         mOnUserInteractionCallback = onUserInteractionCallback;
225         mPackageName = pkg;
226         mEntry = entry;
227         mSbn = sbn;
228         mRanking = ranking;
229         mEntryAdapter = entryAdapter;
230         mPm = pm;
231         mUm = um;
232         mAppName = mPackageName;
233         mOnSettingsClickListener = onSettingsClick;
234         mNotificationChannel = ranking.getChannel();
235         mAppUid = mSbn.getUid();
236         mDelegatePkg = mSbn.getOpPkg();
237         mIsDeviceProvisioned = isDeviceProvisioned;
238         mOnConversationSettingsClickListener = onConversationSettingsClickListener;
239         mIconFactory = conversationIconFactory;
240         mUserContext = userContext;
241         mBubbleMetadata = sbn.getNotification().getBubbleMetadata();
242         mBubblesManagerOptional = bubblesManagerOptional;
243         mShadeController = shadeController;
244         mMainHandler = mainHandler;
245         mBgHandler = bgHandler;
246         mShortcutManager = shortcutManager;
247         mShortcutInfo = ranking.getConversationShortcutInfo();
248         mFeedbackClickListener = onFeedbackClickListener;
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 dismissButton = findViewById(R.id.inline_dismiss);
267         dismissButton.setOnClickListener(onCloseClick);
268         dismissButton.setVisibility(dismissButton.hasOnClickListeners() && isDismissable
269                 ? VISIBLE : GONE);
270 
271         View done = findViewById(R.id.done);
272         done.setOnClickListener(mOnDone);
273         done.setAccessibilityDelegate(mGutsContainer.getAccessibilityDelegate());
274     }
275 
bindActions()276     private void bindActions() {
277 
278         // TODO: b/152050825
279         /*
280         Button home = findViewById(R.id.home);
281         home.setOnClickListener(mOnHomeClick);
282         home.setVisibility(mShortcutInfo != null
283                 && mShortcutManager.isRequestPinShortcutSupported()
284                 ? VISIBLE : GONE);
285 
286         Button snooze = findViewById(R.id.snooze);
287         snooze.setOnClickListener(mOnSnoozeClick);
288         */
289 
290         TextView defaultSummaryTextView = findViewById(R.id.default_summary);
291         if (mAppBubble == BUBBLE_PREFERENCE_ALL
292                 && BubblesManager.areBubblesEnabled(mContext, mSbn.getUser())) {
293             defaultSummaryTextView.setText(getResources().getString(
294                     R.string.notification_channel_summary_default_with_bubbles, mAppName));
295         } else {
296             defaultSummaryTextView.setText(getResources().getString(
297                     R.string.notification_channel_summary_default));
298         }
299 
300         findViewById(R.id.priority).setOnClickListener(mOnFavoriteClick);
301         findViewById(R.id.default_behavior).setOnClickListener(mOnDefaultClick);
302         findViewById(R.id.silence).setOnClickListener(mOnMuteClick);
303 
304         final View settingsButton = findViewById(R.id.info);
305         settingsButton.setOnClickListener(getSettingsOnClickListener());
306         settingsButton.setVisibility(settingsButton.hasOnClickListeners() ? VISIBLE : GONE);
307 
308         bindFeedback();
309 
310         updateToggleActions(mSelectedAction == -1 ? getPriority() : mSelectedAction,
311                 false);
312     }
313 
bindHeader()314     private void bindHeader() {
315         bindConversationDetails();
316 
317         // Delegate
318         bindDelegate();
319     }
320 
bindFeedback()321     private void bindFeedback() {
322         View feedbackButton = findViewById(R.id.feedback);
323         if (!NmSummarizationUiFlag.isEnabled()
324                 || TextUtils.isEmpty(mRanking.getSummarization())) {
325             feedbackButton.setVisibility(GONE);
326         } else {
327             Intent intent = NotificationInfo.getAssistantFeedbackIntent(
328                     mINotificationManager, mPm, mSbn.getKey(), mRanking);
329             if (intent == null) {
330                 feedbackButton.setVisibility(GONE);
331             } else {
332                 feedbackButton.setVisibility(VISIBLE);
333                 feedbackButton.setOnClickListener((View v) -> {
334                     mFeedbackClickListener.onClick(v, intent);
335                 });
336             }
337         }
338     }
339 
getSettingsOnClickListener()340     private OnClickListener getSettingsOnClickListener() {
341         if (mAppUid >= 0 && mOnSettingsClickListener != null && mIsDeviceProvisioned) {
342             final int appUidF = mAppUid;
343             return ((View view) -> {
344                 mOnSettingsClickListener.onClick(view, mNotificationChannel, appUidF);
345             });
346         }
347         return null;
348     }
349 
bindConversationDetails()350     private void bindConversationDetails() {
351         final TextView channelName = findViewById(R.id.parent_channel_name);
352         channelName.setText(mNotificationChannel.getName());
353 
354         bindGroup();
355         // TODO: bring back when channel name does not include name
356         // bindName();
357         bindPackage();
358         bindIcon(mNotificationChannel.isImportantConversation());
359 
360         mPriorityDescriptionView = findViewById(R.id.priority_summary);
361         if (willShowAsBubble() && willBypassDnd()) {
362             mPriorityDescriptionView.setText(R.string.notification_channel_summary_priority_all);
363         } else if (willShowAsBubble()) {
364             mPriorityDescriptionView.setText(R.string.notification_channel_summary_priority_bubble);
365         } else if (willBypassDnd()) {
366             mPriorityDescriptionView.setText(R.string.notification_channel_summary_priority_dnd);
367         } else {
368             mPriorityDescriptionView.setText(
369                     R.string.notification_channel_summary_priority_baseline);
370         }
371     }
372 
bindIcon(boolean important)373     private void bindIcon(boolean important) {
374         Drawable person =  mIconFactory.getBaseIconDrawable(mShortcutInfo);
375         if (person == null) {
376             person = mContext.getDrawable(R.drawable.ic_person).mutate();
377             int colorPrimary = mContext.getColor(com.android.internal.R.color.materialColorPrimary);
378             person.setTint(colorPrimary);
379         }
380         ImageView image = findViewById(R.id.conversation_icon);
381         image.setImageDrawable(person);
382 
383         ImageView app = findViewById(R.id.conversation_icon_badge_icon);
384         app.setImageDrawable(mIconFactory.getAppBadge(
385                         mPackageName, UserHandle.getUserId(mSbn.getUid())));
386 
387         findViewById(R.id.conversation_icon_badge_ring).setVisibility(important ? VISIBLE : GONE);
388     }
389 
bindPackage()390     private void bindPackage() {
391         // filled in if missing during notification inflation, which must have happened if
392         // we have a notification to long press on
393         ApplicationInfo info =
394                 mSbn.getNotification().extras.getParcelable(EXTRA_BUILDER_APPLICATION_INFO,
395                         ApplicationInfo.class);
396         if (info != null) {
397             try {
398                 mAppName = String.valueOf(mPm.getApplicationLabel(info));
399             } catch (Exception ignored) {}
400         }
401         ((TextView) findViewById(R.id.pkg_name)).setText(mAppName);
402     }
403 
bindDelegate()404     private void bindDelegate() {
405         TextView delegateView = findViewById(R.id.delegate_name);
406 
407         if (!TextUtils.equals(mPackageName, mDelegatePkg)) {
408             // this notification was posted by a delegate!
409             delegateView.setVisibility(View.VISIBLE);
410         } else {
411             delegateView.setVisibility(View.GONE);
412         }
413     }
414 
bindGroup()415     private void bindGroup() {
416         // Set group information if this channel has an associated group.
417         CharSequence groupName = null;
418         if (mNotificationChannel != null && mNotificationChannel.getGroup() != null) {
419             try {
420                 final NotificationChannelGroup notificationChannelGroup =
421                         mINotificationManager.getNotificationChannelGroupForPackage(
422                                 mNotificationChannel.getGroup(), mPackageName, mAppUid);
423                 if (notificationChannelGroup != null) {
424                     groupName = notificationChannelGroup.getName();
425                 }
426             } catch (RemoteException e) {
427             }
428         }
429         TextView groupNameView = findViewById(R.id.group_name);
430         if (groupName != null) {
431             groupNameView.setText(groupName);
432             groupNameView.setVisibility(VISIBLE);
433         } else {
434             groupNameView.setVisibility(GONE);
435         }
436     }
437 
438     @Override
post(Runnable action)439     public boolean post(Runnable action) {
440         if (mSkipPost) {
441             action.run();
442             return true;
443         } else {
444             return super.post(action);
445         }
446     }
447 
448     @Override
onFinishInflate()449     protected void onFinishInflate() {
450         super.onFinishInflate();
451 
452         mDefaultDescriptionView = findViewById(R.id.default_summary);
453         mSilentDescriptionView = findViewById(R.id.silence_summary);
454     }
455 
456     @Override
onFinishedClosing()457     public void onFinishedClosing() { }
458 
459     @Override
needsFalsingProtection()460     public boolean needsFalsingProtection() {
461         return true;
462     }
463 
464     @Override
onInitializeAccessibilityEvent(AccessibilityEvent event)465     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
466         super.onInitializeAccessibilityEvent(event);
467         if (mGutsContainer != null &&
468                 event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
469             if (mGutsContainer.isExposed()) {
470                 event.getText().add(mContext.getString(
471                         R.string.notification_channel_controls_opened_accessibility, mAppName));
472             } else {
473                 event.getText().add(mContext.getString(
474                         R.string.notification_channel_controls_closed_accessibility, mAppName));
475             }
476         }
477     }
478 
updateToggleActions(int selectedAction, boolean userTriggered)479     private void updateToggleActions(int selectedAction, boolean userTriggered) {
480         if (userTriggered) {
481             TransitionSet transition = new TransitionSet();
482             transition.setOrdering(TransitionSet.ORDERING_TOGETHER);
483             transition.addTransition(new Fade(Fade.OUT))
484                     .addTransition(new ChangeBounds())
485                     .addTransition(
486                             new Fade(Fade.IN)
487                                     .setStartDelay(150)
488                                     .setDuration(200)
489                                     .setInterpolator(FAST_OUT_SLOW_IN));
490             transition.setDuration(350);
491             transition.setInterpolator(FAST_OUT_SLOW_IN);
492             TransitionManager.beginDelayedTransition(this, transition);
493         }
494 
495         View priority = findViewById(R.id.priority);
496         View defaultBehavior = findViewById(R.id.default_behavior);
497         View silence = findViewById(R.id.silence);
498 
499         switch (selectedAction) {
500             case ACTION_FAVORITE:
501                 mPriorityDescriptionView.setVisibility(VISIBLE);
502                 mDefaultDescriptionView.setVisibility(GONE);
503                 mSilentDescriptionView.setVisibility(GONE);
504                 post(() -> {
505                     priority.setSelected(true);
506                     defaultBehavior.setSelected(false);
507                     silence.setSelected(false);
508                 });
509                 break;
510 
511             case ACTION_MUTE:
512                 mSilentDescriptionView.setVisibility(VISIBLE);
513                 mDefaultDescriptionView.setVisibility(GONE);
514                 mPriorityDescriptionView.setVisibility(GONE);
515                 post(() -> {
516                     priority.setSelected(false);
517                     defaultBehavior.setSelected(false);
518                     silence.setSelected(true);
519                 });
520                 break;
521 
522             case ACTION_DEFAULT:
523                 mDefaultDescriptionView.setVisibility(VISIBLE);
524                 mSilentDescriptionView.setVisibility(GONE);
525                 mPriorityDescriptionView.setVisibility(GONE);
526                 post(() -> {
527                     priority.setSelected(false);
528                     defaultBehavior.setSelected(true);
529                     silence.setSelected(false);
530                 });
531                 break;
532 
533             default:
534                 throw new IllegalArgumentException("Unrecognized behavior: " + mSelectedAction);
535         }
536 
537         boolean isAChange = getPriority() != selectedAction;
538         TextView done = findViewById(R.id.done);
539         done.setText(isAChange
540                 ? R.string.inline_ok_button
541                 : R.string.inline_done_button);
542 
543         // update icon in case importance has changed
544         bindIcon(selectedAction == ACTION_FAVORITE);
545     }
546 
getSelectedAction()547     int getSelectedAction() {
548         return mSelectedAction;
549     }
550 
getPriority()551     private int getPriority() {
552         if (mNotificationChannel.getImportance() <= IMPORTANCE_LOW
553                 && mNotificationChannel.getImportance() > IMPORTANCE_UNSPECIFIED) {
554             return ACTION_MUTE;
555         } else {
556             if (mNotificationChannel.isImportantConversation()) {
557                 return ACTION_FAVORITE;
558             }
559         }
560         return ACTION_DEFAULT;
561     }
562 
updateChannel()563     private void updateChannel() {
564         mBgHandler.post(
565                 new UpdateChannelRunnable(mINotificationManager, mPackageName,
566                         mAppUid, mSelectedAction, mNotificationChannel));
567         if (NotificationBundleUi.isEnabled()) {
568             mEntryAdapter.markForUserTriggeredMovement();
569             mMainHandler.postDelayed(
570                     () -> mEntryAdapter.onImportanceChanged(),
571                     StackStateAnimator.ANIMATION_DURATION_STANDARD);
572         } else {
573             mEntry.markForUserTriggeredMovement(true);
574             mMainHandler.postDelayed(
575                     () -> mOnUserInteractionCallback.onImportanceChanged(mEntry),
576                     StackStateAnimator.ANIMATION_DURATION_STANDARD);
577         }
578     }
579 
willBypassDnd()580     private boolean willBypassDnd() {
581         boolean bypassesDnd = false;
582         try {
583             int allowedSenders = mINotificationManager
584                     .getConsolidatedNotificationPolicy().priorityConversationSenders;
585             bypassesDnd =  allowedSenders == CONVERSATION_SENDERS_IMPORTANT
586                     || allowedSenders == CONVERSATION_SENDERS_ANYONE;
587         } catch (RemoteException e) {
588             Log.e(TAG, "Could not check conversation senders", e);
589         }
590         return bypassesDnd;
591     }
592 
willShowAsBubble()593     private boolean willShowAsBubble() {
594         return mBubbleMetadata != null
595                 && BubblesManager.areBubblesEnabled(mContext, mSbn.getUser());
596     }
597 
598     @Override
setGutsParent(NotificationGuts guts)599     public void setGutsParent(NotificationGuts guts) {
600         mGutsContainer = guts;
601     }
602 
603     @Override
willBeRemoved()604     public boolean willBeRemoved() {
605         return false;
606     }
607 
608     @Override
shouldBeSavedOnClose()609     public boolean shouldBeSavedOnClose() {
610         return mPressedApply;
611     }
612 
613     @Override
getContentView()614     public View getContentView() {
615         return this;
616     }
617 
618     @Override
handleCloseControls(boolean save, boolean force)619     public boolean handleCloseControls(boolean save, boolean force) {
620         if (save && mSelectedAction > -1) {
621             updateChannel();
622         }
623 
624         // Clear the selected importance when closing, so when when we open again,
625         // we starts from a clean state.
626         mSelectedAction = -1;
627         mPressedApply = false;
628 
629         return false;
630     }
631 
632     @Override
getActualHeight()633     public int getActualHeight() {
634         // Because we're animating the bounds, getHeight will return the small height at the
635         // beginning of the animation. Instead we'd want it to already return the end value
636         return mActualHeight;
637     }
638 
639     @Override
onLayout(boolean changed, int l, int t, int r, int b)640     protected void onLayout(boolean changed, int l, int t, int r, int b) {
641         super.onLayout(changed, l, t, r, b);
642         mActualHeight = getHeight();
643     }
644 
645     @VisibleForTesting
isAnimating()646     public boolean isAnimating() {
647         return false;
648     }
649 
650     class UpdateChannelRunnable implements Runnable {
651 
652         private final INotificationManager mINotificationManager;
653         private final String mAppPkg;
654         private final int mAppUid;
655         private  NotificationChannel mChannelToUpdate;
656         private final @Action int mAction;
657 
UpdateChannelRunnable(INotificationManager notificationManager, String packageName, int appUid, @Action int action, @NonNull NotificationChannel channelToUpdate)658         public UpdateChannelRunnable(INotificationManager notificationManager,
659                 String packageName, int appUid, @Action int action,
660                 @NonNull NotificationChannel channelToUpdate) {
661             mINotificationManager = notificationManager;
662             mAppPkg = packageName;
663             mAppUid = appUid;
664             mChannelToUpdate = channelToUpdate;
665             mAction = action;
666         }
667 
668         @Override
run()669         public void run() {
670             try {
671                 switch (mAction) {
672                     case ACTION_FAVORITE:
673                         mChannelToUpdate.setImportantConversation(true);
674                         if (mChannelToUpdate.isImportantConversation()) {
675                             mChannelToUpdate.setAllowBubbles(true);
676                             if (mAppBubble == BUBBLE_PREFERENCE_NONE) {
677                                 mINotificationManager.setBubblesAllowed(mAppPkg, mAppUid,
678                                         BUBBLE_PREFERENCE_SELECTED);
679                             }
680                             if (mBubblesManagerOptional.isPresent()) {
681                                 if (NotificationBundleUi.isEnabled()) {
682                                     post(() -> mBubblesManagerOptional.get()
683                                             .onUserSetImportantConversation(mEntryAdapter));
684                                 } else {
685                                     post(() -> mBubblesManagerOptional.get()
686                                             .onUserSetImportantConversation(mEntry));
687                                 }
688                             }
689                         }
690                         mChannelToUpdate.setImportance(Math.max(
691                                 mChannelToUpdate.getOriginalImportance(), IMPORTANCE_DEFAULT));
692                         break;
693                     case ACTION_DEFAULT:
694                         mChannelToUpdate.setImportance(Math.max(
695                                 mChannelToUpdate.getOriginalImportance(), IMPORTANCE_DEFAULT));
696                         if (mChannelToUpdate.isImportantConversation()) {
697                             mChannelToUpdate.setImportantConversation(false);
698                             mChannelToUpdate.setAllowBubbles(false);
699                         }
700                         break;
701                     case ACTION_MUTE:
702                         if (mChannelToUpdate.getImportance() == IMPORTANCE_UNSPECIFIED
703                                 || mChannelToUpdate.getImportance() >= IMPORTANCE_DEFAULT) {
704                             mChannelToUpdate.setImportance(IMPORTANCE_LOW);
705                         }
706                         if (mChannelToUpdate.isImportantConversation()) {
707                             mChannelToUpdate.setImportantConversation(false);
708                             mChannelToUpdate.setAllowBubbles(false);
709                         }
710                         break;
711                 }
712 
713                 mINotificationManager.updateNotificationChannelForPackage(
714                             mAppPkg, mAppUid, mChannelToUpdate);
715             } catch (RemoteException e) {
716                 Log.e(TAG, "Unable to update notification channel", e);
717             }
718         }
719     }
720 }
721