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