1 /* 2 * Copyright (C) 2017 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.IMPORTANCE_DEFAULT; 20 import static android.app.NotificationManager.IMPORTANCE_LOW; 21 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; 22 23 import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN; 24 25 import static java.lang.annotation.RetentionPolicy.SOURCE; 26 27 import android.annotation.IntDef; 28 import android.annotation.Nullable; 29 import android.app.INotificationManager; 30 import android.app.Notification; 31 import android.app.NotificationChannel; 32 import android.app.NotificationChannelGroup; 33 import android.content.Context; 34 import android.content.Intent; 35 import android.content.pm.ActivityInfo; 36 import android.content.pm.ApplicationInfo; 37 import android.content.pm.PackageManager; 38 import android.content.pm.ResolveInfo; 39 import android.graphics.drawable.Drawable; 40 import android.metrics.LogMaker; 41 import android.os.Handler; 42 import android.os.RemoteException; 43 import android.service.notification.StatusBarNotification; 44 import android.text.TextUtils; 45 import android.transition.ChangeBounds; 46 import android.transition.Fade; 47 import android.transition.TransitionManager; 48 import android.transition.TransitionSet; 49 import android.util.AttributeSet; 50 import android.util.Log; 51 import android.view.View; 52 import android.view.accessibility.AccessibilityEvent; 53 import android.widget.ImageView; 54 import android.widget.LinearLayout; 55 import android.widget.TextView; 56 57 import com.android.internal.annotations.VisibleForTesting; 58 import com.android.internal.logging.MetricsLogger; 59 import com.android.internal.logging.UiEventLogger; 60 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 61 import com.android.systemui.Dependency; 62 import com.android.systemui.R; 63 import com.android.systemui.statusbar.notification.VisualStabilityManager; 64 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 65 66 import java.lang.annotation.Retention; 67 import java.util.List; 68 import java.util.Set; 69 70 /** 71 * The guts of a notification revealed when performing a long press. 72 */ 73 public class NotificationInfo extends LinearLayout implements NotificationGuts.GutsContent { 74 private static final String TAG = "InfoGuts"; 75 private int mActualHeight; 76 77 @IntDef(prefix = { "ACTION_" }, value = { 78 ACTION_NONE, 79 ACTION_TOGGLE_ALERT, 80 ACTION_TOGGLE_SILENT, 81 }) 82 public @interface NotificationInfoAction { 83 } 84 85 public static final int ACTION_NONE = 0; 86 // standard controls 87 static final int ACTION_TOGGLE_SILENT = 2; 88 // standard controls 89 private static final int ACTION_TOGGLE_ALERT = 5; 90 91 private TextView mPriorityDescriptionView; 92 private TextView mSilentDescriptionView; 93 94 private INotificationManager mINotificationManager; 95 private PackageManager mPm; 96 private MetricsLogger mMetricsLogger; 97 private VisualStabilityManager mVisualStabilityManager; 98 private ChannelEditorDialogController mChannelEditorDialogController; 99 100 private String mPackageName; 101 private String mAppName; 102 private int mAppUid; 103 private String mDelegatePkg; 104 private int mNumUniqueChannelsInRow; 105 private Set<NotificationChannel> mUniqueChannelsInRow; 106 private NotificationChannel mSingleNotificationChannel; 107 private int mStartingChannelImportance; 108 private boolean mWasShownHighPriority; 109 private boolean mPressedApply; 110 private boolean mPresentingChannelEditorDialog = false; 111 112 /** 113 * The last importance level chosen by the user. Null if the user has not chosen an importance 114 * level; non-null once the user takes an action which indicates an explicit preference. 115 */ 116 @Nullable private Integer mChosenImportance; 117 private boolean mIsSingleDefaultChannel; 118 private boolean mIsNonblockable; 119 private StatusBarNotification mSbn; 120 private boolean mIsDeviceProvisioned; 121 122 private OnSettingsClickListener mOnSettingsClickListener; 123 private OnAppSettingsClickListener mAppSettingsClickListener; 124 private NotificationGuts mGutsContainer; 125 private Drawable mPkgIcon; 126 private UiEventLogger mUiEventLogger; 127 128 @VisibleForTesting 129 boolean mSkipPost = false; 130 131 // used by standard ui 132 private OnClickListener mOnAlert = v -> { 133 mChosenImportance = IMPORTANCE_DEFAULT; 134 applyAlertingBehavior(BEHAVIOR_ALERTING, true /* userTriggered */); 135 }; 136 137 // used by standard ui 138 private OnClickListener mOnSilent = v -> { 139 mChosenImportance = IMPORTANCE_LOW; 140 applyAlertingBehavior(BEHAVIOR_SILENT, true /* userTriggered */); 141 }; 142 143 // used by standard ui 144 private OnClickListener mOnDismissSettings = v -> { 145 mPressedApply = true; 146 mGutsContainer.closeControls(v, true); 147 }; 148 NotificationInfo(Context context, AttributeSet attrs)149 public NotificationInfo(Context context, AttributeSet attrs) { 150 super(context, attrs); 151 } 152 153 @Override onFinishInflate()154 protected void onFinishInflate() { 155 super.onFinishInflate(); 156 157 mPriorityDescriptionView = findViewById(R.id.alert_summary); 158 mSilentDescriptionView = findViewById(R.id.silence_summary); 159 } 160 161 // Specify a CheckSaveListener to override when/if the user's changes are committed. 162 public interface CheckSaveListener { 163 // Invoked when importance has changed and the NotificationInfo wants to try to save it. 164 // Listener should run saveImportance unless the change should be canceled. checkSave(Runnable saveImportance, StatusBarNotification sbn)165 void checkSave(Runnable saveImportance, StatusBarNotification sbn); 166 } 167 168 public interface OnSettingsClickListener { onClick(View v, NotificationChannel channel, int appUid)169 void onClick(View v, NotificationChannel channel, int appUid); 170 } 171 172 public interface OnAppSettingsClickListener { onClick(View v, Intent intent)173 void onClick(View v, Intent intent); 174 } 175 bindNotification( PackageManager pm, INotificationManager iNotificationManager, VisualStabilityManager visualStabilityManager, ChannelEditorDialogController channelEditorDialogController, String pkg, NotificationChannel notificationChannel, Set<NotificationChannel> uniqueChannelsInRow, NotificationEntry entry, OnSettingsClickListener onSettingsClick, OnAppSettingsClickListener onAppSettingsClick, UiEventLogger uiEventLogger, boolean isDeviceProvisioned, boolean isNonblockable, boolean wasShownHighPriority)176 public void bindNotification( 177 PackageManager pm, 178 INotificationManager iNotificationManager, 179 VisualStabilityManager visualStabilityManager, 180 ChannelEditorDialogController channelEditorDialogController, 181 String pkg, 182 NotificationChannel notificationChannel, 183 Set<NotificationChannel> uniqueChannelsInRow, 184 NotificationEntry entry, 185 OnSettingsClickListener onSettingsClick, 186 OnAppSettingsClickListener onAppSettingsClick, 187 UiEventLogger uiEventLogger, 188 boolean isDeviceProvisioned, 189 boolean isNonblockable, 190 boolean wasShownHighPriority) 191 throws RemoteException { 192 mINotificationManager = iNotificationManager; 193 mMetricsLogger = Dependency.get(MetricsLogger.class); 194 mVisualStabilityManager = visualStabilityManager; 195 mChannelEditorDialogController = channelEditorDialogController; 196 mPackageName = pkg; 197 mUniqueChannelsInRow = uniqueChannelsInRow; 198 mNumUniqueChannelsInRow = uniqueChannelsInRow.size(); 199 mSbn = entry.getSbn(); 200 mPm = pm; 201 mAppSettingsClickListener = onAppSettingsClick; 202 mAppName = mPackageName; 203 mOnSettingsClickListener = onSettingsClick; 204 mSingleNotificationChannel = notificationChannel; 205 mStartingChannelImportance = mSingleNotificationChannel.getImportance(); 206 mWasShownHighPriority = wasShownHighPriority; 207 mIsNonblockable = isNonblockable; 208 mAppUid = mSbn.getUid(); 209 mDelegatePkg = mSbn.getOpPkg(); 210 mIsDeviceProvisioned = isDeviceProvisioned; 211 mUiEventLogger = uiEventLogger; 212 213 int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage( 214 pkg, mAppUid, false /* includeDeleted */); 215 if (mNumUniqueChannelsInRow == 0) { 216 throw new IllegalArgumentException("bindNotification requires at least one channel"); 217 } else { 218 // Special behavior for the Default channel if no other channels have been defined. 219 mIsSingleDefaultChannel = mNumUniqueChannelsInRow == 1 220 && mSingleNotificationChannel.getId().equals( 221 NotificationChannel.DEFAULT_CHANNEL_ID) 222 && numTotalChannels == 1; 223 } 224 225 bindHeader(); 226 bindChannelDetails(); 227 228 bindInlineControls(); 229 230 logUiEvent(NotificationControlsEvent.NOTIFICATION_CONTROLS_OPEN); 231 mMetricsLogger.write(notificationControlsLogMaker()); 232 } 233 bindInlineControls()234 private void bindInlineControls() { 235 if (mIsNonblockable) { 236 findViewById(R.id.non_configurable_text).setVisibility(VISIBLE); 237 findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE); 238 findViewById(R.id.interruptiveness_settings).setVisibility(GONE); 239 ((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button); 240 findViewById(R.id.turn_off_notifications).setVisibility(GONE); 241 } else if (mNumUniqueChannelsInRow > 1) { 242 findViewById(R.id.non_configurable_text).setVisibility(GONE); 243 findViewById(R.id.interruptiveness_settings).setVisibility(GONE); 244 findViewById(R.id.non_configurable_multichannel_text).setVisibility(VISIBLE); 245 } else { 246 findViewById(R.id.non_configurable_text).setVisibility(GONE); 247 findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE); 248 findViewById(R.id.interruptiveness_settings).setVisibility(VISIBLE); 249 } 250 251 View turnOffButton = findViewById(R.id.turn_off_notifications); 252 turnOffButton.setOnClickListener(getTurnOffNotificationsClickListener()); 253 turnOffButton.setVisibility(turnOffButton.hasOnClickListeners() && !mIsNonblockable 254 ? VISIBLE : GONE); 255 256 View done = findViewById(R.id.done); 257 done.setOnClickListener(mOnDismissSettings); 258 done.setAccessibilityDelegate(mGutsContainer.getAccessibilityDelegate()); 259 260 View silent = findViewById(R.id.silence); 261 View alert = findViewById(R.id.alert); 262 silent.setOnClickListener(mOnSilent); 263 alert.setOnClickListener(mOnAlert); 264 265 int behavior = mWasShownHighPriority 266 ? BEHAVIOR_ALERTING 267 : BEHAVIOR_SILENT; 268 applyAlertingBehavior(behavior, false /* userTriggered */); 269 } 270 bindHeader()271 private void bindHeader() { 272 // Package name 273 mPkgIcon = null; 274 ApplicationInfo info; 275 try { 276 info = mPm.getApplicationInfo( 277 mPackageName, 278 PackageManager.MATCH_UNINSTALLED_PACKAGES 279 | PackageManager.MATCH_DISABLED_COMPONENTS 280 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE 281 | PackageManager.MATCH_DIRECT_BOOT_AWARE); 282 if (info != null) { 283 mAppName = String.valueOf(mPm.getApplicationLabel(info)); 284 mPkgIcon = mPm.getApplicationIcon(info); 285 } 286 } catch (PackageManager.NameNotFoundException e) { 287 // app is gone, just show package name and generic icon 288 mPkgIcon = mPm.getDefaultActivityIcon(); 289 } 290 ((ImageView) findViewById(R.id.pkg_icon)).setImageDrawable(mPkgIcon); 291 ((TextView) findViewById(R.id.pkg_name)).setText(mAppName); 292 293 // Delegate 294 bindDelegate(); 295 296 // Set up app settings link (i.e. Customize) 297 View settingsLinkView = findViewById(R.id.app_settings); 298 Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName, 299 mSingleNotificationChannel, 300 mSbn.getId(), mSbn.getTag()); 301 if (settingsIntent != null 302 && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) { 303 settingsLinkView.setVisibility(VISIBLE); 304 settingsLinkView.setOnClickListener((View view) -> { 305 mAppSettingsClickListener.onClick(view, settingsIntent); 306 }); 307 } else { 308 settingsLinkView.setVisibility(View.GONE); 309 } 310 311 // System Settings button. 312 final View settingsButton = findViewById(R.id.info); 313 settingsButton.setOnClickListener(getSettingsOnClickListener()); 314 settingsButton.setVisibility(settingsButton.hasOnClickListeners() ? VISIBLE : GONE); 315 } 316 getSettingsOnClickListener()317 private OnClickListener getSettingsOnClickListener() { 318 if (mAppUid >= 0 && mOnSettingsClickListener != null && mIsDeviceProvisioned) { 319 final int appUidF = mAppUid; 320 return ((View view) -> { 321 mOnSettingsClickListener.onClick(view, 322 mNumUniqueChannelsInRow > 1 ? null : mSingleNotificationChannel, 323 appUidF); 324 }); 325 } 326 return null; 327 } 328 getTurnOffNotificationsClickListener()329 private OnClickListener getTurnOffNotificationsClickListener() { 330 return ((View view) -> { 331 if (!mPresentingChannelEditorDialog && mChannelEditorDialogController != null) { 332 mPresentingChannelEditorDialog = true; 333 334 mChannelEditorDialogController.prepareDialogForApp(mAppName, mPackageName, mAppUid, 335 mUniqueChannelsInRow, mPkgIcon, mOnSettingsClickListener); 336 mChannelEditorDialogController.setOnFinishListener(() -> { 337 mPresentingChannelEditorDialog = false; 338 mGutsContainer.closeControls(this, false); 339 }); 340 mChannelEditorDialogController.show(); 341 } 342 }); 343 } 344 345 private void bindChannelDetails() throws RemoteException { 346 bindName(); 347 bindGroup(); 348 } 349 350 private void bindName() { 351 final TextView channelName = findViewById(R.id.channel_name); 352 if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) { 353 channelName.setVisibility(View.GONE); 354 } else { 355 channelName.setText(mSingleNotificationChannel.getName()); 356 } 357 } 358 359 private void bindDelegate() { 360 TextView delegateView = findViewById(R.id.delegate_name); 361 362 CharSequence delegatePkg = null; 363 if (!TextUtils.equals(mPackageName, mDelegatePkg)) { 364 // this notification was posted by a delegate! 365 delegateView.setVisibility(View.VISIBLE); 366 } else { 367 delegateView.setVisibility(View.GONE); 368 } 369 } 370 371 private void bindGroup() throws RemoteException { 372 // Set group information if this channel has an associated group. 373 CharSequence groupName = null; 374 if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) { 375 final NotificationChannelGroup notificationChannelGroup = 376 mINotificationManager.getNotificationChannelGroupForPackage( 377 mSingleNotificationChannel.getGroup(), mPackageName, mAppUid); 378 if (notificationChannelGroup != null) { 379 groupName = notificationChannelGroup.getName(); 380 } 381 } 382 TextView groupNameView = findViewById(R.id.group_name); 383 if (groupName != null) { 384 groupNameView.setText(groupName); 385 groupNameView.setVisibility(VISIBLE); 386 } else { 387 groupNameView.setVisibility(GONE); 388 } 389 } 390 391 private void saveImportance() { 392 if (!mIsNonblockable) { 393 if (mChosenImportance == null) { 394 mChosenImportance = mStartingChannelImportance; 395 } 396 updateImportance(); 397 } 398 } 399 400 /** 401 * Commits the updated importance values on the background thread. 402 */ 403 private void updateImportance() { 404 if (mChosenImportance != null) { 405 logUiEvent(NotificationControlsEvent.NOTIFICATION_CONTROLS_SAVE_IMPORTANCE); 406 mMetricsLogger.write(importanceChangeLogMaker()); 407 408 int newImportance = mChosenImportance; 409 if (mStartingChannelImportance != IMPORTANCE_UNSPECIFIED) { 410 if ((mWasShownHighPriority && mChosenImportance >= IMPORTANCE_DEFAULT) 411 || (!mWasShownHighPriority && mChosenImportance < IMPORTANCE_DEFAULT)) { 412 newImportance = mStartingChannelImportance; 413 } 414 } 415 416 Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER)); 417 bgHandler.post( 418 new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid, 419 mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null, 420 mStartingChannelImportance, newImportance)); 421 mVisualStabilityManager.temporarilyAllowReordering(); 422 } 423 } 424 425 @Override 426 public boolean post(Runnable action) { 427 if (mSkipPost) { 428 action.run(); 429 return true; 430 } else { 431 return super.post(action); 432 } 433 } 434 435 private void applyAlertingBehavior(@AlertingBehavior int behavior, boolean userTriggered) { 436 if (userTriggered) { 437 TransitionSet transition = new TransitionSet(); 438 transition.setOrdering(TransitionSet.ORDERING_TOGETHER); 439 transition.addTransition(new Fade(Fade.OUT)) 440 .addTransition(new ChangeBounds()) 441 .addTransition( 442 new Fade(Fade.IN) 443 .setStartDelay(150) 444 .setDuration(200) 445 .setInterpolator(FAST_OUT_SLOW_IN)); 446 transition.setDuration(350); 447 transition.setInterpolator(FAST_OUT_SLOW_IN); 448 TransitionManager.beginDelayedTransition(this, transition); 449 } 450 451 View alert = findViewById(R.id.alert); 452 View silence = findViewById(R.id.silence); 453 454 switch (behavior) { 455 case BEHAVIOR_ALERTING: 456 mPriorityDescriptionView.setVisibility(VISIBLE); 457 mSilentDescriptionView.setVisibility(GONE); 458 post(() -> { 459 alert.setSelected(true); 460 silence.setSelected(false); 461 }); 462 break; 463 464 case BEHAVIOR_SILENT: 465 mSilentDescriptionView.setVisibility(VISIBLE); 466 mPriorityDescriptionView.setVisibility(GONE); 467 post(() -> { 468 alert.setSelected(false); 469 silence.setSelected(true); 470 }); 471 break; 472 473 default: 474 throw new IllegalArgumentException("Unrecognized alerting behavior: " + behavior); 475 } 476 477 boolean isAChange = mWasShownHighPriority != (behavior == BEHAVIOR_ALERTING); 478 TextView done = findViewById(R.id.done); 479 done.setText(isAChange 480 ? R.string.inline_ok_button 481 : R.string.inline_done_button); 482 } 483 484 @Override 485 public void onFinishedClosing() { 486 if (mChosenImportance != null) { 487 mStartingChannelImportance = mChosenImportance; 488 } 489 490 bindInlineControls(); 491 492 logUiEvent(NotificationControlsEvent.NOTIFICATION_CONTROLS_CLOSE); 493 mMetricsLogger.write(notificationControlsLogMaker().setType(MetricsEvent.TYPE_CLOSE)); 494 } 495 496 @Override 497 public boolean needsFalsingProtection() { 498 return true; 499 } 500 501 @Override 502 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 503 super.onInitializeAccessibilityEvent(event); 504 if (mGutsContainer != null && 505 event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { 506 if (mGutsContainer.isExposed()) { 507 event.getText().add(mContext.getString( 508 R.string.notification_channel_controls_opened_accessibility, mAppName)); 509 } else { 510 event.getText().add(mContext.getString( 511 R.string.notification_channel_controls_closed_accessibility, mAppName)); 512 } 513 } 514 } 515 516 private Intent getAppSettingsIntent(PackageManager pm, String packageName, 517 NotificationChannel channel, int id, String tag) { 518 Intent intent = new Intent(Intent.ACTION_MAIN) 519 .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES) 520 .setPackage(packageName); 521 final List<ResolveInfo> resolveInfos = pm.queryIntentActivities( 522 intent, 523 PackageManager.MATCH_DEFAULT_ONLY 524 ); 525 if (resolveInfos == null || resolveInfos.size() == 0 || resolveInfos.get(0) == null) { 526 return null; 527 } 528 final ActivityInfo activityInfo = resolveInfos.get(0).activityInfo; 529 intent.setClassName(activityInfo.packageName, activityInfo.name); 530 if (channel != null) { 531 intent.putExtra(Notification.EXTRA_CHANNEL_ID, channel.getId()); 532 } 533 intent.putExtra(Notification.EXTRA_NOTIFICATION_ID, id); 534 intent.putExtra(Notification.EXTRA_NOTIFICATION_TAG, tag); 535 return intent; 536 } 537 538 @Override 539 public void setGutsParent(NotificationGuts guts) { 540 mGutsContainer = guts; 541 } 542 543 @Override 544 public boolean willBeRemoved() { 545 return false; 546 } 547 548 @Override 549 public boolean shouldBeSaved() { 550 return mPressedApply; 551 } 552 553 @Override 554 public View getContentView() { 555 return this; 556 } 557 558 @Override 559 public boolean handleCloseControls(boolean save, boolean force) { 560 if (mPresentingChannelEditorDialog && mChannelEditorDialogController != null) { 561 mPresentingChannelEditorDialog = false; 562 // No need for the finish listener because we're closing 563 mChannelEditorDialogController.setOnFinishListener(null); 564 mChannelEditorDialogController.close(); 565 } 566 567 // Save regardless of the importance so we can lock the importance field if the user wants 568 // to keep getting notifications 569 if (save) { 570 saveImportance(); 571 } 572 return false; 573 } 574 575 @Override 576 public int getActualHeight() { 577 // Because we're animating the bounds, getHeight will return the small height at the 578 // beginning of the animation. Instead we'd want it to already return the end value 579 return mActualHeight; 580 } 581 582 @Override 583 protected void onLayout(boolean changed, int l, int t, int r, int b) { 584 super.onLayout(changed, l, t, r, b); 585 mActualHeight = getHeight(); 586 } 587 588 @VisibleForTesting 589 public boolean isAnimating() { 590 return false; 591 } 592 593 /** 594 * Runnable to either update the given channel (with a new importance value) or, if no channel 595 * is provided, update notifications enabled state for the package. 596 */ 597 private static class UpdateImportanceRunnable implements Runnable { 598 private final INotificationManager mINotificationManager; 599 private final String mPackageName; 600 private final int mAppUid; 601 private final @Nullable NotificationChannel mChannelToUpdate; 602 private final int mCurrentImportance; 603 private final int mNewImportance; 604 605 606 public UpdateImportanceRunnable(INotificationManager notificationManager, 607 String packageName, int appUid, @Nullable NotificationChannel channelToUpdate, 608 int currentImportance, int newImportance) { 609 mINotificationManager = notificationManager; 610 mPackageName = packageName; 611 mAppUid = appUid; 612 mChannelToUpdate = channelToUpdate; 613 mCurrentImportance = currentImportance; 614 mNewImportance = newImportance; 615 } 616 617 @Override 618 public void run() { 619 try { 620 if (mChannelToUpdate != null) { 621 mChannelToUpdate.setImportance(mNewImportance); 622 mChannelToUpdate.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); 623 mINotificationManager.updateNotificationChannelForPackage( 624 mPackageName, mAppUid, mChannelToUpdate); 625 } else { 626 // For notifications with more than one channel, update notification enabled 627 // state. If the importance was lowered, we disable notifications. 628 mINotificationManager.setNotificationsEnabledWithImportanceLockForPackage( 629 mPackageName, mAppUid, mNewImportance >= mCurrentImportance); 630 } 631 } catch (RemoteException e) { 632 Log.e(TAG, "Unable to update notification importance", e); 633 } 634 } 635 } 636 637 private void logUiEvent(NotificationControlsEvent event) { 638 if (mSbn != null) { 639 mUiEventLogger.logWithInstanceId(event, 640 mSbn.getUid(), mSbn.getPackageName(), mSbn.getInstanceId()); 641 } 642 } 643 644 /** 645 * Returns a LogMaker with all available notification information. 646 * Caller should set category, type, and maybe subtype, before passing it to mMetricsLogger. 647 * @return LogMaker 648 */ 649 private LogMaker getLogMaker() { 650 // The constructor requires a category, so also do it in the other branch for consistency. 651 return mSbn == null ? new LogMaker(MetricsEvent.NOTIFICATION_BLOCKING_HELPER) 652 : mSbn.getLogMaker().setCategory(MetricsEvent.NOTIFICATION_BLOCKING_HELPER); 653 } 654 655 /** 656 * Returns an initialized LogMaker for logging importance changes. 657 * The caller may override the type before passing it to mMetricsLogger. 658 * @return LogMaker 659 */ 660 private LogMaker importanceChangeLogMaker() { 661 Integer chosenImportance = 662 mChosenImportance != null ? mChosenImportance : mStartingChannelImportance; 663 return getLogMaker().setCategory(MetricsEvent.ACTION_SAVE_IMPORTANCE) 664 .setType(MetricsEvent.TYPE_ACTION) 665 .setSubtype(chosenImportance - mStartingChannelImportance); 666 } 667 668 /** 669 * Returns an initialized LogMaker for logging open/close of the info display. 670 * The caller may override the type before passing it to mMetricsLogger. 671 * @return LogMaker 672 */ 673 private LogMaker notificationControlsLogMaker() { 674 return getLogMaker().setCategory(MetricsEvent.ACTION_NOTE_CONTROLS) 675 .setType(MetricsEvent.TYPE_OPEN) 676 .setSubtype(MetricsEvent.BLOCKING_HELPER_UNKNOWN); 677 } 678 679 @Retention(SOURCE) 680 @IntDef({BEHAVIOR_ALERTING, BEHAVIOR_SILENT}) 681 private @interface AlertingBehavior {} 682 private static final int BEHAVIOR_ALERTING = 0; 683 private static final int BEHAVIOR_SILENT = 1; 684 } 685