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.app.NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE; 26 import static android.app.NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT; 27 28 import static com.android.systemui.animation.Interpolators.FAST_OUT_SLOW_IN; 29 30 import static java.lang.annotation.RetentionPolicy.SOURCE; 31 32 import android.annotation.IntDef; 33 import android.annotation.NonNull; 34 import android.annotation.Nullable; 35 import android.app.INotificationManager; 36 import android.app.Notification; 37 import android.app.NotificationChannel; 38 import android.app.NotificationChannelGroup; 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.content.res.TypedArray; 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.service.notification.StatusBarNotification; 52 import android.text.TextUtils; 53 import android.transition.ChangeBounds; 54 import android.transition.Fade; 55 import android.transition.TransitionManager; 56 import android.transition.TransitionSet; 57 import android.util.AttributeSet; 58 import android.util.Log; 59 import android.view.View; 60 import android.view.accessibility.AccessibilityEvent; 61 import android.widget.ImageView; 62 import android.widget.LinearLayout; 63 import android.widget.TextView; 64 65 import com.android.internal.annotations.VisibleForTesting; 66 import com.android.settingslib.notification.ConversationIconFactory; 67 import com.android.systemui.R; 68 import com.android.systemui.dagger.qualifiers.Background; 69 import com.android.systemui.dagger.qualifiers.Main; 70 import com.android.systemui.people.widget.PeopleSpaceWidgetManager; 71 import com.android.systemui.shade.ShadeController; 72 import com.android.systemui.statusbar.notification.NotificationChannelHelper; 73 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 74 import com.android.systemui.statusbar.notification.stack.StackStateAnimator; 75 import com.android.systemui.wmshell.BubblesManager; 76 77 import java.lang.annotation.Retention; 78 import java.util.Optional; 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 private INotificationManager mINotificationManager; 88 private ShortcutManager mShortcutManager; 89 private PackageManager mPm; 90 private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager; 91 private ConversationIconFactory mIconFactory; 92 private OnUserInteractionCallback mOnUserInteractionCallback; 93 private Handler mMainHandler; 94 private Handler mBgHandler; 95 private Optional<BubblesManager> mBubblesManagerOptional; 96 private ShadeController mShadeController; 97 private String mPackageName; 98 private String mAppName; 99 private int mAppUid; 100 private String mDelegatePkg; 101 private NotificationChannel mNotificationChannel; 102 private ShortcutInfo mShortcutInfo; 103 private NotificationEntry mEntry; 104 private StatusBarNotification mSbn; 105 @Nullable private Notification.BubbleMetadata mBubbleMetadata; 106 private Context mUserContext; 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 OnSettingsClickListener mOnSettingsClickListener; 118 private NotificationGuts mGutsContainer; 119 private OnConversationSettingsClickListener mOnConversationSettingsClickListener; 120 121 @VisibleForTesting 122 boolean mSkipPost = false; 123 private int mActualHeight; 124 125 @Retention(SOURCE) 126 @IntDef({ACTION_DEFAULT, ACTION_HOME, ACTION_FAVORITE, ACTION_SNOOZE, ACTION_MUTE, 127 ACTION_SETTINGS}) 128 private @interface Action {} 129 static final int ACTION_DEFAULT = 0; 130 static final int ACTION_HOME = 1; 131 static final int ACTION_FAVORITE = 2; 132 static final int ACTION_SNOOZE = 3; 133 static final int ACTION_MUTE = 4; 134 static final int ACTION_SETTINGS = 5; 135 136 private OnClickListener mOnFavoriteClick = v -> { 137 setSelectedAction(ACTION_FAVORITE); 138 updateToggleActions(mSelectedAction, true); 139 }; 140 141 private OnClickListener mOnDefaultClick = v -> { 142 setSelectedAction(ACTION_DEFAULT); 143 updateToggleActions(mSelectedAction, true); 144 }; 145 146 private OnClickListener mOnMuteClick = v -> { 147 setSelectedAction(ACTION_MUTE); 148 updateToggleActions(mSelectedAction, true); 149 }; 150 151 private OnClickListener mOnDone = v -> { 152 mPressedApply = true; 153 154 // If the user selected Priority and the previous selection was not priority, show a 155 // People Tile add request. 156 if (mSelectedAction == ACTION_FAVORITE && getPriority() != mSelectedAction) { 157 mShadeController.animateCollapseShade(); 158 mPeopleSpaceWidgetManager.requestPinAppWidget(mShortcutInfo, new Bundle()); 159 } 160 mGutsContainer.closeControls(v, /* save= */ true); 161 }; 162 NotificationConversationInfo(Context context, AttributeSet attrs)163 public NotificationConversationInfo(Context context, AttributeSet attrs) { 164 super(context, attrs); 165 } 166 167 public interface OnSettingsClickListener { onClick(View v, NotificationChannel channel, int appUid)168 void onClick(View v, NotificationChannel channel, int appUid); 169 } 170 171 public interface OnConversationSettingsClickListener { onClick()172 void onClick(); 173 } 174 175 public interface OnAppSettingsClickListener { onClick(View v, Intent intent)176 void onClick(View v, Intent intent); 177 } 178 179 @VisibleForTesting setSelectedAction(int selectedAction)180 void setSelectedAction(int selectedAction) { 181 if (mSelectedAction == selectedAction) { 182 return; 183 } 184 185 mSelectedAction = selectedAction; 186 } 187 bindNotification( ShortcutManager shortcutManager, PackageManager pm, PeopleSpaceWidgetManager peopleSpaceWidgetManager, INotificationManager iNotificationManager, OnUserInteractionCallback onUserInteractionCallback, String pkg, NotificationChannel notificationChannel, NotificationEntry entry, Notification.BubbleMetadata bubbleMetadata, OnSettingsClickListener onSettingsClick, ConversationIconFactory conversationIconFactory, Context userContext, boolean isDeviceProvisioned, @Main Handler mainHandler, @Background Handler bgHandler, OnConversationSettingsClickListener onConversationSettingsClickListener, Optional<BubblesManager> bubblesManagerOptional, ShadeController shadeController)188 public void bindNotification( 189 ShortcutManager shortcutManager, 190 PackageManager pm, 191 PeopleSpaceWidgetManager peopleSpaceWidgetManager, 192 INotificationManager iNotificationManager, 193 OnUserInteractionCallback onUserInteractionCallback, 194 String pkg, 195 NotificationChannel notificationChannel, 196 NotificationEntry entry, 197 Notification.BubbleMetadata bubbleMetadata, 198 OnSettingsClickListener onSettingsClick, 199 ConversationIconFactory conversationIconFactory, 200 Context userContext, 201 boolean isDeviceProvisioned, 202 @Main Handler mainHandler, 203 @Background Handler bgHandler, 204 OnConversationSettingsClickListener onConversationSettingsClickListener, 205 Optional<BubblesManager> bubblesManagerOptional, 206 ShadeController shadeController) { 207 mINotificationManager = iNotificationManager; 208 mPeopleSpaceWidgetManager = peopleSpaceWidgetManager; 209 mOnUserInteractionCallback = onUserInteractionCallback; 210 mPackageName = pkg; 211 mEntry = entry; 212 mSbn = entry.getSbn(); 213 mPm = pm; 214 mAppName = mPackageName; 215 mOnSettingsClickListener = onSettingsClick; 216 mNotificationChannel = notificationChannel; 217 mAppUid = mSbn.getUid(); 218 mDelegatePkg = mSbn.getOpPkg(); 219 mIsDeviceProvisioned = isDeviceProvisioned; 220 mOnConversationSettingsClickListener = onConversationSettingsClickListener; 221 mIconFactory = conversationIconFactory; 222 mUserContext = userContext; 223 mBubbleMetadata = bubbleMetadata; 224 mBubblesManagerOptional = bubblesManagerOptional; 225 mShadeController = shadeController; 226 mMainHandler = mainHandler; 227 mBgHandler = bgHandler; 228 mShortcutManager = shortcutManager; 229 mShortcutInfo = entry.getRanking().getConversationShortcutInfo(); 230 if (mShortcutInfo == null) { 231 throw new IllegalArgumentException("Does not have required information"); 232 } 233 234 mNotificationChannel = NotificationChannelHelper.createConversationChannelIfNeeded( 235 getContext(), mINotificationManager, entry, mNotificationChannel); 236 237 try { 238 mAppBubble = mINotificationManager.getBubblePreferenceForPackage(mPackageName, mAppUid); 239 } catch (RemoteException e) { 240 Log.e(TAG, "can't reach OS", e); 241 mAppBubble = BUBBLE_PREFERENCE_SELECTED; 242 } 243 244 bindHeader(); 245 bindActions(); 246 247 View done = findViewById(R.id.done); 248 done.setOnClickListener(mOnDone); 249 done.setAccessibilityDelegate(mGutsContainer.getAccessibilityDelegate()); 250 } 251 bindActions()252 private void bindActions() { 253 254 // TODO: b/152050825 255 /* 256 Button home = findViewById(R.id.home); 257 home.setOnClickListener(mOnHomeClick); 258 home.setVisibility(mShortcutInfo != null 259 && mShortcutManager.isRequestPinShortcutSupported() 260 ? VISIBLE : GONE); 261 262 Button snooze = findViewById(R.id.snooze); 263 snooze.setOnClickListener(mOnSnoozeClick); 264 */ 265 266 TextView defaultSummaryTextView = findViewById(R.id.default_summary); 267 if (mAppBubble == BUBBLE_PREFERENCE_ALL 268 && BubblesManager.areBubblesEnabled(mContext, mSbn.getUser())) { 269 defaultSummaryTextView.setText(getResources().getString( 270 R.string.notification_channel_summary_default_with_bubbles, mAppName)); 271 } else { 272 defaultSummaryTextView.setText(getResources().getString( 273 R.string.notification_channel_summary_default)); 274 } 275 276 findViewById(R.id.priority).setOnClickListener(mOnFavoriteClick); 277 findViewById(R.id.default_behavior).setOnClickListener(mOnDefaultClick); 278 findViewById(R.id.silence).setOnClickListener(mOnMuteClick); 279 280 final View settingsButton = findViewById(R.id.info); 281 settingsButton.setOnClickListener(getSettingsOnClickListener()); 282 settingsButton.setVisibility(settingsButton.hasOnClickListeners() ? VISIBLE : GONE); 283 284 updateToggleActions(mSelectedAction == -1 ? getPriority() : mSelectedAction, 285 false); 286 } 287 bindHeader()288 private void bindHeader() { 289 bindConversationDetails(); 290 291 // Delegate 292 bindDelegate(); 293 } 294 getSettingsOnClickListener()295 private OnClickListener getSettingsOnClickListener() { 296 if (mAppUid >= 0 && mOnSettingsClickListener != null && mIsDeviceProvisioned) { 297 final int appUidF = mAppUid; 298 return ((View view) -> { 299 mOnSettingsClickListener.onClick(view, mNotificationChannel, appUidF); 300 }); 301 } 302 return null; 303 } 304 bindConversationDetails()305 private void bindConversationDetails() { 306 final TextView channelName = findViewById(R.id.parent_channel_name); 307 channelName.setText(mNotificationChannel.getName()); 308 309 bindGroup(); 310 // TODO: bring back when channel name does not include name 311 // bindName(); 312 bindPackage(); 313 bindIcon(mNotificationChannel.isImportantConversation()); 314 315 mPriorityDescriptionView = findViewById(R.id.priority_summary); 316 if (willShowAsBubble() && willBypassDnd()) { 317 mPriorityDescriptionView.setText(R.string.notification_channel_summary_priority_all); 318 } else if (willShowAsBubble()) { 319 mPriorityDescriptionView.setText(R.string.notification_channel_summary_priority_bubble); 320 } else if (willBypassDnd()) { 321 mPriorityDescriptionView.setText(R.string.notification_channel_summary_priority_dnd); 322 } else { 323 mPriorityDescriptionView.setText( 324 R.string.notification_channel_summary_priority_baseline); 325 } 326 } 327 bindIcon(boolean important)328 private void bindIcon(boolean important) { 329 Drawable person = mIconFactory.getBaseIconDrawable(mShortcutInfo); 330 if (person == null) { 331 person = mContext.getDrawable(R.drawable.ic_person).mutate(); 332 TypedArray ta = mContext.obtainStyledAttributes(new int[]{android.R.attr.colorAccent}); 333 int colorAccent = ta.getColor(0, 0); 334 ta.recycle(); 335 person.setTint(colorAccent); 336 } 337 ImageView image = findViewById(R.id.conversation_icon); 338 image.setImageDrawable(person); 339 340 ImageView app = findViewById(R.id.conversation_icon_badge_icon); 341 app.setImageDrawable(mIconFactory.getAppBadge( 342 mPackageName, UserHandle.getUserId(mSbn.getUid()))); 343 344 findViewById(R.id.conversation_icon_badge_ring).setVisibility(important ? VISIBLE : GONE); 345 } 346 bindPackage()347 private void bindPackage() { 348 ApplicationInfo info; 349 try { 350 info = mPm.getApplicationInfo( 351 mPackageName, 352 PackageManager.MATCH_UNINSTALLED_PACKAGES 353 | PackageManager.MATCH_DISABLED_COMPONENTS 354 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE 355 | PackageManager.MATCH_DIRECT_BOOT_AWARE); 356 if (info != null) { 357 mAppName = String.valueOf(mPm.getApplicationLabel(info)); 358 } 359 } catch (PackageManager.NameNotFoundException e) { 360 } 361 ((TextView) findViewById(R.id.pkg_name)).setText(mAppName); 362 } 363 bindDelegate()364 private void bindDelegate() { 365 TextView delegateView = findViewById(R.id.delegate_name); 366 367 if (!TextUtils.equals(mPackageName, mDelegatePkg)) { 368 // this notification was posted by a delegate! 369 delegateView.setVisibility(View.VISIBLE); 370 } else { 371 delegateView.setVisibility(View.GONE); 372 } 373 } 374 bindGroup()375 private void bindGroup() { 376 // Set group information if this channel has an associated group. 377 CharSequence groupName = null; 378 if (mNotificationChannel != null && mNotificationChannel.getGroup() != null) { 379 try { 380 final NotificationChannelGroup notificationChannelGroup = 381 mINotificationManager.getNotificationChannelGroupForPackage( 382 mNotificationChannel.getGroup(), mPackageName, mAppUid); 383 if (notificationChannelGroup != null) { 384 groupName = notificationChannelGroup.getName(); 385 } 386 } catch (RemoteException e) { 387 } 388 } 389 TextView groupNameView = findViewById(R.id.group_name); 390 if (groupName != null) { 391 groupNameView.setText(groupName); 392 groupNameView.setVisibility(VISIBLE); 393 } else { 394 groupNameView.setVisibility(GONE); 395 } 396 } 397 398 @Override post(Runnable action)399 public boolean post(Runnable action) { 400 if (mSkipPost) { 401 action.run(); 402 return true; 403 } else { 404 return super.post(action); 405 } 406 } 407 408 @Override onFinishInflate()409 protected void onFinishInflate() { 410 super.onFinishInflate(); 411 412 mDefaultDescriptionView = findViewById(R.id.default_summary); 413 mSilentDescriptionView = findViewById(R.id.silence_summary); 414 } 415 416 @Override onFinishedClosing()417 public void onFinishedClosing() { } 418 419 @Override needsFalsingProtection()420 public boolean needsFalsingProtection() { 421 return true; 422 } 423 424 @Override onInitializeAccessibilityEvent(AccessibilityEvent event)425 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 426 super.onInitializeAccessibilityEvent(event); 427 if (mGutsContainer != null && 428 event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { 429 if (mGutsContainer.isExposed()) { 430 event.getText().add(mContext.getString( 431 R.string.notification_channel_controls_opened_accessibility, mAppName)); 432 } else { 433 event.getText().add(mContext.getString( 434 R.string.notification_channel_controls_closed_accessibility, mAppName)); 435 } 436 } 437 } 438 updateToggleActions(int selectedAction, boolean userTriggered)439 private void updateToggleActions(int selectedAction, boolean userTriggered) { 440 if (userTriggered) { 441 TransitionSet transition = new TransitionSet(); 442 transition.setOrdering(TransitionSet.ORDERING_TOGETHER); 443 transition.addTransition(new Fade(Fade.OUT)) 444 .addTransition(new ChangeBounds()) 445 .addTransition( 446 new Fade(Fade.IN) 447 .setStartDelay(150) 448 .setDuration(200) 449 .setInterpolator(FAST_OUT_SLOW_IN)); 450 transition.setDuration(350); 451 transition.setInterpolator(FAST_OUT_SLOW_IN); 452 TransitionManager.beginDelayedTransition(this, transition); 453 } 454 455 View priority = findViewById(R.id.priority); 456 View defaultBehavior = findViewById(R.id.default_behavior); 457 View silence = findViewById(R.id.silence); 458 459 switch (selectedAction) { 460 case ACTION_FAVORITE: 461 mPriorityDescriptionView.setVisibility(VISIBLE); 462 mDefaultDescriptionView.setVisibility(GONE); 463 mSilentDescriptionView.setVisibility(GONE); 464 post(() -> { 465 priority.setSelected(true); 466 defaultBehavior.setSelected(false); 467 silence.setSelected(false); 468 }); 469 break; 470 471 case ACTION_MUTE: 472 mSilentDescriptionView.setVisibility(VISIBLE); 473 mDefaultDescriptionView.setVisibility(GONE); 474 mPriorityDescriptionView.setVisibility(GONE); 475 post(() -> { 476 priority.setSelected(false); 477 defaultBehavior.setSelected(false); 478 silence.setSelected(true); 479 }); 480 break; 481 482 case ACTION_DEFAULT: 483 mDefaultDescriptionView.setVisibility(VISIBLE); 484 mSilentDescriptionView.setVisibility(GONE); 485 mPriorityDescriptionView.setVisibility(GONE); 486 post(() -> { 487 priority.setSelected(false); 488 defaultBehavior.setSelected(true); 489 silence.setSelected(false); 490 }); 491 break; 492 493 default: 494 throw new IllegalArgumentException("Unrecognized behavior: " + mSelectedAction); 495 } 496 497 boolean isAChange = getPriority() != selectedAction; 498 TextView done = findViewById(R.id.done); 499 done.setText(isAChange 500 ? R.string.inline_ok_button 501 : R.string.inline_done_button); 502 503 // update icon in case importance has changed 504 bindIcon(selectedAction == ACTION_FAVORITE); 505 } 506 getSelectedAction()507 int getSelectedAction() { 508 return mSelectedAction; 509 } 510 getPriority()511 private int getPriority() { 512 if (mNotificationChannel.getImportance() <= IMPORTANCE_LOW 513 && mNotificationChannel.getImportance() > IMPORTANCE_UNSPECIFIED) { 514 return ACTION_MUTE; 515 } else { 516 if (mNotificationChannel.isImportantConversation()) { 517 return ACTION_FAVORITE; 518 } 519 } 520 return ACTION_DEFAULT; 521 } 522 updateChannel()523 private void updateChannel() { 524 mBgHandler.post( 525 new UpdateChannelRunnable(mINotificationManager, mPackageName, 526 mAppUid, mSelectedAction, mNotificationChannel)); 527 mEntry.markForUserTriggeredMovement(true); 528 mMainHandler.postDelayed( 529 () -> mOnUserInteractionCallback.onImportanceChanged(mEntry), 530 StackStateAnimator.ANIMATION_DURATION_STANDARD); 531 } 532 willBypassDnd()533 private boolean willBypassDnd() { 534 boolean bypassesDnd = false; 535 try { 536 int allowedSenders = mINotificationManager 537 .getConsolidatedNotificationPolicy().priorityConversationSenders; 538 bypassesDnd = allowedSenders == CONVERSATION_SENDERS_IMPORTANT 539 || allowedSenders == CONVERSATION_SENDERS_ANYONE; 540 } catch (RemoteException e) { 541 Log.e(TAG, "Could not check conversation senders", e); 542 } 543 return bypassesDnd; 544 } 545 willShowAsBubble()546 private boolean willShowAsBubble() { 547 return mBubbleMetadata != null 548 && BubblesManager.areBubblesEnabled(mContext, mSbn.getUser()); 549 } 550 551 @Override setGutsParent(NotificationGuts guts)552 public void setGutsParent(NotificationGuts guts) { 553 mGutsContainer = guts; 554 } 555 556 @Override willBeRemoved()557 public boolean willBeRemoved() { 558 return false; 559 } 560 561 @Override shouldBeSavedOnClose()562 public boolean shouldBeSavedOnClose() { 563 return mPressedApply; 564 } 565 566 @Override getContentView()567 public View getContentView() { 568 return this; 569 } 570 571 @Override handleCloseControls(boolean save, boolean force)572 public boolean handleCloseControls(boolean save, boolean force) { 573 if (save && mSelectedAction > -1) { 574 updateChannel(); 575 } 576 577 // Clear the selected importance when closing, so when when we open again, 578 // we starts from a clean state. 579 mSelectedAction = -1; 580 mPressedApply = false; 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 if (mBubblesManagerOptional.isPresent()) { 634 post(() -> mBubblesManagerOptional.get() 635 .onUserSetImportantConversation(mEntry)); 636 } 637 } 638 mChannelToUpdate.setImportance(Math.max( 639 mChannelToUpdate.getOriginalImportance(), IMPORTANCE_DEFAULT)); 640 break; 641 case ACTION_DEFAULT: 642 mChannelToUpdate.setImportance(Math.max( 643 mChannelToUpdate.getOriginalImportance(), IMPORTANCE_DEFAULT)); 644 if (mChannelToUpdate.isImportantConversation()) { 645 mChannelToUpdate.setImportantConversation(false); 646 mChannelToUpdate.setAllowBubbles(false); 647 } 648 break; 649 case ACTION_MUTE: 650 if (mChannelToUpdate.getImportance() == IMPORTANCE_UNSPECIFIED 651 || mChannelToUpdate.getImportance() >= IMPORTANCE_DEFAULT) { 652 mChannelToUpdate.setImportance(IMPORTANCE_LOW); 653 } 654 if (mChannelToUpdate.isImportantConversation()) { 655 mChannelToUpdate.setImportantConversation(false); 656 mChannelToUpdate.setAllowBubbles(false); 657 } 658 break; 659 } 660 661 mINotificationManager.updateNotificationChannelForPackage( 662 mAppPkg, mAppUid, mChannelToUpdate); 663 } catch (RemoteException e) { 664 Log.e(TAG, "Unable to update notification channel", e); 665 } 666 } 667 } 668 } 669