Android12 源码分析 SystemUI 学习(3) --- Notification(1/2)

前言:如果说StatusBar是代码最多最重要的一个,那Notification是使用最多,最广泛的一个。这节主要讲通知栏相关的服务和组件控制器是如何构建的。

NotificationBar的创建过程

SystemUI学习篇(2)中,已经了解过StatusBar的启动过程等。其中也提到了,通知栏,导航栏以及锁屏界面都在StatusBar里有相关的启动操作。

关于NotificationBar的初始化

  • StatusBar中有一个启动过程 start() -> createAndAddWindows() -> makeStatusBarView() -> inflateStatusBarWindow(),在inflateStatusBarWindow()方法中就涉及到了通知栏的一些视图的创建。
  • 在inflateStatusBarWindow()中完成一些变量数据初始化后,回到makeStatusBarView()执行一些设置服务、视图填充和Controller绑定操作。然后继续在createAndAddWindows()中执行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是如何将通知显示,以及重要级是如何区分的。即通知的实现过程

Notification的关键API

NotificationChannels

什么是 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 设备的话还会创建画中画通知通道。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-情绪零碎-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值