1 /* 2 * Copyright (C) 2015 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 package com.android.settings.notification; 17 18 import static android.app.NotificationManager.IMPORTANCE_NONE; 19 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; 20 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED; 21 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC; 22 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER; 23 import static android.os.UserHandle.USER_SYSTEM; 24 25 import android.Manifest; 26 import android.app.INotificationManager; 27 import android.app.NotificationChannel; 28 import android.app.NotificationChannelGroup; 29 import android.app.NotificationHistory; 30 import android.app.NotificationManager; 31 import android.app.compat.CompatChanges; 32 import android.app.role.RoleManager; 33 import android.app.usage.IUsageStatsManager; 34 import android.app.usage.UsageEvents; 35 import android.companion.ICompanionDeviceManager; 36 import android.compat.annotation.ChangeId; 37 import android.compat.annotation.EnabledAfter; 38 import android.compat.annotation.EnabledSince; 39 import android.content.ComponentName; 40 import android.content.Context; 41 import android.content.Intent; 42 import android.content.pm.ApplicationInfo; 43 import android.content.pm.LauncherApps; 44 import android.content.pm.PackageInfo; 45 import android.content.pm.PackageManager; 46 import android.content.pm.ParceledListSlice; 47 import android.content.pm.ShortcutInfo; 48 import android.content.pm.ShortcutManager; 49 import android.graphics.drawable.Drawable; 50 import android.os.Build; 51 import android.os.RemoteException; 52 import android.os.ServiceManager; 53 import android.os.UserHandle; 54 import android.provider.Settings; 55 import android.service.notification.ConversationChannelWrapper; 56 import android.service.notification.NotificationListenerFilter; 57 import android.text.format.DateUtils; 58 import android.util.IconDrawableFactory; 59 import android.util.Log; 60 61 import androidx.annotation.VisibleForTesting; 62 63 import com.android.internal.util.CollectionUtils; 64 import com.android.settingslib.R; 65 import com.android.settingslib.Utils; 66 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 67 import com.android.settingslib.bluetooth.LocalBluetoothManager; 68 import com.android.settingslib.notification.ConversationIconFactory; 69 import com.android.settingslib.utils.StringUtil; 70 71 import java.util.ArrayList; 72 import java.util.Arrays; 73 import java.util.Collection; 74 import java.util.HashMap; 75 import java.util.List; 76 import java.util.Map; 77 import java.util.Objects; 78 79 public class NotificationBackend { 80 private static final String TAG = "NotificationBackend"; 81 82 static IUsageStatsManager sUsageStatsManager = IUsageStatsManager.Stub.asInterface( 83 ServiceManager.getService(Context.USAGE_STATS_SERVICE)); 84 private static final int DAYS_TO_CHECK = 7; 85 static INotificationManager sINM = INotificationManager.Stub.asInterface( 86 ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 87 loadAppRow(Context context, PackageManager pm, ApplicationInfo app)88 public AppRow loadAppRow(Context context, PackageManager pm, ApplicationInfo app) { 89 final AppRow row = new AppRow(); 90 row.pkg = app.packageName; 91 row.uid = app.uid; 92 try { 93 row.label = app.loadLabel(pm); 94 } catch (Throwable t) { 95 Log.e(TAG, "Error loading application label for " + row.pkg, t); 96 row.label = row.pkg; 97 } 98 row.icon = IconDrawableFactory.newInstance(context).getBadgedIcon(app); 99 row.banned = getNotificationsBanned(row.pkg, row.uid); 100 row.showBadge = canShowBadge(row.pkg, row.uid); 101 row.bubblePreference = getBubblePreference(row.pkg, row.uid); 102 row.userId = UserHandle.getUserId(row.uid); 103 row.blockedChannelCount = getBlockedChannelCount(row.pkg, row.uid); 104 row.channelCount = getChannelCount(row.pkg, row.uid); 105 recordAggregatedUsageEvents(context, row); 106 return row; 107 } 108 loadAppRow(Context context, PackageManager pm, PackageInfo app)109 public AppRow loadAppRow(Context context, PackageManager pm, PackageInfo app) { 110 final AppRow row = loadAppRow(context, pm, app.applicationInfo); 111 recordCanBeBlocked(app, row); 112 return row; 113 } 114 recordCanBeBlocked(PackageInfo app, AppRow row)115 void recordCanBeBlocked(PackageInfo app, AppRow row) { 116 try { 117 row.systemApp = row.lockedImportance = 118 sINM.isImportanceLocked(app.packageName, app.applicationInfo.uid); 119 } catch (RemoteException e) { 120 Log.w(TAG, "Error calling NMS", e); 121 } 122 123 // if the app targets T but has not requested the permission, we cannot change the 124 // permission state 125 if (app.applicationInfo.targetSdkVersion > Build.VERSION_CODES.S_V2) { 126 if (app.requestedPermissions == null || Arrays.stream(app.requestedPermissions) 127 .noneMatch(p -> p.equals(android.Manifest.permission.POST_NOTIFICATIONS))) { 128 row.lockedImportance = true; 129 row.permissionStateLocked = true; 130 } 131 } 132 } 133 getDeviceList(ICompanionDeviceManager cdm, LocalBluetoothManager lbm, String pkg, int userId)134 static public CharSequence getDeviceList(ICompanionDeviceManager cdm, LocalBluetoothManager lbm, 135 String pkg, int userId) { 136 boolean multiple = false; 137 StringBuilder sb = new StringBuilder(); 138 139 try { 140 List<String> associatedMacAddrs = CollectionUtils.mapNotNull( 141 cdm.getAssociations(pkg, userId), 142 a -> a.isSelfManaged() ? null : a.getDeviceMacAddress().toString()); 143 if (associatedMacAddrs != null) { 144 for (String assocMac : associatedMacAddrs) { 145 final Collection<CachedBluetoothDevice> cachedDevices = 146 lbm.getCachedDeviceManager().getCachedDevicesCopy(); 147 for (CachedBluetoothDevice cachedBluetoothDevice : cachedDevices) { 148 if (Objects.equals(assocMac, cachedBluetoothDevice.getAddress())) { 149 if (multiple) { 150 sb.append(", "); 151 } else { 152 multiple = true; 153 } 154 sb.append(cachedBluetoothDevice.getName()); 155 } 156 } 157 } 158 } 159 } catch (RemoteException e) { 160 Log.w(TAG, "Error calling CDM", e); 161 } 162 return sb.toString(); 163 } 164 enableSwitch(Context context, ApplicationInfo app)165 public boolean enableSwitch(Context context, ApplicationInfo app) { 166 try { 167 PackageInfo info = context.getPackageManager().getPackageInfo( 168 app.packageName, PackageManager.GET_PERMISSIONS); 169 final AppRow row = new AppRow(); 170 recordCanBeBlocked(info, row); 171 boolean systemBlockable = !row.systemApp || (row.systemApp && row.banned); 172 return systemBlockable && !row.lockedImportance; 173 } catch (PackageManager.NameNotFoundException e) { 174 e.printStackTrace(); 175 } 176 return false; 177 } 178 getNotificationsBanned(String pkg, int uid)179 public boolean getNotificationsBanned(String pkg, int uid) { 180 try { 181 final boolean enabled = sINM.areNotificationsEnabledForPackage(pkg, uid); 182 return !enabled; 183 } catch (Exception e) { 184 Log.w(TAG, "Error calling NoMan", e); 185 return false; 186 } 187 } 188 setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled)189 public boolean setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) { 190 try { 191 if (onlyHasDefaultChannel(pkg, uid)) { 192 NotificationChannel defaultChannel = 193 getChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID, null); 194 defaultChannel.setImportance(enabled ? IMPORTANCE_UNSPECIFIED : IMPORTANCE_NONE); 195 updateChannel(pkg, uid, defaultChannel); 196 } 197 sINM.setNotificationsEnabledForPackage(pkg, uid, enabled); 198 return true; 199 } catch (Exception e) { 200 Log.w(TAG, "Error calling NoMan", e); 201 return false; 202 } 203 } 204 canShowBadge(String pkg, int uid)205 public boolean canShowBadge(String pkg, int uid) { 206 try { 207 return sINM.canShowBadge(pkg, uid); 208 } catch (Exception e) { 209 Log.w(TAG, "Error calling NoMan", e); 210 return false; 211 } 212 } 213 setShowBadge(String pkg, int uid, boolean showBadge)214 public boolean setShowBadge(String pkg, int uid, boolean showBadge) { 215 try { 216 sINM.setShowBadge(pkg, uid, showBadge); 217 return true; 218 } catch (Exception e) { 219 Log.w(TAG, "Error calling NoMan", e); 220 return false; 221 } 222 } 223 getBubblePreference(String pkg, int uid)224 public int getBubblePreference(String pkg, int uid) { 225 try { 226 return sINM.getBubblePreferenceForPackage(pkg, uid); 227 } catch (Exception e) { 228 Log.w(TAG, "Error calling NoMan", e); 229 return -1; 230 } 231 } 232 setAllowBubbles(String pkg, int uid, int preference)233 public boolean setAllowBubbles(String pkg, int uid, int preference) { 234 try { 235 sINM.setBubblesAllowed(pkg, uid, preference); 236 return true; 237 } catch (Exception e) { 238 Log.w(TAG, "Error calling NoMan", e); 239 return false; 240 } 241 } 242 getChannel(String pkg, int uid, String channelId)243 public NotificationChannel getChannel(String pkg, int uid, String channelId) { 244 return getChannel(pkg, uid, channelId, null); 245 } 246 getChannel(String pkg, int uid, String channelId, String conversationId)247 public NotificationChannel getChannel(String pkg, int uid, String channelId, 248 String conversationId) { 249 if (channelId == null) { 250 return null; 251 } 252 try { 253 return sINM.getNotificationChannelForPackage(pkg, uid, channelId, conversationId, true); 254 } catch (Exception e) { 255 Log.w(TAG, "Error calling NoMan", e); 256 return null; 257 } 258 } 259 getGroup(String pkg, int uid, String groupId)260 public NotificationChannelGroup getGroup(String pkg, int uid, String groupId) { 261 if (groupId == null) { 262 return null; 263 } 264 try { 265 return sINM.getNotificationChannelGroupForPackage(groupId, pkg, uid); 266 } catch (Exception e) { 267 Log.w(TAG, "Error calling NoMan", e); 268 return null; 269 } 270 } 271 getGroups(String pkg, int uid)272 public ParceledListSlice<NotificationChannelGroup> getGroups(String pkg, int uid) { 273 try { 274 return sINM.getNotificationChannelGroupsForPackage(pkg, uid, false); 275 } catch (Exception e) { 276 Log.w(TAG, "Error calling NoMan", e); 277 return ParceledListSlice.emptyList(); 278 } 279 } 280 getConversations(String pkg, int uid)281 public ParceledListSlice<ConversationChannelWrapper> getConversations(String pkg, int uid) { 282 try { 283 return sINM.getConversationsForPackage(pkg, uid); 284 } catch (Exception e) { 285 Log.w(TAG, "Error calling NoMan", e); 286 return ParceledListSlice.emptyList(); 287 } 288 } 289 getConversations(boolean onlyImportant)290 public ParceledListSlice<ConversationChannelWrapper> getConversations(boolean onlyImportant) { 291 try { 292 return sINM.getConversations(onlyImportant); 293 } catch (Exception e) { 294 Log.w(TAG, "Error calling NoMan", e); 295 return ParceledListSlice.emptyList(); 296 } 297 } 298 hasSentValidMsg(String pkg, int uid)299 public boolean hasSentValidMsg(String pkg, int uid) { 300 try { 301 return sINM.hasSentValidMsg(pkg, uid); 302 } catch (Exception e) { 303 Log.w(TAG, "Error calling NoMan", e); 304 return false; 305 } 306 } 307 isInInvalidMsgState(String pkg, int uid)308 public boolean isInInvalidMsgState(String pkg, int uid) { 309 try { 310 return sINM.isInInvalidMsgState(pkg, uid); 311 } catch (Exception e) { 312 Log.w(TAG, "Error calling NoMan", e); 313 return false; 314 } 315 } 316 hasUserDemotedInvalidMsgApp(String pkg, int uid)317 public boolean hasUserDemotedInvalidMsgApp(String pkg, int uid) { 318 try { 319 return sINM.hasUserDemotedInvalidMsgApp(pkg, uid); 320 } catch (Exception e) { 321 Log.w(TAG, "Error calling NoMan", e); 322 return false; 323 } 324 } 325 setInvalidMsgAppDemoted(String pkg, int uid, boolean isDemoted)326 public void setInvalidMsgAppDemoted(String pkg, int uid, boolean isDemoted) { 327 try { 328 sINM.setInvalidMsgAppDemoted(pkg, uid, isDemoted); 329 } catch (Exception e) { 330 Log.w(TAG, "Error calling NoMan", e); 331 } 332 } 333 hasSentValidBubble(String pkg, int uid)334 public boolean hasSentValidBubble(String pkg, int uid) { 335 try { 336 return sINM.hasSentValidBubble(pkg, uid); 337 } catch (Exception e) { 338 Log.w(TAG, "Error calling NoMan", e); 339 return false; 340 } 341 } 342 343 /** 344 * Returns all notification channels associated with the package and uid that will bypass DND 345 */ getNotificationChannelsBypassingDnd(String pkg, int uid)346 public ParceledListSlice<NotificationChannel> getNotificationChannelsBypassingDnd(String pkg, 347 int uid) { 348 try { 349 return sINM.getNotificationChannelsBypassingDnd(pkg, uid); 350 } catch (Exception e) { 351 Log.w(TAG, "Error calling NoMan", e); 352 return ParceledListSlice.emptyList(); 353 } 354 } 355 updateChannel(String pkg, int uid, NotificationChannel channel)356 public void updateChannel(String pkg, int uid, NotificationChannel channel) { 357 try { 358 sINM.updateNotificationChannelForPackage(pkg, uid, channel); 359 } catch (Exception e) { 360 Log.w(TAG, "Error calling NoMan", e); 361 } 362 } 363 updateChannelGroup(String pkg, int uid, NotificationChannelGroup group)364 public void updateChannelGroup(String pkg, int uid, NotificationChannelGroup group) { 365 try { 366 sINM.updateNotificationChannelGroupForPackage(pkg, uid, group); 367 } catch (Exception e) { 368 Log.w(TAG, "Error calling NoMan", e); 369 } 370 } 371 getDeletedChannelCount(String pkg, int uid)372 public int getDeletedChannelCount(String pkg, int uid) { 373 try { 374 return sINM.getDeletedChannelCount(pkg, uid); 375 } catch (Exception e) { 376 Log.w(TAG, "Error calling NoMan", e); 377 return 0; 378 } 379 } 380 getBlockedChannelCount(String pkg, int uid)381 public int getBlockedChannelCount(String pkg, int uid) { 382 try { 383 return sINM.getBlockedChannelCount(pkg, uid); 384 } catch (Exception e) { 385 Log.w(TAG, "Error calling NoMan", e); 386 return 0; 387 } 388 } 389 onlyHasDefaultChannel(String pkg, int uid)390 public boolean onlyHasDefaultChannel(String pkg, int uid) { 391 try { 392 return sINM.onlyHasDefaultChannel(pkg, uid); 393 } catch (Exception e) { 394 Log.w(TAG, "Error calling NoMan", e); 395 return false; 396 } 397 } 398 getChannelCount(String pkg, int uid)399 public int getChannelCount(String pkg, int uid) { 400 try { 401 return sINM.getNumNotificationChannelsForPackage(pkg, uid, false); 402 } catch (Exception e) { 403 Log.w(TAG, "Error calling NoMan", e); 404 return 0; 405 } 406 } 407 shouldHideSilentStatusBarIcons(Context context)408 public boolean shouldHideSilentStatusBarIcons(Context context) { 409 try { 410 return sINM.shouldHideSilentStatusIcons(context.getPackageName()); 411 } catch (Exception e) { 412 Log.w(TAG, "Error calling NoMan", e); 413 return false; 414 } 415 } 416 setHideSilentStatusIcons(boolean hide)417 public void setHideSilentStatusIcons(boolean hide) { 418 try { 419 sINM.setHideSilentStatusIcons(hide); 420 } catch (Exception e) { 421 Log.w(TAG, "Error calling NoMan", e); 422 } 423 } 424 allowAssistantAdjustment(String capability, boolean allowed)425 public void allowAssistantAdjustment(String capability, boolean allowed) { 426 try { 427 if (allowed) { 428 sINM.allowAssistantAdjustment(capability); 429 } else { 430 sINM.disallowAssistantAdjustment(capability); 431 } 432 } catch (Exception e) { 433 Log.w(TAG, "Error calling NoMan", e); 434 } 435 } 436 getAssistantAdjustments(String pkg)437 public List<String> getAssistantAdjustments(String pkg) { 438 try { 439 return sINM.getAllowedAssistantAdjustments(pkg); 440 } catch (Exception e) { 441 Log.w(TAG, "Error calling NoMan", e); 442 } 443 return new ArrayList<>(); 444 } 445 showSilentInStatusBar(String pkg)446 public boolean showSilentInStatusBar(String pkg) { 447 try { 448 return !sINM.shouldHideSilentStatusIcons(pkg); 449 } catch (Exception e) { 450 Log.w(TAG, "Error calling NoMan", e); 451 } 452 return false; 453 } 454 getNotificationHistory(String pkg, String attributionTag)455 public NotificationHistory getNotificationHistory(String pkg, String attributionTag) { 456 try { 457 return sINM.getNotificationHistory(pkg, attributionTag); 458 } catch (Exception e) { 459 Log.w(TAG, "Error calling NoMan", e); 460 } 461 return new NotificationHistory(); 462 } 463 recordAggregatedUsageEvents(Context context, AppRow appRow)464 protected void recordAggregatedUsageEvents(Context context, AppRow appRow) { 465 long now = System.currentTimeMillis(); 466 long startTime = now - (DateUtils.DAY_IN_MILLIS * DAYS_TO_CHECK); 467 UsageEvents events = null; 468 try { 469 events = sUsageStatsManager.queryEventsForPackageForUser( 470 startTime, now, appRow.userId, appRow.pkg, context.getPackageName()); 471 } catch (RemoteException e) { 472 e.printStackTrace(); 473 } 474 recordAggregatedUsageEvents(events, appRow); 475 } 476 recordAggregatedUsageEvents(UsageEvents events, AppRow appRow)477 protected void recordAggregatedUsageEvents(UsageEvents events, AppRow appRow) { 478 appRow.sentByChannel = new HashMap<>(); 479 appRow.sentByApp = new NotificationsSentState(); 480 if (events != null) { 481 UsageEvents.Event event = new UsageEvents.Event(); 482 while (events.hasNextEvent()) { 483 events.getNextEvent(event); 484 485 if (event.getEventType() == UsageEvents.Event.NOTIFICATION_INTERRUPTION) { 486 String channelId = event.mNotificationChannelId; 487 if (channelId != null) { 488 NotificationsSentState stats = appRow.sentByChannel.get(channelId); 489 if (stats == null) { 490 stats = new NotificationsSentState(); 491 appRow.sentByChannel.put(channelId, stats); 492 } 493 if (event.getTimeStamp() > stats.lastSent) { 494 stats.lastSent = event.getTimeStamp(); 495 appRow.sentByApp.lastSent = event.getTimeStamp(); 496 } 497 stats.sentCount++; 498 appRow.sentByApp.sentCount++; 499 calculateAvgSentCounts(stats); 500 } 501 } 502 503 } 504 calculateAvgSentCounts(appRow.sentByApp); 505 } 506 } 507 getSentSummary(Context context, NotificationsSentState state, boolean sortByRecency)508 public static CharSequence getSentSummary(Context context, NotificationsSentState state, 509 boolean sortByRecency) { 510 if (state == null) { 511 return null; 512 } 513 if (sortByRecency) { 514 if (state.lastSent == 0) { 515 return context.getString(R.string.notifications_sent_never); 516 } 517 return StringUtil.formatRelativeTime( 518 context, System.currentTimeMillis() - state.lastSent, true); 519 } else { 520 if (state.avgSentDaily > 0) { 521 return context.getResources().getQuantityString(R.plurals.notifications_sent_daily, 522 state.avgSentDaily, state.avgSentDaily); 523 } 524 return context.getResources().getQuantityString(R.plurals.notifications_sent_weekly, 525 state.avgSentWeekly, state.avgSentWeekly); 526 } 527 } 528 calculateAvgSentCounts(NotificationsSentState stats)529 private void calculateAvgSentCounts(NotificationsSentState stats) { 530 if (stats != null) { 531 stats.avgSentDaily = Math.round((float) stats.sentCount / DAYS_TO_CHECK); 532 if (stats.sentCount < DAYS_TO_CHECK) { 533 stats.avgSentWeekly = stats.sentCount; 534 } 535 } 536 } 537 getAllowedNotificationAssistant()538 public ComponentName getAllowedNotificationAssistant() { 539 try { 540 return sINM.getAllowedNotificationAssistant(); 541 } catch (Exception e) { 542 Log.w(TAG, "Error calling NoMan", e); 543 return null; 544 } 545 } 546 getDefaultNotificationAssistant()547 public ComponentName getDefaultNotificationAssistant() { 548 try { 549 return sINM.getDefaultNotificationAssistant(); 550 } catch (Exception e) { 551 Log.w(TAG, "Error calling NoMan", e); 552 return null; 553 } 554 } 555 setNASMigrationDoneAndResetDefault(int userId, boolean loadFromConfig)556 public void setNASMigrationDoneAndResetDefault(int userId, boolean loadFromConfig) { 557 try { 558 sINM.setNASMigrationDoneAndResetDefault(userId, loadFromConfig); 559 } catch (Exception e) { 560 Log.w(TAG, "Error calling NoMan", e); 561 } 562 } 563 setNotificationAssistantGranted(ComponentName cn)564 public boolean setNotificationAssistantGranted(ComponentName cn) { 565 try { 566 sINM.setNotificationAssistantAccessGranted(cn, true); 567 if (cn == null) { 568 return sINM.getAllowedNotificationAssistant() == null; 569 } else { 570 return cn.equals(sINM.getAllowedNotificationAssistant()); 571 } 572 } catch (Exception e) { 573 Log.w(TAG, "Error calling NoMan", e); 574 return false; 575 } 576 } 577 createConversationNotificationChannel(String pkg, int uid, NotificationChannel parent, String conversationId)578 public void createConversationNotificationChannel(String pkg, int uid, 579 NotificationChannel parent, String conversationId) { 580 try { 581 sINM.createConversationNotificationChannelForPackage(pkg, uid, parent, conversationId); 582 } catch (Exception e) { 583 Log.w(TAG, "Error calling NoMan", e); 584 } 585 } 586 getConversationInfo(Context context, String pkg, int uid, String id)587 public ShortcutInfo getConversationInfo(Context context, String pkg, int uid, String id) { 588 LauncherApps la = context.getSystemService(LauncherApps.class); 589 590 LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery() 591 .setPackage(pkg) 592 .setQueryFlags(FLAG_MATCH_DYNAMIC 593 | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER | FLAG_MATCH_CACHED) 594 .setShortcutIds(Arrays.asList(id)); 595 List<ShortcutInfo> shortcuts = la.getShortcuts( 596 query, UserHandle.of(UserHandle.getUserId(uid))); 597 if (shortcuts != null && !shortcuts.isEmpty()) { 598 return shortcuts.get(0); 599 } 600 return null; 601 } 602 getConversationDrawable(Context context, ShortcutInfo info, String pkg, int uid, boolean important)603 public Drawable getConversationDrawable(Context context, ShortcutInfo info, String pkg, 604 int uid, boolean important) { 605 if (info == null) { 606 return null; 607 } 608 ConversationIconFactory iconFactory = new ConversationIconFactory(context, 609 context.getSystemService(LauncherApps.class), 610 context.getPackageManager(), 611 IconDrawableFactory.newInstance(context, false), 612 context.getResources().getDimensionPixelSize( 613 R.dimen.conversation_icon_size)); 614 return iconFactory.getConversationDrawable(info, pkg, uid, important); 615 } 616 requestPinShortcut(Context context, ShortcutInfo shortcutInfo)617 public void requestPinShortcut(Context context, ShortcutInfo shortcutInfo) { 618 ShortcutManager sm = context.getSystemService(ShortcutManager.class); 619 sm.requestPinShortcut(shortcutInfo, null); 620 } 621 resetNotificationImportance()622 public void resetNotificationImportance() { 623 try { 624 sINM.unlockAllNotificationChannels(); 625 } catch (Exception e) { 626 Log.w(TAG, "Error calling NoMan", e); 627 } 628 } 629 getListenerFilter(ComponentName cn, int userId)630 public NotificationListenerFilter getListenerFilter(ComponentName cn, int userId) { 631 NotificationListenerFilter nlf = null; 632 try { 633 nlf = sINM.getListenerFilter(cn, userId); 634 } catch (Exception e) { 635 Log.w(TAG, "Error calling NoMan", e); 636 } 637 return nlf != null ? nlf : new NotificationListenerFilter(); 638 } 639 setListenerFilter(ComponentName cn, int userId, NotificationListenerFilter nlf)640 public void setListenerFilter(ComponentName cn, int userId, NotificationListenerFilter nlf) { 641 try { 642 sINM.setListenerFilter(cn, userId, nlf); 643 } catch (Exception e) { 644 Log.w(TAG, "Error calling NoMan", e); 645 } 646 } 647 isNotificationListenerAccessGranted(ComponentName cn)648 public boolean isNotificationListenerAccessGranted(ComponentName cn) { 649 try { 650 return sINM.isNotificationListenerAccessGranted(cn); 651 } catch (Exception e) { 652 Log.w(TAG, "Error calling NoMan", e); 653 } 654 return false; 655 } 656 657 @VisibleForTesting setNm(INotificationManager inm)658 void setNm(INotificationManager inm) { 659 sINM = inm; 660 } 661 662 /** 663 * NotificationsSentState contains how often an app sends notifications and how recently it sent 664 * one. 665 */ 666 public static class NotificationsSentState { 667 public int avgSentDaily = 0; 668 public int avgSentWeekly = 0; 669 public long lastSent = 0; 670 public int sentCount = 0; 671 } 672 673 static class Row { 674 public String section; 675 } 676 677 public static class AppRow extends Row { 678 public String pkg; 679 public int uid; 680 public Drawable icon; 681 public CharSequence label; 682 public Intent settingsIntent; 683 public boolean banned; 684 public boolean first; // first app in section 685 public boolean systemApp; 686 public boolean lockedImportance; 687 public boolean showBadge; 688 // For apps target T but have not but has not requested the permission 689 // we cannot change the permission state 690 public boolean permissionStateLocked; 691 public int bubblePreference = NotificationManager.BUBBLE_PREFERENCE_NONE; 692 public int userId; 693 public int blockedChannelCount; 694 public int channelCount; 695 public Map<String, NotificationsSentState> sentByChannel; 696 public NotificationsSentState sentByApp; 697 } 698 } 699