Android14 SystemUI NotificationShadeWindowView 加载显示过程

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

下面先看一下CentralSurfacesImplstart()方法, 这里代码很多,这里分析有关的代码 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 的参数。mCurrentStateNotificationShadeWindowState, 是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函数更新上面的修改

相关推荐
kerli2 小时前
Android 嵌套滑动设计思想
android·客户端
恣艺3 小时前
LeetCode 854:相似度为 K 的字符串
android·算法·leetcode
阿华的代码王国3 小时前
【Android】相对布局应用-登录界面
android·xml·java
用户207038619494 小时前
StateFlow与SharedFlow如何取舍?
android
QmDeve4 小时前
原生Android Java调用系统指纹识别方法
android
淹没4 小时前
🚀 告别复杂的HTTP模拟!HttpHook让Dart应用测试变得超简单
android·flutter·dart
HX4365 小时前
MP - List (not just list)
android·ios·全栈
安卓开发者6 小时前
Android WorkManager 详解:高效管理后台任务
android