Android14 SystemUI NotificationShadeWindowView 加载显示过程

Android14 SystemUI 启动过程中介绍到SystemUI启动最后会调用 CoreStartable 实现类中 start() 方法。通过源码可知NotificationShadeWindowView 的启动类是CentralSurfacesImpl实现了 CoreStartable 接口。

下面先看一下CentralSurfacesImpl的start()方法, 这里代码很多,这里分析有关的代码 frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java

scss 复制代码
public void start() {
    // 省略部分代码
    RegisterStatusBarResult result = null;
    try {
        result = mBarService.registerStatusBar(mCommandQueue);
    } catch (RemoteException ex) {
        ex.rethrowFromSystemServer();
    }

    createAndAddWindows(result);
    // 省略部分代码
}

@Override
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
    makeStatusBarView(result);
    mNotificationShadeWindowController.attach();
    mStatusBarWindowController.attach();
}

protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
    // 省略部分代码
    inflateStatusBarWindow();
    // 省略部分代码
}

private void inflateStatusBarWindow() {
    // 省略部分代码
    mCentralSurfacesComponent = mCentralSurfacesComponentFactory.create();
    mNotificationShadeWindowView = mCentralSurfacesComponent.getNotificationShadeWindowView();
    mNotificationShadeWindowViewController = mCentralSurfacesComponent
            .getNotificationShadeWindowViewController();
    mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
    // 省略部分代码
}

通过上面调用链可以知道最后调用到inflateStatusBarWindow()函数中, 通过mCentralSurfacesComponent.getNotificationShadeWindowView() 获取到NotificationShadeWindowView,看一下dagger中是怎么实现的 frameworks/base/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt

kotlin 复制代码
@Module
abstract class ShadeModule {

    @Binds
    @IntoMap
    @ClassKey(AuthRippleController::class)
    abstract fun bindAuthRippleController(controller: AuthRippleController): CoreStartable

    companion object {
        const val SHADE_HEADER = "large_screen_shade_header"

        @Provides
        @SysUISingleton
        // TODO(b/277762009): Do something similar to
        //  {@link StatusBarWindowModule.InternalWindowView} so that only
        //  {@link NotificationShadeWindowViewController} can inject this view.
        fun providesNotificationShadeWindowView(
            layoutInflater: LayoutInflater,
        ): NotificationShadeWindowView {
            return layoutInflater.inflate(R.layout.super_notification_shade, /* root= */ null)
                as NotificationShadeWindowView?
                ?: throw IllegalStateException(
                    "R.layout.super_notification_shade could not be properly inflated"
                )
        }
    }
    // 省略部分代码
}

然后将获取到的NotificationShadeWindowView交给NotificationShadeWindowControllerImpl 进行控制。在设置完NotificationShadeWindowView后就会执行mNotificationShadeWindowController.attach()函数将View 添加到屏幕上, 看一下具体实现 frameworks/base/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java

scss 复制代码
/**
 * Adds the notification shade view to the window manager.
 */
@Override
public void attach() {
    // Now that the notification shade encompasses the sliding panel and its
    // translucent backdrop, the entire thing is made TRANSLUCENT and is
    // hardware-accelerated.
    mLp = new LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.MATCH_PARENT,
            LayoutParams.TYPE_NOTIFICATION_SHADE,
            LayoutParams.FLAG_NOT_FOCUSABLE
                    | LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                    | LayoutParams.FLAG_SPLIT_TOUCH
                    | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                    | LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
            PixelFormat.TRANSLUCENT);
    mLp.token = new Binder();
    mLp.gravity = Gravity.TOP;
    mLp.setFitInsetsTypes(0 /* types */);
    mLp.setTitle("NotificationShade");
    mLp.packageName = mContext.getPackageName();
    mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
    mLp.privateFlags |= PRIVATE_FLAG_OPTIMIZE_MEASURE;

    // We use BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE here, however, there is special logic in
    // window manager which disables the transient show behavior.
    // TODO: Clean this up once that behavior moves into the Shell.
    mLp.privateFlags |= PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
    mLp.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;

    mWindowManager.addView(mNotificationShadeView, mLp);

    mLpChanged.copyFrom(mLp);
    onThemeChanged();

    // Make the state consistent with KeyguardViewMediator#setupLocked during initialization.
    if (mKeyguardViewMediator.isShowingAndNotOccluded()) {
        setKeyguardShowing(true);
    }
}
@Override
public void setNotificationShadeView(ViewGroup view) {
    mNotificationShadeView = view;
}

上面代码很清晰,把mNotificationShadeView通过mWindowManager添加到屏幕上

下面在看一个在NotificationShadeWindowControllerImpl中比较关键的函数apply函数,

scss 复制代码
private void apply(NotificationShadeWindowState state) {
    logState(state);
    applyKeyguardFlags(state);
    applyFocusableFlag(state);
    applyForceShowNavigationFlag(state);
    adjustScreenOrientation(state);
    applyVisibility(state);
    applyUserActivityTimeout(state);
    applyInputFeatures(state);
    applyFitsSystemWindows(state);
    applyModalFlag(state);
    applyBrightness(state);
    applyHasTopUi(state);
    applyNotTouchable(state);
    applyStatusBarColorSpaceAgnosticFlag(state);
    applyWindowLayoutParams();

    if (mHasTopUi != mHasTopUiChanged) {
        whitelistIpcs(() -> {
            try {
                mActivityManager.setHasTopUi(mHasTopUiChanged);
            } catch (RemoteException e) {
                Log.e(TAG, "Failed to call setHasTopUi", e);
            }
            mHasTopUi = mHasTopUiChanged;
        });
    }
    notifyStateChangedCallbacks();
}

这个函数在很多场景都会触发,更新一显示相关状态。哪里会调用这里不详细介绍,都在这个类中。以setKeyguardShowing函数为例介绍一下。

typescript 复制代码
@Override
public void setKeyguardShowing(boolean showing) {
    mCurrentState.keyguardShowing = showing;
    apply(mCurrentState);
}

到这里调用逻辑就更加清晰了, 当锁屏显示时,调用setKeyguardShowing函数, 在setKeyguardShowing中修改 mCurrentState.keyguardShowing状态, 最后调用apply函数更新View的参数, 在apply函数中调用的函数通过state变量来更改NotificationShadeWindowView 的参数。mCurrentState 是NotificationShadeWindowState, 是NotificationShadeWindowControllerImpl中维护的一个内部的状态管理变量。

最后介绍两个相关函数:

ini 复制代码
private void adjustScreenOrientation(NotificationShadeWindowState state) {
    Log.d(TAG, "adjustScreenOrientation: tree:" + Log.getStackTraceString(new Throwable()));
    if (state.bouncerShowing || state.isKeyguardShowingAndNotOccluded() || state.dozing) {
        if (mKeyguardStateController.isKeyguardScreenRotationAllowed()) {
            mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
        } else {
            mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
        }
    } else {
        mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
    }
}

这个是控制显示方向的,在这里可以设置NotificationShadeWindowView显示方向,比如:需要锁屏在任何场景下都是横屏显示,只需要修改一下这个地方的配置就可以

scss 复制代码
private void applyWindowLayoutParams() {
    if (mDeferWindowLayoutParams == 0 && mLp != null && mLp.copyFrom(mLpChanged) != 0) {
        mLogger.logApplyingWindowLayoutParams(mLp);
        Trace.beginSection("updateViewLayout");
        mWindowManager.updateViewLayout(mNotificationShadeView, mLp);
        Trace.endSection();
    }
}

最后通过applyWindowLayoutParams函数更新上面的修改

相关推荐
传奇开心果编程2 小时前
【Compose Multiplatform 跨端开发学与练】第8课 资源管理与主题
android·windows·学习·ios·kotlin·web·composer
传奇开心果编程3 小时前
【Compose Multiplatform 跨端开发学与练】第9课 测试与调试
android·学习·macos·ios·kotlin·web·composer
传奇开心果编程4 小时前
【Compose Multiplatform 跨端开发学与练】第4课 导航与路由
android·windows·学习·ui·ios·kotlin·composer
传奇开心果编程4 小时前
【Compose Multiplatform 跨端开发学与练】第6课 状态管理与架构
android·学习·ui·ios·架构·kotlin·composer
事圆则缓4 小时前
Android AOSP 定制常见概念:源码目录、系统镜像与刷机流程
android
传奇开心果编程4 小时前
【Compose Multiplatform 跨端开发学与练】第2课 Compose 基础语法
android·windows·学习·ui·ios·kotlin·composer
supabc1235 小时前
Celium:连接 Windows、Mac、Linux 与 Android,让远程访问和设备管理更简单
android·linux·windows·macos·远程访问·网络管理·celium
Dovis(誓平步青云)5 小时前
导览音频切换太快,旧讲解不能覆盖新展品
android·前端·javascript·ecmascript·音视频·宠物
五彩小白21 小时前
自回归和上下文
android
事圆则缓1 天前
Android 多渠道打包实战:Product Flavors、签名、AAB 与发包校验
android