从Android O系统后,安卓发布了针对车机的Auto版本系统。
比较Android P的的Auto比起Android O来说,更加成熟,逻辑功能更加完善。
Auto系统启动后第一页是用户界面:
选择用户后,进入Home画面:
Auto版本的导航栏和抬头栏也是在SystemUI中实现的。
代码路径:frameworks\base\packages\SystemUI\src\com\android\systemui\SystemBars.java
private void createStatusBarFromConfig() {
if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
//根据config_statusBarComponent配置来决定选择哪个StatusBar
final String clsName = mContext.getString(R.string.config_statusBarComponent);
if (clsName == null || clsName.length() == 0) {
throw andLog("No status bar component configured", null);
}
Class<?> cls = null;
try {
cls = mContext.getClassLoader().loadClass(clsName);
} catch (Throwable t) {
throw andLog("Error loading status bar component: " + clsName, t);
}
try {
mStatusBar = (SystemUI) cls.newInstance();
} catch (Throwable t) {
throw andLog("Error creating status bar component: " + clsName, t);
}
mStatusBar.mContext = mContext;
mStatusBar.mComponents = mComponents;
mStatusBar.start();
if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
}
手机版xml文件:\frameworks\base\packages\SystemUI\res\values\config.xml
<string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar
</string>
Auto版xml文件:\packages\services\Car\car_product\overlay\frameworks\base\packages\SystemUI\res\values\config.xml
<string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.car.CarStatusBar
</string>
可以看到两个版本的差异化,在我们编译系统时,会进行lunch选择:
//...
33. mek_8q-eng
34. mek_8q-userdebug
35. mek_8q_car-eng
36. mek_8q_car-userdebug
37. mek_8q_car2-eng
38. mek_8q_car2-userdebug
//...
如果选择lunch 36则是进行Auto版本编译,会自动根据Auto版本编译配置进行区分,所以Auto版使用的是com.android.systemui.statusbar.car.CarStatusBar的状态栏。
底部那一栏就是我们的NavigationBar。
代码路径:
\frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\car\CarStatusBar.java
\frameworks\base\packages\SystemUI\res\layout\navigation_bar_window.xml
\packages\services\Car\car_product\overlay\frameworks\base\packages\SystemUI\res\layout\car_navigation_bar.xml
CarStatusBar中加载NavigationBar的代码逻辑:
@Override
protected void createNavigationBar() {
mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar);
mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar);
mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar);
buildNavBarWindows();
buildNavBarContent();
attachNavBarWindows();
}
private void buildNavBarContent() {
if (mShowBottom) {
buildBottomBar((mDeviceIsProvisioned) ? R.layout.car_navigation_bar :
R.layout.car_navigation_bar_unprovisioned);
}
//省略一部分代码
//...
}
private void buildNavBarWindows() {
if (mShowBottom) {
//navigation_bar_window这个布局文件是kong
mNavigationBarWindow = (ViewGroup) View.inflate(mContext,
R.layout.navigation_bar_window, null);
}
if (mShowLeft) {
mLeftNavigationBarWindow = (ViewGroup) View.inflate(mContext,
R.layout.navigation_bar_window, null);
}
if (mShowRight) {
mRightNavigationBarWindow = (ViewGroup) View.inflate(mContext,
R.layout.navigation_bar_window, null);
}
}
private void attachNavBarWindows() {
//可以看到最终的窗口是放在TYPE_NAVIGATION_BAR这个系统窗口中的
if (mShowBottom) {
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
PixelFormat.TRANSLUCENT);
lp.setTitle("CarNavigationBar");
lp.windowAnimations = 0;
mWindowManager.addView(mNavigationBarWindow, lp);
}
//省略一部分代码
//...
}
//得到mNavigationBarView
private void buildBottomBar(int layout) {
// SystemUI requires that the navigation bar view have a parent. Since the regular
// StatusBar inflates navigation_bar_window as this parent view, use the same view for the
// CarNavigationBarView.
View.inflate(mContext, layout, mNavigationBarWindow);
mNavigationBarView = (CarNavigationBarView) mNavigationBarWindow.getChildAt(0);
if (mNavigationBarView == null) {
Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
throw new RuntimeException("Unable to build botom nav bar due to missing layout");
}
mNavigationBarView.setStatusBar(this);
}
根据上面的代码,最终layout\car_navigation_bar.xml的布局是放到navigation_bar_window.xml中去,加载到TYPE_NAVIGATION_BAR的系统窗口中去。
分析\packages\services\Car\car_product\overlay\frameworks\base\packages\SystemUI\res\layout\car_navigation_bar.xml代码可以看到,Auto版本的布局都在这个文件里。
从左到右的布局:Home,地图,音乐,电话,菜单,空调,通知,设置和时间。
布局:\packages\services\Car\car_product\overlay\frameworks\base\packages\SystemUI\res\layout\car_navigation_bar.xml
其中Home的按钮:
<com.android.systemui.statusbar.car.CarNavigationButton
android:id="@+id/home"
style="@style/NavigationBarButton"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
android:src="@drawable/car_ic_overview"
android:background="?android:attr/selectableItemBackground"
/>
intent指定跳转界面就是我们的Launcher。
HOME界面其实就是上面我们看到的 Let’s Driver界面。就是Auto版本的Launcher。
Auto版本代码路径:\packages\apps\Car\Launcher
Carlauncher这个应用的代码比较简单,就不多讲了。
CarStatusBar的代码中,CarStatusBar类是继承自phone/StatusBar 这个类的。
所以车机版的状态栏加载跟手机版的加载差不多,不错,在、、、、、、\packages\services\Car\car_product\overlay\frameworks\base\packages\SystemUI\res\layout\status_bar.xml对状态栏的功能做了简化。
/**
* A status bar (and navigation bar) tailored for the automotive use case.
*/
public class CarStatusBar extends StatusBar implements
CarBatteryController.BatteryViewHandler {
private static final String TAG = "CarStatusBar";
public static final boolean ENABLE_HVAC_CONNECTION
= !SystemProperties.getBoolean("android.car.hvac.demo", true);
Auto系统启动后,第一个界面是用户选择界面,并不是我们的CarLauncher界面。所以我们来分析一下用户界面的加载过程。
相关代码路径:
\frameworks\base\packages\SystemUI\res\layout\car_fullscreen_user_switcher.xml
\frameworks\base\packages\SystemUI\res\layout\super_status_bar.xml
\frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\car\UserGridRecyclerView.java
\frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\car\FullscreenUserSwitcher.java
\frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\policy\UserSwitcherController.java
\frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.java
代码\frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\car\CarStatusBar.java中会去加载car_fullscreen_user_switcher.xml
布局文件,布局默认改界面是隐藏,生成FullscreenUserSwitcher的对象并对用户选择界面进行操作:
@Override
protected void createUserSwitcher() {
UserSwitcherController userSwitcherController =
Dependency.get(UserSwitcherController.class);
if (userSwitcherController.useFullscreenUserSwitcher()) {
mFullscreenUserSwitcher = new FullscreenUserSwitcher(this,
mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub), mContext); //加载fullscreen_user_switcher_stub布局
} else {
super.createUserSwitcher();
}
}
@Override
public void onUserSwitched(int newUserId) {
super.onUserSwitched(newUserId);
if (mFullscreenUserSwitcher != null) {
mFullscreenUserSwitcher.onUserSwitched(newUserId);
}
}
@Override
public void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked) {
super.updateKeyguardState(goingToFullShade, fromShadeLocked);
if (mFullscreenUserSwitcher != null) {
//是否为FULLSCREEN_USER_SWITCHER状态
if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
mFullscreenUserSwitcher.show(); //显示用户选择界面
} else {
mFullscreenUserSwitcher.hide(); //隐藏用户选择界面
}
}
}
其中,StatusBar.java中:
public void showKeyguardImpl() {
mIsKeyguard = true;
if (mLaunchTransitionFadingAway) {
mNotificationPanel.animate().cancel();
onLaunchTransitionFadingEnded();
}
mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
//根据useFullscreenUserSwitcher的状态来决定是否设置FULLSCREEN_USER_SWITCHER状态
if (mUserSwitcherController != null && mUserSwitcherController.useFullscreenUserSwitcher()) {
setBarState(StatusBarState.FULLSCREEN_USER_SWITCHER);
} else {
setBarState(StatusBarState.KEYGUARD);
}
updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
updatePanelExpansionForKeyguard();
if (mDraggedDownRow != null) {
mDraggedDownRow.setUserLocked(false);
mDraggedDownRow.notifyHeightChanged(false /* needsAnimation */);
mDraggedDownRow = null;
}
}
UserSwitcherController.java中:
public boolean useFullscreenUserSwitcher() {
// Use adb to override:
// adb shell settings put system enable_fullscreen_user_switcher 0 # Turn it off.
// adb shell settings put system enable_fullscreen_user_switcher 1 # Turn it on.
// Restart SystemUI or adb reboot.
final int DEFAULT = -1;
final int overrideUseFullscreenUserSwitcher =
Settings.System.getInt(mContext.getContentResolver(),
"enable_fullscreen_user_switcher", DEFAULT);
if (overrideUseFullscreenUserSwitcher != DEFAULT) {
return overrideUseFullscreenUserSwitcher != 0;
}
// Otherwise default to the build setting.
return mContext.getResources().getBoolean(R.bool.config_enableFullscreenUserSwitcher);
}
使用以下的命令进行设置
// Use adb to override:
// adb shell settings put system enable_fullscreen_user_switcher 0 # Turn it off.
// adb shell settings put system enable_fullscreen_user_switcher 1 # Turn it on.
// Restart SystemUI or adb reboot.
所以只要使用 adb shell settings put system enable_fullscreen_user_switcher 0 ,就可以开机不显示用户选择界面。直接进入Home画面。
使用adb reboot后生效。