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 时序图

相关推荐
ItJavawfc8 天前
Android12_13左上角状态栏数字时间显示右移动
systemui·时间右移动
Swuagg15 天前
AR 眼镜之-系统通知定制(通知弹窗)-实现方案
notification·systemui·系统通知·通知弹窗
Swuagg16 天前
AR 眼镜之-系统通知定制(通知中心)-实现方案
systemui·系统通知·通知中心
攻城狮_Dream3 个月前
企业必备技能导航栏的写法
前端·css·html·导航栏
小先生Zcutie5 个月前
【Android】SystemUI通知栏过滤指定应用的通知
android·systemui
hehui09215 个月前
android11 SystemUI入門之KeyguardPatternView解析
systemui
大美B端工场-B端系统美颜师7 个月前
B端系统:导航机制设计,用户体验提升的法宝
ux·导航栏·b端系统
黛琳ghz7 个月前
uni-app 经验分享,从入门到离职(四)——页面栈以及页面跳转的 API(开发经验总结)
小程序·uni-app·页面跳转·导航栏·页面栈·开发经验总结·入门基础
JohnnyDeng9410 个月前
StatusBarManager中的相关标志位
systemui
Jon_Lo1 年前
Android SystemUI setSystemUiVisibility()参数Flag详解
android·导航栏·systemui·1024程序员节·状态栏·fullscreen