0 x00 to introduce

You’ve already seen what SystemUI components are used for, and if I remember correctly, there are about 20 of them. Today we’ll take a look at the key apis for the notification components that I think are the most important. Understanding them is an entry point to further understand the implementation logic of Notification.

As a reminder, the notification related components we introduced earlier are

Com. Android. Systemui. Util. NotificationChannels used to handle notification logic com. Android. Systemui. Status. The phone. The StatusBar status bar, It also includes notification bars and other important UI interactions, such as keyboard locks. This also listens for notificationsCopy the code

There are other components such as PowerUI that also send notifications, but we are more concerned with receiving notifications and handling the logic associated with notifications.

We’ll talk more about how notifications are handled in these two classes. Right

This article is based on Android 10 source code

0x01 NotificationChannels

The NotificationChannels class is relatively simple

public class NotificationChannels extends SystemUI { // ... Public static void createAll(Context Context) {final NotificationManager nm = context.getSystemService(NotificationManager.class); final NotificationChannel batteryChannel = new NotificationChannel(BATTERY, context.getString(R.string.notification_channel_battery), NotificationManager.IMPORTANCE_MAX); final String soundPath = Settings.Global.getString(context.getContentResolver(), Settings.Global.LOW_BATTERY_SOUND); batteryChannel.setSound(Uri.parse("file://" + soundPath), new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .setUsage(AudioAttributes.USAGE_NOTIFICATION_EVENT) .build()); batteryChannel.setBlockable(true); final NotificationChannel alerts = new NotificationChannel( ALERTS, context.getString(R.string.notification_channel_alerts), NotificationManager.IMPORTANCE_HIGH); final NotificationChannel general = new NotificationChannel( GENERAL, context.getString(R.string.notification_channel_general), NotificationManager.IMPORTANCE_MIN); final NotificationChannel storage = new NotificationChannel( STORAGE, context.getString(R.string.notification_channel_storage), isTv(context) ? NotificationManager.IMPORTANCE_DEFAULT : NotificationManager.IMPORTANCE_LOW); final NotificationChannel hint = new NotificationChannel( HINTS, context.getString(R.string.notification_channel_hints), NotificationManager.IMPORTANCE_DEFAULT); // No need to bypass DND. nm.createNotificationChannels(Arrays.asList( alerts, general, storage, createScreenshotChannel( context.getString(R.string.notification_channel_screenshot), nm.getNotificationChannel(SCREENSHOTS_LEGACY)), batteryChannel, hint )); // Delete older SS channel if present. // Screenshots promoted to heads-up in P, this cleans up the lower priority channel from O. // This line can be deleted in Q. nm.deleteNotificationChannel(SCREENSHOTS_LEGACY); if (isTv(context)) { // TV specific notification channel for TV PIP controls. // Importance should be {@link NotificationManager#IMPORTANCE_MAX} to have the highest // priority, so it can be shown in all times. nm.createNotificationChannel(new NotificationChannel( TVPIP, context.getString(R.string.notification_channel_tv_pip), NotificationManager.IMPORTANCE_MAX)); } } /** * Set up screenshot channel, respecting any previously committed user settings on legacy * channel. * @return */ @VisibleForTesting static NotificationChannel createScreenshotChannel( String name, NotificationChannel legacySS) { NotificationChannel screenshotChannel = new NotificationChannel(SCREENSHOTS_HEADSUP, name, NotificationManager.IMPORTANCE_HIGH); // pop on screen screenshotChannel.setSound(null, // silent new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build()); screenshotChannel.setBlockable(true); if (legacySS ! = null) { // Respect any user modified fields from the old channel. int userlock = legacySS.getUserLockedFields(); if ((userlock & NotificationChannel.USER_LOCKED_IMPORTANCE) ! = 0) { screenshotChannel.setImportance(legacySS.getImportance()); } if ((userlock & NotificationChannel.USER_LOCKED_SOUND) ! = 0) { screenshotChannel.setSound(legacySS.getSound(), legacySS.getAudioAttributes()); } if ((userlock & NotificationChannel.USER_LOCKED_VIBRATION) ! = 0) { screenshotChannel.setVibrationPattern(legacySS.getVibrationPattern()); } if ((userlock & NotificationChannel.USER_LOCKED_LIGHTS) ! = 0) { screenshotChannel.setLightColor(legacySS.getLightColor()); } // skip show_badge, irrelevant for system channel } return screenshotChannel; } @Override public void start() { createAll(mContext); } private static boolean isTv(Context context) { PackageManager packageManager = context.getPackageManager(); return packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK); }}Copy the code

NotificationChannels extends from SystemUI and overwrites the start method, which executes createAll, Create notification channels such as batteryChannel, Alerts, storage, screenshots, hints, and General. In addition, the picture-in-picture notification channel is created for TV devices.

So what is NotificationChannel? NotificationChannel must be specified when using notifications in Android 8.0 to avoid disturbing users excessively. Users have the ability to turn off certain notifications without affecting other notifications that users care about. For example, there are many types of notifications in an application, but the user only cares about one of them, so the user can configure it through Settings.

For more information, please refer to the official documentation

0x02 StatusBar

In the user interface, the StatusBar is mostly always displayed at the top of the screen (full screen applications are hidden). It is a very core feature of SystemUI, with nearly 5,000 lines of code, and its importance can be seen in another way. Let’s look at the constructor first. It has so many arguments that I can barely read them. So discard the constructor entry first. Then we know that it also inherits from the SystemUI class, so we can look at its start method and see what initialization it does.

But the start method is not easy, with nearly 190 lines of code. But let’s focus on the logic related to Notification for now, and the streamlined code looks like this:

public void start() { // ... // create Windows createAndAddWindows(result); // Make sure we always have the most current wallpaper info. IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED); mContext.registerReceiverAsUser(mWallpaperChangedReceiver, UserHandle.ALL, wallpaperChangedFilter, null /* broadcastPermission */, null /* scheduler */); mWallpaperChangedReceiver.onReceive(mContext, null); // Set up the initial notification state. This needs to happen before commandqueue.disable () setUpPresenter(); / / set the systemui visibility (navigationbar and statusbar) setSystemUiVisibility (mDisplayId, result mSystemUiVisibility, result.mFullscreenStackSysUiVisibility, result.mDockedStackSysUiVisibility, 0xffffffff, result.mFullscreenStackBounds, result.mDockedStackBounds, result.mNavbarColorManagedByIme); // StatusBarManagerService has a back up of IME token and it's restored here. setImeWindowStatus(mDisplayId, result.mImeToken, result.mImeWindowVis, result.mImeBackDisposition, result.mShowImeSwitcher); / /... // omit code}Copy the code

Follow up to the setUpPresenter method

private void setUpPresenter() { // Set up the initial notification state. mActivityLaunchAnimator = new ActivityLaunchAnimator( mStatusBarWindow, this, mNotificationPanel, (NotificationListContainer) mStackScroller); final NotificationRowBinderImpl rowBinder = new NotificationRowBinderImpl( mContext, SystemUIFactory.getInstance().provideAllowNotificationLongPress()); / / / / mNotificationPanel is a notification panel mStackScroller is NotificationStackScrollLayout instance, It is the notification list // mStatusBarWindow window mPresenter = new integrating StatusBar and NotificationPanel StatusBarNotificationPresenter(mContext, mNotificationPanel, mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController, mScrimController, mActivityLaunchAnimator, mStatusBarKeyguardViewManager, mNotificationAlertingManager, rowBinder); // Controller of the notification list, It references (in fact, the internal hold NotificationListContainer NotificationStackScrollLayout) mNotificationListController = new NotificationListController( mEntryManager, (NotificationListContainer) mStackScroller, mForegroundServiceController, mDeviceProvisionedController); mAppOpsController.addCallback(APP_OPS, this); mNotificationShelf.setOnActivatedListener(mPresenter); mRemoteInputManager.getController().addCallback(mStatusBarWindowController); final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback = (StatusBarRemoteInputCallback) Dependency.get( NotificationRemoteInputManager.Callback.class); mShadeController = Dependency.get(ShadeController.class); final ActivityStarter activityStarter = Dependency.get(ActivityStarter.class); // For handling notifications related interactions, For example, click on the notification, the jump is a an application such as interactive mNotificationActivityStarter = new StatusBarNotificationActivityStarter (mContext mCommandQueue, mAssistManager, mNotificationPanel, mPresenter, mEntryManager, mHeadsUpManager, activityStarter, mActivityLaunchAnimator, mBarService, mStatusBarStateController, mKeyguardManager, mDreamManager, mRemoteInputManager, mStatusBarRemoteInputCallback, mGroupManager, mLockscreenUserManager, mShadeController, mKeyguardMonitor, mNotificationInterruptionStateProvider, mMetricsLogger, new LockPatternUtils(mContext), Dependency.get(MAIN_HANDLER), Dependency.get(BG_HANDLER), mActivityIntentHelper, mBubbleController); mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter); mEntryManager.setRowBinder(rowBinder); rowBinder.setNotificationClicker(new NotificationClicker( this, Dependency.get(BubbleController.class), mNotificationActivityStarter)); mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager); mNotificationListController.bind(); }Copy the code

The setUpPresenter method creates many notification related objects and establishes relationships between them. And that includes NotificationListContainer, NotificationListController, NotificationShelf, NotificationPanel and StatusBarNotificationActivitySt Arter, etc. These are key apis for handling notification logic. If the impression of these classes is vague at the beginning, we do not know where to start. When we understand the purpose of these classes and their user interface in the operating system, we will have a more intuitive understanding, and then we will focus on exploring the internal logic of these classes.

0 x03 reference

  • About the notification channel developer.android.com/training/no official document…
  • Read the source code online at cs.android.com/