第一句子网 - 唯美句子、句子迷、好句子大全
第一句子网 > Android 12系统源码_SystemUI(三)车载状态栏CarStatusBar的创建流程

Android 12系统源码_SystemUI(三)车载状态栏CarStatusBar的创建流程

时间:2022-11-12 20:41:21

相关推荐

Android 12系统源码_SystemUI(三)车载状态栏CarStatusBar的创建流程

前言

上一篇文章我们具体分析了Android12原生状态栏类StatusBar构建状态栏视图以及将状态栏添加到Window的过程,也分析了状态栏视图的整个布局结构;早期的状态栏和导航栏对于手机设备来说那是相当重要的,但是随着手机不断的推陈出新,状态栏和导航栏对于手机的重要性在逐渐降低,特别是在快捷手势出现之后,导航栏几乎变得可有可无。但是对于当前如火如荼的商用车载系统来说,状态栏和导航栏却几乎是必备的,谷歌自然也意识到了这点,特意在Android12的原生代码中提供了一个包含有状态栏、导航栏、消息中心等SystemUI组件的项目CarSystemUI,而它正是本篇文章我们所要分析的。

一、车载SystemUI和原生SystemUI的关系

1)两者的位置不同

原生SystemUI项目位于/frameworks/base/package/SystemUI目录,车载SystemUI项目位于/packages/apps/Car/SystemUI目录

2)原生SystemUI是车载SystemUI的基础

原生SystemUI可以独立编译成SystemUI.apk文件,车载SystemUI无法独立编译,必须在原生SystemUI的基础上才能编译出CarSystemUI.apk文件;通过阅读CarSystemUI的Android.bp文件可以发现,CarSystemUI在编译的时候,会将原生的SystemUI以静态库的方式引入。

packages/apps/Car/SystemUI/Android.bp

android_library {name: "CarSystemUI-core",srcs: ["src/**/*.java","src/**/I*.aidl",],resource_dirs: ["res-keyguard","res",],static_libs: ["SystemUI-core","CarNotificationLib","SystemUIPluginLib","SystemUISharedLib","SettingsLib","car-admin-ui-lib","car-ui-lib","android.car.userlib","car-qc-lib","androidx.legacy_legacy-support-v4","androidx.recyclerview_recyclerview","androidx.preference_preference","androidx.appcompat_appcompat","androidx.mediarouter_mediarouter","androidx.palette_palette","androidx.legacy_legacy-preference-v14","androidx.leanback_leanback","androidx.slice_slice-core","androidx.slice_slice-view","androidx.slice_slice-builders","androidx.arch.core_core-runtime","androidx.lifecycle_lifecycle-extensions","SystemUI-tags","SystemUI-proto","dagger2","//external/kotlinc:kotlin-annotations",],libs: ["android.car",],manifest: "AndroidManifest.xml",plugins: ["dagger2-compiler"],}android_app {name: "CarSystemUI",static_libs: ["CarSystemUI-core",],export_package_resources: true,libs: ["android.car",],resource_dirs: [],overrides: ["SystemUI",],platform_apis: true,system_ext_specific: true,certificate: "platform",privileged: true,optimize: {proguard_flags_files: ["proguard.flags",],},dxflags: ["--multi-dex"],aaptflags: ["--extra-packages","com.android.keyguard",],kotlincflags: ["-Xjvm-default=enable"],plugins: ["dagger2-compiler"],required: ["privapp_whitelist_com.android.systemui", "allowed_privapp_com.android.carsystemui"],}

3)两者包含的组件不同

车载SystemUI去掉了原生SystemUI中的一部分组件,又新增了一部分自定义组件。

二、CarSystemUIFactory创建CarSystemUI模块所需的各种SystemUI组件

1、在Android 12系统源码_SystemUI(一)SystemUI的启动流程这篇文章中我们有讲过,SystemUIApplication的onCreate方法会回调对象SystemUIAppComponentFactory为自己设置的回调方法。

frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java

public class SystemUIAppComponentFactory extends AppComponentFactory {@NonNull@Overridepublic Application instantiateApplicationCompat(@NonNull ClassLoader cl, @NonNull String className)throws InstantiationException, IllegalAccessException, ClassNotFoundException {Application app = super.instantiateApplicationCompat(cl, className);if (app instanceof ContextInitializer) {//为Application设置回调方法((ContextInitializer) app).setContextAvailableCallback(context -> {SystemUIFactory.createFromConfig(context);SystemUIFactory.getInstance().getSysUIComponent().inject(SystemUIAppComponentFactory.this);});}return app;}}

2、该回调方法会调用SystemUIFactory的createFromConfig方法。

framework/base/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java

public class SystemUIFactory {static SystemUIFactory mFactory;public static <T extends SystemUIFactory> T getInstance() {return (T) mFactory;}public static void createFromConfig(Context context) {createFromConfig(context, false);}@VisibleForTestingpublic static void createFromConfig(Context context, boolean fromTest) {if (mFactory != null) {return;}//获取config_systemUIFactoryComponent所对应的字符串final String clsName = context.getString(R.string.config_systemUIFactoryComponent);if (clsName == null || clsName.length() == 0) {throw new RuntimeException("No SystemUIFactory component configured");}try {Class<?> cls = null;cls = context.getClassLoader().loadClass(clsName);//通过反射创建SystemUIFactory实例并赋值给静态属性mFactory mFactory = (SystemUIFactory) cls.newInstance();//执行init方法mFactory.init(context, fromTest);} catch (Throwable t) {Log.w(TAG, "Error creating SystemUIFactory component: " + clsName, t);throw new RuntimeException(t);}}}

frameworks/base/packages/SystemUI/res/values/config.xml

<string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.SystemUIFactory</string>

SystemUIFactory的createFromConfig最终是获取R.string.config_systemUIFactoryComponent所对应的字符串,在原生SystemUI项目中这个字符串就是SystemUIFactory自己的类名,这样通过反射创建的对象自然就是SystemUIFactory本身,后续调用SystemUIFactory的getInstance方法所获取的对象自然也是SystemUIFactory对象实例。

3、然而车载SystemUI项目修改了这个字段,让该字段变成了CarSystemUIFactory的类名。

packages/apps/Car/SystemUI/res/values/config.xml

<string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.CarSystemUIFactory</string>

这样SystemUIFactory后续调用SystemUIFactory的getInstance方法所获取的对象就变成了CarSystemUIFactory。

4、SystemUI组件最初都是在SystemUIApplication的startServicesIfNeeded方法中被创建并初始化的。

public void startServicesIfNeeded() {String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names);}

SystemUIApplication的startServicesIfNeeded方法调用SystemUIFactory.getInstance()方法最终得到了CarSystemUIFactory对象,接着会调用CarSystemUIFactory的getSystemUIServiceComponents方法获取需要创建和初始化的SystemUI组件对象。

5、CarSystemUIFactory的getSystemUIServiceComponents方法如下所示。

public class CarSystemUIFactory extends SystemUIFactory {@Overridepublic String[] getSystemUIServiceComponents(Resources resources) {Set<String> names = new HashSet<>();//获取SystemUI项目配置的所有SystemUI组件for (String s : super.getSystemUIServiceComponents(resources)) {names.add(s);}//CarSystemUI模块需要移除的SystemUI组件for (String s : resources.getStringArray(R.array.config_systemUIServiceComponentsExclude)) {names.remove(s);}//CarSystemUI模块需要新增的SystemUI组件for (String s : resources.getStringArray(R.array.config_systemUIServiceComponentsInclude)) {names.add(s);}//在经过移除、新增两步后,会将新的组件数组返回String[] finalNames = new String[names.size()];names.toArray(finalNames);return finalNames;}}

1)获取SystemUI项目配置的所有SystemUI组件。

<string-array name="config_systemUIServiceComponents" translatable="false"><item>com.android.systemui.util.NotificationChannels</item><!--通知--><item>com.android.systemui.keyguard.KeyguardViewMediator</item><!--键盘锁状态--><item>com.android.systemui.recents.Recents</item><!--任务列表--><item>com.android.systemui.volume.VolumeUI</item><!--监听音量,并决定是否显示音量的对话框--><item>com.android.systemui.statusbar.phone.StatusBar</item><!--状态栏--><item>com.android.systemui.usb.StorageNotification</item><!--监听 USB 连接状态并发送通知进行提示--><item>com.android.systemui.power.PowerUI</item><!--监听电量状态并在低电量时发送通知--><item>com.android.systemui.media.RingtonePlayer</item><!--用于播放铃声--><item>com.android.systemui.keyboard.KeyboardUI</item><!--键盘锁 UI--><item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item><!--快捷分发器--><item>@string/config_systemUIVendorServiceComponent</item><!--这里可以定义厂商定制的组件--><item>com.android.systemui.util.leak.GarbageMonitor$Service</item><!--用于监控内存泄漏的服务--><item>com.android.systemui.LatencyTester</item><!--仅在 debug 环境执行,用于监听系统测试延迟的模拟动作--><item>com.android.systemui.globalactions.GlobalActionsComponent</item><!--用于显示全局对话框(例如长按电源按键)--><item>com.android.systemui.ScreenDecorations</item><!--处理页面中的显示的形状(如圆角)--><item>com.android.systemui.biometrics.AuthController</item><!--身份验证--><item>com.android.systemui.SliceBroadcastRelayHandler</item><!--允许打开设置App--><item>com.android.systemui.statusbar.notification.InstantAppNotifier</item><!--时应用程序通知--><item>com.android.systemui.theme.ThemeOverlayController</item><!--主题--><item>com.android.systemui.accessibility.WindowMagnification</item><!--放大器--><item>com.android.systemui.accessibility.SystemActions</item><item>com.android.systemui.toast.ToastUI</item><item>com.android.systemui.wmshell.WMShell</item></string-array>

2)CarSystemUI模块需要移除的SystemUI组件。

<string-array name="config_systemUIServiceComponentsExclude" translatable="false"><item>com.android.systemui.recents.Recents</item><item>com.android.systemui.volume.VolumeUI</item><item>com.android.systemui.statusbar.phone.StatusBar</item><!--原生状态栏在这里被移除--><item>com.android.systemui.keyboard.KeyboardUI</item><item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item><item>com.android.systemui.LatencyTester</item><item>com.android.systemui.globalactions.GlobalActionsComponent</item><item>com.android.systemui.SliceBroadcastRelayHandler</item><item>com.android.systemui.statusbar.notification.InstantAppNotifier</item><item>com.android.systemui.accessibility.WindowMagnification</item><item>com.android.systemui.accessibility.SystemActions</item><item>com.android.systemui.toast.ToastUI</item></string-array>

3)CarSystemUI模块需要新增的SystemUI组件。

<string-array name="config_systemUIServiceComponentsInclude" translatable="false"><item>com.android.systemui.car.systembar.CarSystemBar</item><!--车载导航栏在这里被添加--><item>com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier</item><item>com.android.systemui.car.window.SystemUIOverlayWindowManager</item><item>com.android.systemui.car.toast.CarToastUI</item><item>com.android.systemui.car.volume.VolumeUI</item><item>com.android.systemui.car.cluster.ClusterDisplayController</item></string-array>

到这里我们终于看到了车载导航栏CarSystemBar组件,前面的文章我们有介绍,SystemUIApplication的startServicesIfNeeded会创建所有的SystemUI组件对象并调用它们的start方法。

三、从CarSystemBar的start方法到调用buildNavBarWindows方法构建NavigationBarFrame视图:

1、CarSystemBar的构造方法如下所示:

package/app/Car/SystemUI/src/com/android/systemui/car/systembar/CarSystemBar.java

public class CarSystemBar extends SystemUI implements CommandQueue.Callbacks {...代码省略...@Injectpublic CarSystemBar(Context context,CarSystemBarController carSystemBarController,// TODO(b/156052638): Should not need to inject LightBarControllerLightBarController lightBarController,DarkIconDispatcher darkIconDispatcher,WindowManager windowManager,CarDeviceProvisionedController deviceProvisionedController,CommandQueue commandQueue,AutoHideController autoHideController,ButtonSelectionStateListener buttonSelectionStateListener,@Main DelayableExecutor mainExecutor,@UiBackground Executor uiBgExecutor,IStatusBarService barService,Lazy<KeyguardStateController> keyguardStateControllerLazy,Lazy<PhoneStatusBarPolicy> iconPolicyLazy,StatusBarSignalPolicy signalPolicy,HvacController hvacController,SystemBarConfigs systemBarConfigs) {super(context);mCarSystemBarController = carSystemBarController;mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher;mWindowManager = windowManager;mCarDeviceProvisionedController = deviceProvisionedController;mCommandQueue = commandQueue;mAutoHideController = autoHideController;mButtonSelectionStateListener = buttonSelectionStateListener;mExecutor = mainExecutor;mUiBgExecutor = uiBgExecutor;mBarService = barService;mKeyguardStateControllerLazy = keyguardStateControllerLazy;mIconPolicyLazy = iconPolicyLazy;mHvacController = hvacController;mSystemBarConfigs = systemBarConfigs;mSignalPolicy = signalPolicy;mDisplayId = context.getDisplayId();}...代码省略...}

dagger2框架会通过CarSystemBar的构造方法上的注解@Inject创建CarSystemBar实例对象,并会为CarSystemBar构造方法中的所有参数赋值,紧接着CarSystemBar的start方法会被调用。

2、start方法会调用一个关键方法createSystemBar,该方法会构建状态栏视图和导航栏视图。

public class CarSystemBar extends SystemUI implements CommandQueue.Callbacks {...代码省略...public void start() {...代码省略...createSystemBar(result);...代码省略...}...代码省略...}

3、createSystemBar方法如下所示。

public class CarSystemBar extends SystemUI implements CommandQueue.Callbacks {...代码省略...private void createSystemBar(RegisterStatusBarResult result) {buildNavBarWindows();//构建视图对象容器buildNavBarContent();attachNavBarWindows();...代码省略...}...代码省略...}

createSystemBar首先调用buildNavBarWindows构建顶部栏、底部栏、左侧栏、右侧栏这四种导航栏视图对象,然后再调用buildNavBarContent构建每种导航栏所对应的具体视图内容,最后会调用attachNavBarWindows将状态栏和导航栏视图添加到Window中。

4、先来看下buildNavBarWindows方法:

public class CarSystemBar extends SystemUI implements CommandQueue.Callbacks {private final CarSystemBarController mCarSystemBarController;//车载系统栏控制器...代码省略...//视图private ViewGroup mTopSystemBarWindow;//顶部栏视图容器private ViewGroup mBottomSystemBarWindow;//底部栏视图容器private ViewGroup mLeftSystemBarWindow;//左侧栏视图容器private ViewGroup mRightSystemBarWindow;//右侧栏视图容器...代码省略...private void buildNavBarWindows() {mTopSystemBarWindow = mCarSystemBarController.getTopWindow();//获取顶部栏视图容器mBottomSystemBarWindow = mCarSystemBarController.getBottomWindow();//获取底部栏视图容器mLeftSystemBarWindow = mCarSystemBarController.getLeftWindow();//获取左侧栏视图容器mRightSystemBarWindow = mCarSystemBarController.getRightWindow();//获取右侧栏视图容器}}

buildNavBarWindows方法会调用CarSystemBarController的get_Window方法对CarSystemBar中存在的对四个视图对象视图容器mTopSystemBarWindow、mBottomSystemBarWindow、mLeftSystemBarWindow、mRightSystemBarWindow进行赋值。

5、CarSystemBarController关于get_Window的方法如下:

package/app/Car/SystemUI/src/com/android/systemui/car/systembar/CarSystemBarController.java

public class CarSystemBarController {...代码省略...private final CarSystemBarViewFactory mCarSystemBarViewFactory;...代码省略...@Nullablepublic ViewGroup getTopWindow() {return mShowTop ? mCarSystemBarViewFactory.getTopWindow() : null;}@Nullablepublic ViewGroup getBottomWindow() {return mShowBottom ? mCarSystemBarViewFactory.getBottomWindow() : null;}@Nullablepublic ViewGroup getLeftWindow() {return mShowLeft ? mCarSystemBarViewFactory.getLeftWindow() : null;}@Nullablepublic ViewGroup getRightWindow() {return mShowRight ? mCarSystemBarViewFactory.getRightWindow() : null;}...代码省略...}

可见CarSystemBarController的get_Window方法会进一步调用CarSystemBarViewFactory的get_Window方法。

6、CarSystemBarViewFactory关于get_Window的关键代码如下所示:

package/app/Car/SystemUI/src/com/android/systemui/car/systembar/CarSystemBarViewFactory.java

@SysUISingletonpublic class CarSystemBarViewFactory {...代码省略...private final ArrayMap<Type, ViewGroup> mCachedContainerMap = new ArrayMap<>();...代码省略...public CarSystemBarView getTopBar(boolean isSetUp) {return getBar(isSetUp, Type.TOP, Type.TOP_UNPROVISIONED);}public CarSystemBarView getBottomBar(boolean isSetUp) {return getBar(isSetUp, Type.BOTTOM, Type.BOTTOM_UNPROVISIONED);}public CarSystemBarView getLeftBar(boolean isSetUp) {return getBar(isSetUp, Type.LEFT, Type.LEFT_UNPROVISIONED);}public CarSystemBarView getRightBar(boolean isSetUp) {return getBar(isSetUp, Type.RIGHT, Type.RIGHT_UNPROVISIONED);}private ViewGroup getWindowCached(Type type) {if (mCachedContainerMap.containsKey(type)) {return mCachedContainerMap.get(type);}ViewGroup window = (ViewGroup) View.inflate(mContext,R.layout.navigation_bar_window, /* root= */ null);mCachedContainerMap.put(type, window);return mCachedContainerMap.get(type);}...代码省略...

CarSystemBarViewFactory的get_Bar方法会进一步调用getWindowCached方法,而getWindowCached会先判断类型为ArrayMap<Type, ViewGroup>的缓存mCachedContainerMap中是否存在对应的CarSystemBarView视图,如果存在直接返回;否则会调用View的inflate方法将R.layout.navigation_bar_window布局文件构建成相应的视图对象,然后存储到mCachedContainerMap中,并返回该视图对象。

7、来看下navigation_bar_window.xml这个布局文件。

framework/base/package/SystemUI/res/layout/navigation_bar_window.xml

<com.android.systemui.navigationbar.NavigationBarFramexmlns:android="/apk/res/android"xmlns:systemui="/apk/res-auto"android:id="@+id/navigation_bar_frame"android:theme="@style/Theme.SystemUI"android:layout_height="match_parent"android:layout_width="match_parent"></com.android.systemui.navigationbar.NavigationBarFrame>

就是一个很普通的布局文件,只有一个自定义控件NavigationBarFrame。

framework/base/package/SystemUI/src/com/android/systemui/navigationbar/NavigationBarFrame.java

public class NavigationBarFrame extends FrameLayout {private DeadZone mDeadZone = null;public NavigationBarFrame(@NonNull Context context) {super(context);}public NavigationBarFrame(Context context, AttributeSet attrs) {super(context, attrs);}public NavigationBarFrame(@NonNull Context context, @Nullable AttributeSet attrs,@AttrRes int defStyleAttr) {super(context, attrs, defStyleAttr);}public void setDeadZone(@NonNull DeadZone deadZone) {mDeadZone = deadZone;}@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {if (event.getAction() == ACTION_OUTSIDE) {if (mDeadZone != null) {return mDeadZone.onTouchEvent(event);}}return super.dispatchTouchEvent(event);}}

NavigationBarFrame就是一个继承自FrameLayout的自定义控件,单词Frame有边框的意思,这也进一步说明NavigationBarFrame这个控件就是NavigationBar的边框的意思,在前面第4步提到的CarSystemBar调用buildNavBarWindows方法所构建的四个视图容器对象mTopSystemBarWindow、mBottomSystemBarWindow、mLeftSystemBarWindow、mRightSystemBarWindow其实就对应了NavigationBarFrame这个这个对象。

四、构建类型为NavigationBarFrame的顶部栏视图容器、底部栏视图容器、左侧栏视图容器、右侧栏视图容器所需要填充的视图内容。

1、CarSystemBar的createSystemBar方法在调用buildNavBarWindows方法构建NavigationBarFrame视图容器之后,会继续调用buildNavBarContent方法来为视图容器构建具体的视图内容。

public class CarSystemBar extends SystemUI implements CommandQueue.Callbacks {...代码省略...private void createSystemBar(RegisterStatusBarResult result) {buildNavBarWindows();//构建视图对象容器buildNavBarContent();//构建视图对象内容attachNavBarWindows();...代码省略...}...代码省略...}

2、buildNavBarContent方法如下所示:

public class CarSystemBar extends SystemUI implements ...代码省略...private boolean mDeviceIsSetUpForUser = true;private boolean mIsUserSetupInProgress = false;...代码省略...private void buildNavBarContent() {mTopSystemBarView = mCarSystemBarController.getTopBar(isDeviceSetupForUser());if (mTopSystemBarView != null) {mSystemBarConfigs.insetSystemBar(SystemBarConfigs.TOP, mTopSystemBarView);mHvacController.registerHvacViews(mTopSystemBarView);mTopSystemBarWindow.addView(mTopSystemBarView);}mBottomSystemBarView = mCarSystemBarController.getBottomBar(isDeviceSetupForUser());if (mBottomSystemBarView != null) {mSystemBarConfigs.insetSystemBar(SystemBarConfigs.BOTTOM, mBottomSystemBarView);mHvacController.registerHvacViews(mBottomSystemBarView);mBottomSystemBarWindow.addView(mBottomSystemBarView);}mLeftSystemBarView = mCarSystemBarController.getLeftBar(isDeviceSetupForUser());if (mLeftSystemBarView != null) {mSystemBarConfigs.insetSystemBar(SystemBarConfigs.LEFT, mLeftSystemBarView);mHvacController.registerHvacViews(mLeftSystemBarView);mLeftSystemBarWindow.addView(mLeftSystemBarView);}mRightSystemBarView = mCarSystemBarController.getRightBar(isDeviceSetupForUser());if (mRightSystemBarView != null) {mSystemBarConfigs.insetSystemBar(SystemBarConfigs.RIGHT, mRightSystemBarView);mHvacController.registerHvacViews(mRightSystemBarView);mRightSystemBarWindow.addView(mRightSystemBarView);}}private boolean isDeviceSetupForUser() {return mDeviceIsSetUpForUser && !mIsUserSetupInProgress;}...代码省略...}

和之前buildNavBarWindows调用CarSystemBarController的get_Window方法一样,buildNavBarContent方法会继续调用CarSystemBarController的另外一个方法get_Bar,来获取具体的视图内容。

3、CarSystemBarController和get_Bar方法关联的关键代码如下所示:

@SysUISingletonpublic class CarSystemBarController {...代码省略...@Nullablepublic CarSystemBarView getTopBar(boolean isSetUp) {if (!mShowTop) {return null;}mTopView = mCarSystemBarViewFactory.getTopBar(isSetUp);//让mTopView视图内容对象和状态栏触摸监听对象、通知栏控制器、空调面板控制器产生关联setupBar(mTopView, mTopBarTouchListener, mNotificationsShadeController,mHvacPanelController, mHvacPanelOverlayViewController);if (isSetUp) {//对麦克风进行设置,系统不希望在unProvisioned模式下麦克风和配置文件选择器被点击setupMicQcPanel();//配置用户信息面板setupProfilePanel();}return mTopView;}@Nullablepublic CarSystemBarView getBottomBar(boolean isSetUp) {if (!mShowBottom) {return null;}mBottomView = mCarSystemBarViewFactory.getBottomBar(isSetUp);//获取底部栏具体视图内容setupBar(mBottomView, mBottomBarTouchListener, mNotificationsShadeController,mHvacPanelController, mHvacPanelOverlayViewController);return mBottomView;}@Nullablepublic CarSystemBarView getLeftBar(boolean isSetUp) {if (!mShowLeft) {return null;}mLeftView = mCarSystemBarViewFactory.getLeftBar(isSetUp);//获取左侧栏具体视图内容setupBar(mLeftView, mLeftBarTouchListener, mNotificationsShadeController,mHvacPanelController, mHvacPanelOverlayViewController);return mLeftView;}@Nullablepublic CarSystemBarView getRightBar(boolean isSetUp) {if (!mShowRight) {return null;}mRightView = mCarSystemBarViewFactory.getRightBar(isSetUp);//获取右侧栏具体视图内容setupBar(mRightView, mRightBarTouchListener, mNotificationsShadeController,mHvacPanelController, mHvacPanelOverlayViewController);return mRightView;}//让CarSystemBarView视图内容对象和状态栏触摸监听对象、通知栏控制器、空调面板控制器产生关联private void setupBar(CarSystemBarView view, View.OnTouchListener statusBarTouchListener,NotificationsShadeController notifShadeController,HvacPanelController hvacPanelController,HvacPanelOverlayViewController hvacPanelOverlayViewController) {view.setStatusBarWindowTouchListener(statusBarTouchListener);view.setNotificationsPanelController(notifShadeController);view.setHvacPanelController(hvacPanelController);view.registerHvacPanelOverlayViewController(hvacPanelOverlayViewController);mButtonSelectionStateController.addAllButtonsWithSelectionState(view);mButtonRoleHolderController.addAllButtonsWithRoleName(view);mUserNameViewControllerLazy.get().addUserNameView(view);mPrivacyChipViewControllerLazy.get().addPrivacyChipView(view);}private void setupMicQcPanel() {//状态栏图标控制器if (mMicPanelController == null) {mMicPanelController = new StatusIconPanelController(mContext, mCarServiceProvider,mBroadcastDispatcher, mConfigurationController);}mMicPanelController.setOnQcViewsFoundListener(qcViews -> qcViews.forEach(qcView -> {if (qcView.getLocalQCProvider() instanceof MicQcPanel) {MicQcPanel micQcPanel = (MicQcPanel) qcView.getLocalQCProvider();micQcPanel.setControllers(mPrivacyChipViewControllerLazy.get(),mMicPrivacyElementsProviderLazy.get());}}));mMicPanelController.attachPanel(mTopView.requireViewById(R.id.privacy_chip),R.layout.qc_mic_panel, R.dimen.car_mic_qc_panel_width, mPrivacyChipXOffset,mMicPanelController.getDefaultYOffset(), Gravity.TOP | Gravity.END);}//配置用户信息面板private void setupProfilePanel() {View profilePickerView = mTopView.findViewById(R.id.user_name);if (mProfilePanelController == null && profilePickerView != null) {boolean profilePanelDisabledWhileDriving = mContext.getResources().getBoolean(R.bool.config_profile_panel_disabled_while_driving);mProfilePanelController = new StatusIconPanelController(mContext, mCarServiceProvider,mBroadcastDispatcher, mConfigurationController,profilePanelDisabledWhileDriving);mProfilePanelController.attachPanel(profilePickerView, R.layout.qc_profile_switcher,R.dimen.car_profile_quick_controls_panel_width, Gravity.TOP | Gravity.END);}}...代码省略...}

四个视图对象获取视图内容的方式是非常相似的,get_Bar方法首先判断是否显示对应的栏,如果确定显示则会继续调用CarSystemBarViewFactory的get_Bar方法来获取具体的视图内容,随后调用setupBar方法让视图内容对象和状态栏触摸监听对象、通知栏控制器、空调面板控制器产生关联。其中getTopBar方法还会继续调用setupMicQcPanel方法和setupProfilePanel方法来进行一些额外的设置。

4、CarSystemBarViewFactory类关于get_Bar方法的关键代码方法如下所示:

@SysUISingletonpublic class CarSystemBarViewFactory {...代码省略...private final ArrayMap<Type, CarSystemBarView> mCachedViewMap = new ArrayMap<>(Type.values().length);private static final ArrayMap<Type, Integer> sLayoutMap = setupLayoutMapping();private static ArrayMap<Type, Integer> setupLayoutMapping() {ArrayMap<Type, Integer> map = new ArrayMap<>();map.put(Type.TOP, R.layout.car_top_system_bar);//顶部栏视图布局map.put(Type.TOP_UNPROVISIONED, R.layout.car_top_system_bar_unprovisioned);map.put(Type.BOTTOM, R.layout.car_bottom_system_bar);//底部栏视图布局map.put(Type.BOTTOM_UNPROVISIONED, R.layout.car_bottom_system_bar_unprovisioned);map.put(Type.LEFT, R.layout.car_left_system_bar);//左侧栏视图布局map.put(Type.LEFT_UNPROVISIONED, R.layout.car_left_system_bar_unprovisioned);map.put(Type.RIGHT, R.layout.car_right_system_bar);//右侧栏视图布局map.put(Type.RIGHT_UNPROVISIONED, R.layout.car_right_system_bar_unprovisioned);return map;}...代码省略...//获取顶部栏视图内容public CarSystemBarView getTopBar(boolean isSetUp) {return getBar(isSetUp, Type.TOP, Type.TOP_UNPROVISIONED);}//获取底部栏视图内容public CarSystemBarView getBottomBar(boolean isSetUp) {return getBar(isSetUp, Type.BOTTOM, Type.BOTTOM_UNPROVISIONED);}//获取左侧栏视图内容public CarSystemBarView getLeftBar(boolean isSetUp) {return getBar(isSetUp, Type.LEFT, Type.LEFT_UNPROVISIONED);}//获取右侧栏视图内容public CarSystemBarView getRightBar(boolean isSetUp) {return getBar(isSetUp, Type.RIGHT, Type.RIGHT_UNPROVISIONED);}//getBar继续调用getBarCached方法private CarSystemBarView getBar(boolean isSetUp, Type provisioned, Type unprovisioned) {CarSystemBarView view = getBarCached(isSetUp, provisioned, unprovisioned);if (view == null) {String name = isSetUp ? provisioned.name() : unprovisioned.name();Log.e(TAG, "CarStatusBar failed inflate for " + name);throw new RuntimeException("Unable to build " + name + " nav bar due to missing layout");}return view;}//getBarCached方法最终是从sLayoutMap中获取具体的视图内容private CarSystemBarView getBarCached(boolean isSetUp, Type provisioned, Type unprovisioned) {Type type = isSetUp ? provisioned : unprovisioned;if (mCachedViewMap.containsKey(type)) {return mCachedViewMap.get(type);}//从sLayoutMap中获取对应的布局文件资源id@LayoutRes int barLayout = sLayoutMap.get(type);//将布局文件转化为CarSystemBarView类型的View对象。CarSystemBarView view = (CarSystemBarView) View.inflate(mContext, barLayout,/* root= */ null);//为空调按钮设置点击事件view.setupHvacButton();//为快捷图标容器设置控制器view.setupQuickControlsEntryPoints(mQuickControlsEntryPointsController, isSetUp);//为只读图标设置控制器view.setupReadOnlyIcons(mReadOnlyIconsController);view.addView(new FocusParkingView(mContext), 0);//将视图内容View添加到缓存中mCachedViewMap.put(type, view);//返回当前type对应的视图内容Viewreturn mCachedViewMap.get(type);}}

CarSystemBarViewFactory这个类的get_Bar方法会继续调用getBarCached方法,getBarCached首先从类型为ArrayMap<Type, CarSystemBarView>的缓存mCachedViewMap中获取缓存对象,如果存在直接返回,如果不存在则继续从类型为ArrayMap<Type, Integer>的sLayoutMap中获取具体布局文件资源id,结合

private static final ArrayMap<Type, Integer> sLayoutMap = setupLayoutMapping();private static ArrayMap<Type, Integer> setupLayoutMapping() {ArrayMap<Type, Integer> map = new ArrayMap<>();map.put(Type.TOP, R.layout.car_top_system_bar);//顶部栏视图布局map.put(Type.BOTTOM, R.layout.car_bottom_system_bar);//底部栏视图布局map.put(Type.LEFT, R.layout.car_left_system_bar);//左侧栏视图布局map.put(Type.RIGHT, R.layout.car_right_system_bar);//右侧栏视图布局return map;}

我们可以知道顶部栏对应R.layout.car_top_system_bar,底部栏对应R.layout.car_bottom_system_bar,左侧栏对应R.layout.car_left_system_bar,右侧栏对应R.layout.car_right_system_bar,这里在从sLayoutMap中获取到对应的布局文件资源id后,再通过View的inflate方法将布局文件转化为相对应的视图对象,并且会调用setupHvacButton为空调按钮设置点击事件,调用setupQuickControlsEntryPoints为快捷图标容器设置控制器,调用setupReadOnlyIcons为只读图标设置控制器,最后会将CarSystemBarView存储到mCachedViewMap缓存中,并返回CarSystemBarView视图对象。

五、将顶部栏、底部栏、左侧栏、右侧栏添加到Window中。

1、重新回到CarSystemBar的createSystemBar方法

public class CarSystemBar extends SystemUI implements CommandQueue.Callbacks {private void createSystemBar(RegisterStatusBarResult result) {buildNavBarWindows();//构建视图对象容器buildNavBarContent();//构建视图对象内容attachNavBarWindows();//将视图对象添加到Window中...代码省略...}}

前面我们已经分析了构建视图对象容器和构建视图对象内容,接下来我们继续分析attachNavBarWindows方法,是该方法将将视图对象添加到Window中。

2、attachNavBarWindows方法代码如下:

public class CarSystemBar extends SystemUI implements CommandQueue.Callbacks {private void attachNavBarWindows() {mSystemBarConfigs.getSystemBarSidesByZOrder().forEach(this::attachNavBarBySide);}}

attachNavBarWindows会调用SystemBarConfigs的getSystemBarSidesByZOrder方法获取到当前存在的所有SystemBar所对应的Side。

3、SystemBarConfigs类和getSystemBarSidesByZOrder方法相关的关键代码如下所示:

packages/apps/Car/SystemUI/src/com/android/systemui/car/systembar/SystemBarConfigs.java

@SysUISingletonpublic class SystemBarConfigs {private static final int HUN_ZORDER = 10;public static final int TOP = 0;public static final int BOTTOM = 1;public static final int LEFT = 2;public static final int RIGHT = 3;private static final int[] BAR_TYPE_MAP = {InsetsState.ITYPE_STATUS_BAR,//状态栏对应的系统装饰窗口类型InsetsState.ITYPE_NAVIGATION_BAR,//导航栏对应的系统装饰窗口类型InsetsState.ITYPE_CLIMATE_BAR,InsetsState.ITYPE_EXTRA_NAVIGATION_BAR};private final Map<@SystemBarSide Integer, SystemBarConfig> mSystemBarConfigMap =new ArrayMap<>();private final List<@SystemBarSide Integer> mSystemBarSidesByZOrder = new ArrayList<>();@Injectpublic SystemBarConfigs(@Main Resources resources) {...代码省略...populateMaps();readConfigs();//读取SystemBar所对应的SystemBarConfig的配置信息...代码省略...sortSystemBarSidesByZOrder();//使用SystemBarConfig的ZOrder属性对SystemBarConfig的Size进行排序}private static void populateMaps() {BAR_GRAVITY_MAP.put(TOP, Gravity.TOP);BAR_GRAVITY_MAP.put(BOTTOM, Gravity.BOTTOM);BAR_GRAVITY_MAP.put(LEFT, Gravity.LEFT);BAR_GRAVITY_MAP.put(RIGHT, Gravity.RIGHT);BAR_TITLE_MAP.put(TOP, "TopCarSystemBar");BAR_TITLE_MAP.put(BOTTOM, "BottomCarSystemBar");BAR_TITLE_MAP.put(LEFT, "LeftCarSystemBar");BAR_TITLE_MAP.put(RIGHT, "RightCarSystemBar");BAR_GESTURE_MAP.put(TOP, InsetsState.ITYPE_TOP_MANDATORY_GESTURES);BAR_GESTURE_MAP.put(BOTTOM, InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES);BAR_GESTURE_MAP.put(LEFT, InsetsState.ITYPE_LEFT_MANDATORY_GESTURES);BAR_GESTURE_MAP.put(RIGHT, InsetsState.ITYPE_RIGHT_MANDATORY_GESTURES);}private void readConfigs() {// <bool name="config_enableTopSystemBar">true</bool>mTopNavBarEnabled = mResources.getBoolean(R.bool.config_enableTopSystemBar);// <bool name="config_enableBottomSystemBar">true</bool>mBottomNavBarEnabled = mResources.getBoolean(R.bool.config_enableBottomSystemBar);// <bool name="config_enableLeftSystemBar">false</bool>mLeftNavBarEnabled = mResources.getBoolean(R.bool.config_enableLeftSystemBar);// <bool name="config_enableRightSystemBar">false</bool>mRightNavBarEnabled = mResources.getBoolean(R.bool.config_enableRightSystemBar);//顶部栏可用if (mTopNavBarEnabled) {SystemBarConfig topBarConfig =new SystemBarConfigBuilder().setSide(TOP)//顶部栏高度<dimen name="car_top_system_bar_height">@*android:dimen/status_bar_height</dimen>.setGirth(mResources.getDimensionPixelSize(R.dimen.car_top_system_bar_height))//系统栏类型<integer name="config_topSystemBarType">0</integer>.setBarType(mResources.getInteger(R.integer.config_topSystemBarType))//系统栏Z轴序列<integer name="config_topSystemBarZOrder">1</integer>.setZOrder(mResources.getInteger(R.integer.config_topSystemBarZOrder))//<bool name="config_hideTopSystemBarForKeyboard">false</bool>.setHideForKeyboard(mResources.getBoolean(R.bool.config_hideTopSystemBarForKeyboard)).build();mSystemBarConfigMap.put(TOP, topBarConfig);}//底部栏可用if (mBottomNavBarEnabled) {SystemBarConfig bottomBarConfig =new SystemBarConfigBuilder().setSide(BOTTOM)//底部栏高度<dimen name="car_bottom_system_bar_height">@*android:dimen/navigation_bar_height</dimen>.setGirth(mResources.getDimensionPixelSize(R.dimen.car_bottom_system_bar_height))//系统栏类型<<integer name="config_bottomSystemBarType">1</integer>.setBarType(mResources.getInteger(R.integer.config_bottomSystemBarType))//系统栏Z轴序列<integer name="config_bottomSystemBarZOrder">10</integer>.setZOrder(mResources.getInteger(R.integer.config_bottomSystemBarZOrder))//<boolname="config_hideBottomSystemBarForKeyboard">@*android:bool/config_automotiveHideNavBarForKeyboard</bool>.setHideForKeyboard(mResources.getBoolean(R.bool.config_hideBottomSystemBarForKeyboard))//默认为true.build();mSystemBarConfigMap.put(BOTTOM, bottomBarConfig);}//左侧栏不可用if (mLeftNavBarEnabled) {SystemBarConfig leftBarConfig =new SystemBarConfigBuilder().setSide(LEFT).setGirth(mResources.getDimensionPixelSize(R.dimen.car_left_system_bar_width)).setBarType(mResources.getInteger(R.integer.config_leftSystemBarType)).setZOrder(mResources.getInteger(R.integer.config_leftSystemBarZOrder)).setHideForKeyboard(mResources.getBoolean(R.bool.config_hideLeftSystemBarForKeyboard)).build();mSystemBarConfigMap.put(LEFT, leftBarConfig);}//右侧栏不可用if (mRightNavBarEnabled) {SystemBarConfig rightBarConfig =new SystemBarConfigBuilder().setSide(RIGHT).setGirth(mResources.getDimensionPixelSize(R.dimen.car_right_system_bar_width)).setBarType(mResources.getInteger(R.integer.config_rightSystemBarType)).setZOrder(mResources.getInteger(R.integer.config_rightSystemBarZOrder)).setHideForKeyboard(mResources.getBoolean(R.bool.config_hideRightSystemBarForKeyboard)).build();mSystemBarConfigMap.put(RIGHT, rightBarConfig);}}//根据序号对SystemBar进行排序private void sortSystemBarSidesByZOrder() {//获取SystemBarConfig列表List<SystemBarConfig> systemBarsByZOrder = new ArrayList<>(mSystemBarConfigMap.values());systemBarsByZOrder.sort(new Comparator<SystemBarConfig>() {@Overridepublic int compare(SystemBarConfig o1, SystemBarConfig o2) {//调用SystemBarConfig的getZOrder进行大小比较return o1.getZOrder() - o2.getZOrder();}});//在mSystemBarSidesByZOrder中存储当前按照ZOrder从小到大来进行排序的SystemBar的Side数值。systemBarsByZOrder.forEach(systemBarConfig -> {mSystemBarSidesByZOrder.add(systemBarConfig.getSide());});}protected List<Integer> getSystemBarSidesByZOrder() {return mSystemBarSidesByZOrder;}

SystemBarConfig对象本身

private static final class SystemBarConfig {private final int mSide;private final int mBarType;private final int mGirth;private final int mZOrder;private final boolean mHideForKeyboard;private int[] mPaddings = new int[]{0, 0, 0, 0};private SystemBarConfig(@SystemBarSide int side, int barType, int girth, int zOrder,boolean hideForKeyboard) {mSide = side;mBarType = barType;mGirth = girth;mZOrder = zOrder;mHideForKeyboard = hideForKeyboard;}...代码省略...private WindowManager.LayoutParams getLayoutParams() {WindowManager.LayoutParams lp = new WindowManager.LayoutParams(isHorizontalBar(mSide) ? ViewGroup.LayoutParams.MATCH_PARENT : mGirth,isHorizontalBar(mSide) ? mGirth : ViewGroup.LayoutParams.MATCH_PARENT,mapZOrderToBarType(mZOrder),WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,PixelFormat.TRANSLUCENT);//设置窗口半透明// PixelFormat.TRANSPARENT);//设置窗口全透明lp.setTitle(BAR_TITLE_MAP.get(mSide));//顶部栏为new int[]{InsetsState.ITYPE_STATUS_BAR, InsetsState.ITYPE_TOP_MANDATORY_GESTURES};//底部栏为new int[]{InsetsState.ITYPE_NAVIGATION_BAR, InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES};//providesInsetsTypes这个字段很重要,只有设置对这个字段,系统才会认定该窗口是对应的装饰窗口;lp.providesInsetsTypes = new int[]{BAR_TYPE_MAP[mBarType], BAR_GESTURE_MAP.get(mSide)};lp.setFitInsetsTypes(0);lp.windowAnimations = 0;lp.gravity = BAR_GRAVITY_MAP.get(mSide);return lp;}private int mapZOrderToBarType(int zOrder) {//<integer name="config_topSystemBarZOrder">1</integer>//<integer name="config_bottomSystemBarZOrder">10</integer>//zOrder = 10;//从这里可以知道顶部栏的窗口类型为TYPE_STATUS_BAR_ADDITIONAL//从这里可以知道底部栏的窗口类型为TYPE_NAVIGATION_BAR_PANELreturn zOrder >= HUN_ZORDER ? WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;}private void setPaddingBySide(@SystemBarSide int side, int padding) {mPaddings[side] = padding;}}

SystemBarConfig对象的建造者

private static final class SystemBarConfigBuilder {private int mSide;private int mBarType;private int mGirth;private int mZOrder;private boolean mHideForKeyboard;private SystemBarConfigBuilder setSide(@SystemBarSide int side) {mSide = side;return this;}private SystemBarConfigBuilder setBarType(int type) {mBarType = type;return this;}private SystemBarConfigBuilder setGirth(int girth) {mGirth = girth;return this;}private SystemBarConfigBuilder setZOrder(int zOrder) {mZOrder = zOrder;return this;}private SystemBarConfigBuilder setHideForKeyboard(boolean hide) {mHideForKeyboard = hide;return this;}private SystemBarConfig build() {return new SystemBarConfig(mSide, mBarType, mGirth, mZOrder, mHideForKeyboard);}}

总结一下以上代码:

在SystemBarConfigs的构造方法中,调用readConfigs方法来读取SystemBar所对应的SystemBarConfig的配置信息,结合注释可以发现默认情况下,顶部栏和底部栏可用半透明,且顶部栏的窗口类型为TYPE_STATUS_BAR_ADDITIONAL,底部栏的窗口类型为TYPE_NAVIGATION_BAR_PANEL。且它们所对应的配置信息会存储在mSystemBarConfigMap中,左侧栏和右侧栏不可用且它们所对应的配置信息不会存储在mSystemBarConfigMap中。

SystemBarConfigs的构造方法继续调用sortSystemBarSidesByZOrder,该方法会根据已经存储在mSystemBarConfigMap中的SystemBarConfigs的ZOrder字段来进行排序,将mSystemBarConfigMap中SystemBarConfigs的Side字段存储在类型为int的mSystemBarSidesByZOrder集合中。

4、明白了SystemBarConfigs的主要功能,再重新看第2步的attachNavBarWindows方法。

public class CarSystemBar extends SystemUI implements CommandQueue.Callbacks {private void attachNavBarWindows() {mSystemBarConfigs.getSystemBarSidesByZOrder().forEach(this::attachNavBarBySide);}}

我们可以知道该方法最终会循环mSystemBarSidesByZOrder集合的内容,用该集合的子项作为参数,依次调用attachNavBarBySide方法。

5、attachNavBarBySide方法如下所示:

public class CarSystemBar extends SystemUI implements CommandQueue.Callbacks {private void attachNavBarBySide(int side) {switch (side) {case SystemBarConfigs.TOP:if (mTopSystemBarWindow != null) {//如果顶部栏视图容器不为空,将顶部栏视图容器添加到Window中mWindowManager.addView(mTopSystemBarWindow,mSystemBarConfigs.getLayoutParamsBySide(SystemBarConfigs.TOP));}break;case SystemBarConfigs.BOTTOM://如果底部栏视图容器不为空,将顶部栏视图容器添加到Window中if (mBottomSystemBarWindow != null && !mBottomNavBarVisible) {mBottomNavBarVisible = true;mWindowManager.addView(mBottomSystemBarWindow,mSystemBarConfigs.getLayoutParamsBySide(SystemBarConfigs.BOTTOM));}break;case SystemBarConfigs.LEFT:if (mLeftSystemBarWindow != null) {mWindowManager.addView(mLeftSystemBarWindow,mSystemBarConfigs.getLayoutParamsBySide(SystemBarConfigs.LEFT));}break;case SystemBarConfigs.RIGHT:if (mRightSystemBarWindow != null) {mWindowManager.addView(mRightSystemBarWindow,mSystemBarConfigs.getLayoutParamsBySide(SystemBarConfigs.RIGHT));}break;default:return;}}}

attachNavBarBySide做的方法并不多,就是根据对应的type判断当前视图容器的具体类型,到底是顶部栏、底部栏、左侧栏还是右侧栏,根据类型配合类型相对应的参数将该视图容器添加到WindowManager中。

六、总结

本篇文章我们具体分析了CarSystemBar从启动到构建视图,再到将视图添加到Window的流程;结合代码我们可以知道,默认情况下Android车载系统中只显示顶部栏和底部栏视图,最后用一张图对前面的代码流程做一个回顾。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。