SystemUI导航栏
- 1、系统中参数项
- 1.1 相关开关属性
- 2.2 属性设置代码
- 2、设置中设置“三按钮”导航更新流程
- 2.1 属性资源覆盖叠加
- 2.2 SystemUI导航栏接收改变广播
- 2.3 SystemUI导航栏布局更新
- 2.4 时序图
android13-release
1、系统中参数项
1.1 相关开关属性
设置->系统->手势->系统导航->“三按钮”导航
- 设置中:
“三按钮”导航
packages/apps/Settings/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
packages/apps/Settings/res/values-zh-rCN/strings.xml
<string name="legacy_navigation_title" msgid="7877402855994423727">"“三按钮”导航"</string>
- 默认导航栏模式:
config_navBarInteractionMode
frameworks/base/core/res/res/values/config.xml<!-- Controls the navigation bar interaction mode: 0: 3 button mode (back, home, overview buttons) 1: 2 button mode (back, home buttons + swipe up for overview) 2: gestures only for back, home and overview --> <integer name="config_navBarInteractionMode">0</integer> <!-- Whether a software navigation bar should be shown. NOTE: in the future this may be autodetected from the Configuration. --> <bool name="config_showNavigationBar">false</bool>
- Settings数据库中:
adb shell settings get Secure navigation_mode
frameworks/base/core/java/android/provider/Settings.java/** * Navigation bar mode. * 0 = 3 button * 1 = 2 button * 2 = fully gestural * @hide */ @Readable public static final String NAVIGATION_MODE = "navigation_mode";
- prop属性
"qemu.hw.mainkeys"
:允许系统属性覆盖此设置。由仿真器使用。使用方法hasNavigationBar()
- 导航栏高度:
navigation_bar_height
frameworks/base/core/res/res/values/dimens.xml<!-- Height of the bottom navigation / system bar. --> <dimen name="navigation_bar_height">48dp</dimen> <!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height --> <dimen name="navigation_bar_height_landscape">48dp</dimen>
2.2 属性设置代码
设置中显示判断:
String NAV_BAR_MODE_3BUTTON_OVERLAY = "com.android.internal.systemui.navbar.threebutton";
:
/product/overlay/NavigationBarMode3Button/NavigationBarMode3ButtonOverlay.apkString NAV_BAR_MODE_2BUTTON_OVERLAY = "com.android.internal.systemui.navbar.twobutton";
:
/product/overlay/NavigationBarMode2Button/NavigationBarMode2ButtonOverlay.apkString NAV_BAR_MODE_GESTURAL_OVERLAY = "com.android.internal.systemui.navbar.gestural";
:
/product/overlay/NavigationBarModeGestural/NavigationBarModeGesturalOverlay.apk
packages/apps/Settings/src/com/android/settings/gestures/SystemNavigationPreferenceController.java
static boolean isOverlayPackageAvailable(Context context, String overlayPackage) {
try {
return context.getPackageManager().getPackageInfo(overlayPackage, 0) != null;
} catch (PackageManager.NameNotFoundException e) {
// Not found, just return unavailable
return false;
}
}
frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
mNavBarMode = mNavigationModeController.addListener(mModeChangedListener);
frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
private int getCurrentInteractionMode(Context context) {
int mode = context.getResources().getInteger(
com.android.internal.R.integer.config_navBarInteractionMode);
if (DEBUG) {
Log.d(TAG, "getCurrentInteractionMode: mode=" + mode
+ " contextUser=" + context.getUserId());
}
return mode;
}
public void updateCurrentInteractionMode(boolean notify) {
mCurrentUserContext = getCurrentUserContext();
int mode = getCurrentInteractionMode(mCurrentUserContext);
mUiBgExecutor.execute(() ->
Settings.Secure.putString(mCurrentUserContext.getContentResolver(),
Secure.NAVIGATION_MODE, String.valueOf(mode)));
if (DEBUG) {
Log.d(TAG, "updateCurrentInteractionMode: mode=" + mode);
dumpAssetPaths(mCurrentUserContext);
}
if (notify) {
for (int i = 0; i < mListeners.size(); i++) {
mListeners.get(i).onNavigationModeChanged(mode);
}
}
}
frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java
if (mDisplayContent.isDefaultDisplay) {
mHasStatusBar = true;
mHasNavigationBar = mContext.getResources().getBoolean(R.bool.config_showNavigationBar);
// Allow a system property to override this. Used by the emulator.
// See also hasNavigationBar().
String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
if ("1".equals(navBarOverride)) {
mHasNavigationBar = false;
} else if ("0".equals(navBarOverride)) {
mHasNavigationBar = true;
}
} else {
mHasStatusBar = false;
mHasNavigationBar = mDisplayContent.supportsSystemDecorations();
}
2、设置中设置“三按钮”导航更新流程
2.1 属性资源覆盖叠加
OverlayManagerService 运行时资源叠加层 (RRO)
点击设置后,导航栏模式通过OverlayManagerService
服务对config_navBarInteractionMode
资源进行叠加,而settings的Secure表中navigation_mode
属性只是记录模式。
frameworks/base/services/core/java/com/android/server/om/OverlayManagerService.java
资源叠加主要文件:
frameworks/base/core/res/res/values/config.xml
/product/overlay/NavigationBarMode3Button/NavigationBarMode3ButtonOverlay.apk
/product/overlay/NavigationBarMode2Button/NavigationBarMode2ButtonOverlay.apk
/product/overlay/NavigationBarModeGestural/NavigationBarModeGesturalOverlay.apk
updateActivityManager(affectedPackages, userId)
:发送受覆盖状态更改影响的所有目标包的配置更改事件。broadcastActionOverlayChanged(targets, userId)
:发送覆盖包已更改广播ACTION_OVERLAY_CHANGED。
private void updateTargetPackagesLocked(@Nullable Set<PackageAndUser> updatedTargets) {
if (CollectionUtils.isEmpty(updatedTargets)) {
return;
}
persistSettingsLocked();
final SparseArray<ArraySet<String>> userTargets = groupTargetsByUserId(updatedTargets);
for (int i = 0, n = userTargets.size(); i < n; i++) {
final ArraySet<String> targets = userTargets.valueAt(i);
final int userId = userTargets.keyAt(i);
final List<String> affectedPackages = updatePackageManagerLocked(targets, userId);
if (affectedPackages.isEmpty()) {
// The package manager paths are already up-to-date.
continue;
}
FgThread.getHandler().post(() -> {
// Send configuration changed events for all target packages that have been affected
// by overlay state changes.
updateActivityManager(affectedPackages, userId);
// Do not send broadcasts for all affected targets. Overlays targeting the framework
// or shared libraries may cause too many broadcasts to be sent at once.
broadcastActionOverlayChanged(targets, userId);
});
}
}
2.2 SystemUI导航栏接收改变广播
mReceiver :监听ACTION_OVERLAY_CHANGED广播
Secure.NAVIGATION_MODE:记录导航栏模式改变值
mListeners.get(i).onNavigationModeChanged(mode):通知导航栏模式改变的ModeChangedListener
监听
frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) {
Log.d(TAG, "ACTION_OVERLAY_CHANGED");
}
updateCurrentInteractionMode(true /* notify */);
}
};
public NavigationModeController(Context context,
DeviceProvisionedController deviceProvisionedController,
ConfigurationController configurationController,
@UiBackground Executor uiBgExecutor,
DumpManager dumpManager) {
//... ...
IntentFilter overlayFilter = new IntentFilter(ACTION_OVERLAY_CHANGED);
overlayFilter.addDataScheme("package");
overlayFilter.addDataSchemeSpecificPart("android", PatternMatcher.PATTERN_LITERAL);
mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, overlayFilter, null, null);
//... ...
}
public void updateCurrentInteractionMode(boolean notify) {
mCurrentUserContext = getCurrentUserContext();
int mode = getCurrentInteractionMode(mCurrentUserContext);
mUiBgExecutor.execute(() ->
Settings.Secure.putString(mCurrentUserContext.getContentResolver(),
Secure.NAVIGATION_MODE, String.valueOf(mode)));
if (DEBUG) {
Log.d(TAG, "updateCurrentInteractionMode: mode=" + mode);
dumpAssetPaths(mCurrentUserContext);
}
if (notify) {
for (int i = 0; i < mListeners.size(); i++) {
mListeners.get(i).onNavigationModeChanged(mode);
}
}
}
2.3 SystemUI导航栏布局更新
NavigationModeController
通知监听执行onNavigationModeChanged
方法更新,最后navBar.getView().updateStates()
执行更新界面NavigationBarView
:
updateSlippery()
:更新WindowManager.LayoutParams.FLAG_SLIPERY
状态,具体取决于是否启用了向上滑动,或者通知是否在未处于动画状态的情况下完全打开。如果启用了slide,触摸事件将离开导航栏窗口并进入全屏应用程序/主页窗口,如果没有,则手势离开导航栏后,导航栏将收到取消的触摸事件。reloadNavIcons()
:重新导入导航栏相关图片资源updateNavButtonIcons
:更新界面导航栏图标、显示状态,及活动触摸区域
frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@Override
public void onNavigationModeChanged(int mode) {
if (mNavMode == mode) {
return;
}
final int oldMode = mNavMode;
mNavMode = mode;
updateAccessibilityButtonModeIfNeeded();
mHandler.post(() -> {
// create/destroy nav bar based on nav mode only in unfolded state
if (oldMode != mNavMode) {
updateNavbarForTaskbar();
}
for (int i = 0; i < mNavigationBars.size(); i++) {
NavigationBar navBar = mNavigationBars.valueAt(i);
if (navBar == null) {
continue;
}
navBar.getView().updateStates();
}
});
}
frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
public void updateStates() {
if (mNavigationInflaterView != null) {
// Reinflate the navbar if needed, no-op unless the swipe up state changes
mNavigationInflaterView.onLikelyDefaultLayoutChange();
}
updateSlippery();
reloadNavIcons();
updateNavButtonIcons();
WindowManagerWrapper.getInstance().setNavBarVirtualKeyHapticFeedbackEnabled(
!mShowSwipeUpUi);
getHomeButton().setAccessibilityDelegate(
mShowSwipeUpUi ? mQuickStepAccessibilityDelegate : null);
}