Android14 SystemUI 启动过程中介绍到SystemUI启动最后会调用 CoreStartable
实现类中 start()
方法。通过源码可知NotificationShadeWindowView
的启动类是CentralSurfacesImpl
实现了 CoreStartable
接口。
下面先看一下CentralSurfacesImpl
的start()
方法, 这里代码很多,这里分析有关的代码
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
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
@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
/**
* 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
函数,
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
函数为例介绍一下。
@Override
public void setKeyguardShowing(boolean showing) {
mCurrentState.keyguardShowing = showing;
apply(mCurrentState);
}
到这里调用逻辑就更加清晰了, 当锁屏显示时,调用setKeyguardShowing
函数, 在setKeyguardShowing
中修改 mCurrentState.keyguardShowing
状态, 最后调用apply
函数更新View的参数, 在apply
函数中调用的函数通过state
变量来更改NotificationShadeWindowView
的参数。mCurrentState
是NotificationShadeWindowState
, 是NotificationShadeWindowControllerImpl
中维护的一个内部的状态管理变量。
最后介绍两个相关函数:
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
显示方向,比如:需要锁屏在任何场景下都是横屏显示,只需要修改一下这个地方的配置就可以
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
函数更新上面的修改