SystemUI导航栏

SystemUI导航栏

  • 1、系统中参数项
    • [1.1 相关开关属性](#1.1 相关开关属性)
    • [2.2 属性设置代码](#2.2 属性设置代码)
  • 2、设置中设置"三按钮"导航更新流程
    • [2.1 属性资源覆盖叠加](#2.1 属性资源覆盖叠加)
    • [2.2 SystemUI导航栏接收改变广播](#2.2 SystemUI导航栏接收改变广播)
    • [2.3 SystemUI导航栏布局更新](#2.3 SystemUI导航栏布局更新)
    • [2.4 时序图](#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

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
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
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.apk
  • String NAV_BAR_MODE_2BUTTON_OVERLAY = "com.android.internal.systemui.navbar.twobutton";:
    /product/overlay/NavigationBarMode2Button/NavigationBarMode2ButtonOverlay.apk
  • String 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

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

java 复制代码
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;

mNavBarMode = mNavigationModeController.addListener(mModeChangedListener);

frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java

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

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

资源叠加主要文件:config.xml

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。
java 复制代码
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

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

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

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);
}

2.4 时序图

相关推荐
AFinalStone6 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
AFinalStone6 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
AFinalStone6 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui
AFinalStone6 天前
Android7 SystemUI 源码解析(一)全景概览与架构设计
android·systemui
AFinalStone6 天前
Android7 SystemUI 源码解析(六)QuickSettings快捷设置面板
android·systemui
zzzzzzzz'1 个月前
浅谈自动化测试2——selenium库中的常用函数
selenium·测试工具·文件上传·导航栏·弹窗·定位元素·查找元素
这是个栗子5 个月前
【微信小程序问题解决】微信小程序全局 navigationBarTitleText 不起作用
微信小程序·小程序·导航栏
陆业聪6 个月前
SystemUI 里做 Launcher App 列表:四种方案的 Framework 原理与工程取舍
android·framework·systemui·launcher
木子啊8 个月前
Uni-app导航栏适配终极避坑指南
uni-app·自定义导航栏·导航栏
于山巅相见8 个月前
【3588】Android动态隐藏导航栏
android·导航栏·状态栏·android11