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