赞
踩
前言:如果说StatusBar
是代码最多最重要的一个,那Notification
是使用最多,最广泛的一个。这节主要讲通知栏相关的服务和组件控制器是如何构建的。
在SystemUI学习篇(2)
中,已经了解过StatusBar
的启动过程等。其中也提到了,通知栏,导航栏以及锁屏界面都在StatusBar里有相关的启动操作。
关于NotificationBar
的初始化
StatusBar
中有一个启动过程 start() -> createAndAddWindows() -> makeStatusBarView() -> inflateStatusBarWindow(),在inflateStatusBarWindow()方法中就涉及到了通知栏的一些视图的创建。NotificationShadeWindowController
的attach()方法,以及start()方法中的setUpPresenter()。部分与通知栏相关代码:
public class StatusBar extends SystemUI implements /*代码省略*/ { @Override public void start() { //...代码省略... createAndAddWindows(result); //...代码省略... // Set up the initial notification state. This needs to happen before CommandQueue.disable() setUpPresenter(); } public void createAndAddWindows(@Nullable RegisterStatusBarResult result) { makeStatusBarView(result);//调用后续方法初始化 mNotificationShadeWindowController.attach();//初始化完成后执行 //...代码省略... } protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) { //...代码省略... inflateStatusBarWindow(); mNotificationShadeWindowViewController.setService(this, mNotificationShadeWindowController); mNotificationShadeWindowView.setOnTouchListener(getStatusBarWindowTouchListener()); mWallpaperController.setRootView(mNotificationShadeWindowView); // TODO: Deal with the ugliness that comes from having some of the statusbar broken out // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot. NotificationListContainer notifListContainer = mStackScrollerController.getNotificationListContainer(); mNotificationLogger.setUpWithContainer(notifListContainer); mNotificationIconAreaController.setupShelf(mNotificationShelfController); mPanelExpansionStateManager.addExpansionListener(mWakeUpCoordinator); mUserSwitcherController.init(mNotificationShadeWindowView); //...代码省略... mStatusBarWindowController.getFragmentHostManager() .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> { //...代码省略 // Ensure we re-propagate panel expansion values to the panel controller and // any listeners it may have, such as PanelBar. This will also ensure we // re-display the notification panel if necessary (for example, if // a heads-up notification was being displayed and should continue being // displayed). mNotificationPanelViewController.updatePanelExpansionAndVisibility(); }) ./*代码省略*/ //...代码省略... mNotificationPanelViewController.initDependencies( this, this::makeExpandedInvisible, mNotificationShelfController); } private void inflateStatusBarWindow() { mStatusBarComponent = mStatusBarComponentFactory.create(); mFragmentService.addFragmentInstantiationProvider(mStatusBarComponent); mNotificationShadeWindowView = mStatusBarComponent.getNotificationShadeWindowView(); mNotificationShadeWindowViewController = mStatusBarComponent .getNotificationShadeWindowViewController(); mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView); mNotificationShadeWindowViewController.setupExpandedStatusBar(); mNotificationPanelViewController = mStatusBarComponent.getNotificationPanelViewController(); //...代码省略... } }
代码虽然多,但是方法名都有对应相应方法的作用。NotificationShadeWindowView
通知栏的阴影块背景视图、NotificationPanelView
通知面板视图,用来填充各个通知。
private void setUpPresenter() { // Set up the initial notification state. mActivityLaunchAnimator.setCallback(mKeyguardHandler); mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider( mNotificationShadeWindowViewController, mStackScrollerController.getNotificationListContainer(), mHeadsUpManager );//动画相关 // TODO: inject this. mPresenter = new StatusBarNotificationPresenter( mContext, mNotificationPanelViewController, mHeadsUpManager, mNotificationShadeWindowView, mStackScrollerController, mDozeScrimController, mScrimController, mNotificationShadeWindowController, mDynamicPrivacyController, mKeyguardStateController, mKeyguardIndicationController, mFeatureFlags, this /* statusBar */, mShadeController, mLockscreenShadeTransitionController, mCommandQueue, mViewHierarchyManager, mLockscreenUserManager, mStatusBarStateController, mNotifShadeEventSource, mEntryManager, mMediaManager, mGutsManager, mKeyguardUpdateMonitor, mLockscreenGestureLogger, mInitController, mNotificationInterruptStateProvider, mRemoteInputManager, mConfigurationController); mNotificationShelfController.setOnActivatedListener(mPresenter); mRemoteInputManager.addControllerCallback(mNotificationShadeWindowController); mNotificationActivityStarter = mStatusBarNotificationActivityStarterBuilder .setStatusBar(this) .setActivityLaunchAnimator(mActivityLaunchAnimator) .setNotificationAnimatorControllerProvider(mNotificationAnimationProvider) .setNotificationPresenter(mPresenter) .setNotificationPanelViewController(mNotificationPanelViewController) .build(); mStackScrollerController.setNotificationActivityStarter(mNotificationActivityStarter); mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter); mNotificationsController.initialize( this, mBubblesOptional, mPresenter, mStackScrollerController.getNotificationListContainer(), mNotificationActivityStarter, mPresenter); }
这个方法作用就是完成NotificationsController
的initialize()方法。
NotificationShelf
是通知栏主体框架。
执行完这些后,Notification相关的View的
Controller
都被启动起来,在Controller里有各个视图填充相应Framelayout的逻辑。这一块的代码多且杂,就不做深究。下节就分析一下,app或者系统发送通知后,SystemUI是如何将通知显示,以及重要级是如何区分的。即通知的实现过程
什么是 NotificationChannel
?在 Android 8.0
以及之后使用通知时必须指定 NotificationChannel
,并且在指定NotificationChannel
时需要设置通知的重要程度等级,以便用户可以在设置里根据个人喜好操作不同的NotificationChannel
。
源码路径: frameworks/base/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
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)), batteryChannel, hint )); 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 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); return screenshotChannel; } @Override public void start() { createAll(mContext); } private static boolean isTv(Context context) { PackageManager packageManager = context.getPackageManager(); return packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK); } }
NotificationChannels
扩展自 SystemUI
并重写了 start
方法,它执行了 createAll
方法,创建了通知通道有 batteryChannel
(电池)、alerts
(提醒)、storage
(存储空间)、screenshot
(屏幕截图)、hint
(提示)、general
(常规消息)。
此外,如果是 TV
设备的话还会创建画中画通知通道。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。