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