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函数更新上面的修改

相关推荐
szhangbiao1 小时前
“开发板”类APP如果做屏幕适配
android
高林雨露2 小时前
RecyclerView中跳转到最后一条item并确保它在可视区域内显示
android
移动开发者1号4 小时前
ReLinker优化So库加载指南
android·kotlin
山野万里__4 小时前
C++与Java内存共享技术:跨平台与跨语言实现指南
android·java·c++·笔记
Huckings4 小时前
Android 性能问题
android
移动开发者1号5 小时前
剖析 Systrace:定位 UI 线程阻塞的终极指南
android·kotlin
移动开发者1号5 小时前
深入解析内存抖动:定位与修复实战(Kotlin版)
android·kotlin
whysqwhw5 小时前
OkHttp深度架构缺陷分析与革命性演进方案
android
Digitally7 小时前
如何将文件从 iPhone 传输到 Android(新指南)
android·ios·iphone
whysqwhw8 小时前
OkHttp深度架构缺陷分析与演进规划
android