系统手势导航-虚拟导航切换

问题讨论-需求场景

何为手势和物理按键、虚拟导航

Android11 开始支持了手势操作,如大家目前手机基本上都是手势操作形式;早期都是物理按键或者虚拟按键的操作。

手势导航和虚拟导航如何选择

系统层面:设置->系统->手势->手势切换

如何应用程序中让用户自己选择,自由选择设置

强制手势或者底部虚拟导航

定制的很多案子,强制使用手势或者底部虚拟导航:手势导航和虚拟导航二选一,那么如何定制客户选择其一时候有要求虚拟导航或者底部导航,不允许修改的。如何不让用户修改。

参考资料

Android导航方式切换
SystemUI导航栏

如何配置导航方式

mtk 、RK 配置都在同一个路径下,修改文件

\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>

应用层如何设置导航方式

源码分析:

/packages/apps/Settings/src/com/android/settings/gestures/SystemNavigationGestureSettings.java

导航栏切换逻辑在 SystemNavigationGestureSettings.java 文件中

在线源码SystemNavigationGestureSettings.java 源码

获取当前导航方式和设置导航方式 核心代码如下

@VisibleForTesting
202      static String getCurrentSystemNavigationMode(Context context) {
203          if (SystemNavigationPreferenceController.isGestureNavigationEnabled(context)) {
204              return KEY_SYSTEM_NAV_GESTURAL;
205          } else if (SystemNavigationPreferenceController.is2ButtonNavigationEnabled(context)) {
206              return KEY_SYSTEM_NAV_2BUTTONS;
207          } else {
208              return KEY_SYSTEM_NAV_3BUTTONS;
209          }
210      }
211  
212      @VisibleForTesting
213      static void setCurrentSystemNavigationMode(IOverlayManager overlayManager, String key) {
214          String overlayPackage = NAV_BAR_MODE_GESTURAL_OVERLAY;
215          switch (key) {
216              case KEY_SYSTEM_NAV_GESTURAL:
217                  overlayPackage = NAV_BAR_MODE_GESTURAL_OVERLAY;
218                  break;
219              case KEY_SYSTEM_NAV_2BUTTONS:
220                  overlayPackage = NAV_BAR_MODE_2BUTTON_OVERLAY;
221                  break;
222              case KEY_SYSTEM_NAV_3BUTTONS:
223                  overlayPackage = NAV_BAR_MODE_3BUTTON_OVERLAY;
224                  break;
225          }
226  
227          try {
228              overlayManager.setEnabledExclusiveInCategory(overlayPackage, USER_CURRENT);
229          } catch (RemoteException e) {
230              throw e.rethrowFromSystemServer();
231          }
232      }

获取当前导航方式

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";

所以直接通过系统标准API 获取Setting 相关属性值来获取当前导航方式

 public static Mode getNavigationMode(Context context) {
        try {
            ContentResolver resolver = context.getApplicationContext().getContentResolver();
            int mode = Settings.Secure.getInt(resolver, "navigation_mode");
            if (mode == 0) {
                return Mode.THREEBUTTON;
            } else if (mode == 2) {
                return Mode.GESTURAL;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
 

设置导航栏

回归到 SystemNavigationGestureSettings.java 类的 setCurrentSystemNavigationMode 方法,最终调用

 overlayManager.setEnabledExclusiveInCategory(overlayPackage, USER_CURRENT);

思路:拿到 overlayManager 然后调用 setEnabledExclusiveInCategory 方法即可。

SystemNavigationGestureSettings 类中,overlayManager 获取: 不就是获取OVERLAY_SERVICE 的服务吗

       mOverlayManager = IOverlayManager.Stub.asInterface(
                 ServiceManager.getService(Context.OVERLAY_SERVICE));
  

在应用中获取思路

Object mIOverlayManager = context.getSystemService("overlay")

然后调用 setEnabledExclusiveInCategory 方法, 但是服务的方法本身不是对外释放的,那就反射。

@Override
        public boolean setEnabledExclusiveInCategory(@Nullable String packageName,
                final int userIdArg) {
            if (packageName == null) {
                return false;
            }

            try {
                traceBegin(TRACE_TAG_RRO, "OMS#setEnabledExclusiveInCategory " + packageName);

                final OverlayIdentifier overlay = new OverlayIdentifier(packageName);
                final int realUserId = handleIncomingUser(userIdArg,
                        "setEnabledExclusiveInCategory");
                enforceActor(overlay, "setEnabledExclusiveInCategory", realUserId);

                final long ident = Binder.clearCallingIdentity();
                try {
                    synchronized (mLock) {
                        try {
                            mImpl.setEnabledExclusive(overlay,
                                    true /* withinCategory */, realUserId)
                                .ifPresent(OverlayManagerService.this::updateTargetPackagesLocked);
                            return true;
                        } catch (OperationFailedException e) {
                            return false;
                        }
                    }
                } finally {
                    Binder.restoreCallingIdentity(ident);
                }
            } finally {
                traceEnd(TRACE_TAG_RRO);
            }
        }

规避客户切换导航栏

在默认了系统导航栏 上面已经给出了用户可以切换导航栏的。 很多产品在默认导航栏后不允许客户切换导航方式的。 那么就需要在系统设置里面规避,隐藏切换方式。

涉及到的源码修改:

/packages/apps/Settings/src/com/android/settings/gestures/SystemNavigationPreferenceController.java

     getAvailabilityStatus 方法,修改如下:

    @Override
    public int getAvailabilityStatus() {
      //  return isGestureAvailable(mContext) ? UNSUPPORTED_ON_DEVICE : UNSUPPORTED_ON_DEVICE;
        return isGestureAvailable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
    }   
  如果允许设置里面修改,用默认的:return isGestureAvailable(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
  如果不允许修改,那么 返回如下://  return isGestureAvailable(mContext) ? UNSUPPORTED_ON_DEVICE : UNSUPPORTED_ON_DEVICE;
  

其它扩展知识

导航栏高度: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>


涉及到部分图示和工具类

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

设置和获取手导航


import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.Context;
import android.os.UserHandle;
import android.provider.Settings;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class NavigationHelper {


    /**
     * 设置导航模式
     *
     * @param context
     * @param mode    GESTURAL:手势 TWOBUTTON:二按钮 THREEBUTTON:三按钮
     */
    public static void setNavigationMode(Context context, Mode mode) {
        try {
          //  Object mIOverlayManager = context.getSystemService("overlay");
            @SuppressLint("WrongConstant") Object mIOverlayManager = context.getSystemService(Context.OVERLAY_SERVICE);
            Method setEnabledExclusiveInCategory = mIOverlayManager.getClass().getMethod("setEnabledExclusiveInCategory", String.class, UserHandle.class);
            setEnabledExclusiveInCategory.setAccessible(true);
            String overlayPackage = "com.android.internal.systemui.navbar.gestural";
            if (mode == Mode.TWOBUTTON) {
                overlayPackage = "com.android.internal.systemui.navbar.twobutton";
            } else if (mode == Mode.THREEBUTTON) {
                overlayPackage = "com.android.internal.systemui.navbar.threebutton";
            }
            UserHandle userHandle = (UserHandle) newInstance(UserHandle.class, new Class[]{int.class}, 0);
            setEnabledExclusiveInCategory.invoke(mIOverlayManager, overlayPackage, userHandle);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取导航模式
     * @param context
     * @return
     */
    public static Mode getNavigationMode(Context context) {
        try {
            ContentResolver resolver = context.getApplicationContext().getContentResolver();
            int mode = Settings.Secure.getInt(resolver, "navigation_mode");
            if (mode == 0) {
                return Mode.THREEBUTTON;
            } else if (mode == 2) {
                return Mode.GESTURAL;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 开启经典导航模式
     *
     * @param enable
     */
    public static void setClassicNavigationMode(boolean enable) {
        try {
            Class c = Class.forName("android.os.SystemProperties");
            Method method = c.getMethod("set", String.class, String.class);
            method.setAccessible(true);
            method.invoke(null, "persist.sys.classic.navigation.mode", String.valueOf(enable));
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    private static Object newInstance(Class clazz, Class[] argsType, Object... args) {
        Object instance = null;
        try {
            Constructor constructor = clazz.getConstructor(argsType);
            constructor.setAccessible(true);
            instance = constructor.newInstance(args);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return instance;
    }

    public enum Mode {
        GESTURAL,
        TWOBUTTON,
        THREEBUTTON
    }

}
### 回答1: AR室内导航是利用增强现实(AR)技术结合Three.js开发的一种应用。AR技术能够将虚拟的三维图像在现实环境中展示出来,而Three.js是一种用于在Web浏览器中创建和展示三维图形的JavaScript库。 AR室内导航基于用户所在的位置和目标位置,通过设备摄像头获取实时图像,并将虚拟导航信息叠加在图像上,为用户提供导航的帮助。利用Three.js的技术,可以实现将建筑、室内设施等三维模型与实际场景相结合,使用户能够直观地了解周围环境和导航路径。 开发AR室内导航需要进行以下步骤:首先,利用AR技术获取实时的摄像头图像。其次,借助Three.js创建和加载三维模型,包括建筑和室内设施的模型。然后,通过算法将虚拟模型与实际场景对齐,确保其在图像中的正确展示。最后,根据用户的位置和目标位置,计算出导航路径并在图像上显示出来。 AR室内导航在现实生活中具有广泛的应用前景。比如,可以应用于商场、机场等大型室内空间,帮助用户快速找到目标位置;也可以用于导览系统,为用户提供关于展览品的信息和导览路径;甚至可以应用于室内导航机器人,帮助机器人自主地完成室内导航任务。 总之,AR室内导航结合了增强现实和Three.js技术,可以有效利用虚拟的三维图像为用户提供室内导航的帮助,具有广泛的应用前景。AR技术和Three.js技术的不断发展将进一步推动AR室内导航的创新和应用。 ### 回答2: AR室内导航是一种利用增强现实技术为用户提供室内导航服务的应用。而Three.js是一款基于WebGL的开源JavaScript 3D库,可以在网页上创建并展示3D图形和动画。 通过结合AR技术和Three.js库,可以实现AR室内导航服务。首先,我们需要收集室内的地图数据,并将其转换为Three.js可以处理的格式。这些地图数据包括建筑物的结构、房间的位置、通道、门窗等信息。 接下来,利用AR技术,我们可以在用户的手机或者其他设备的摄像头画面上叠加虚拟信息,如地图、导航路径等。用户可以通过观看屏幕上的现实场景,同时看到与现实世界相结合的虚拟导航信息,以指导其在室内进行导航。 在AR室内导航中,利用Three.js可以创建虚拟的建筑物模型和导航路径。借助于Three.js的3D渲染功能,我们可以根据地图数据绘制建筑物的3D模型,并在模型上标记房间名称、导航路径等信息。用户可以通过观察屏幕上的AR场景,准确地了解自己当前所处的位置和前进方向。 另外,AR室内导航还可以结合其他功能,如语音导航、路径规划等。通过结合语音识别技术,用户可以通过语音指令告诉设备他们想去的目的地,然后AR导航系统可以为用户提供路径规划并进行语音导航指引。 总体而言,AR室内导航结合了增强现实技术和Three.js库的优势,为用户提供了更直观、沉浸式的室内导航体验。通过利用现有技术的结合,我们可以帮助用户更轻松地找到目的地,并提升室内导航的效率和准确性。 ### 回答3: AR室内导航是一种利用增强现实技术和Three.js库实现的室内导航系统。AR(增强现实)技术是指通过手机相机或其他设备的摄像头捕捉现实世界的图像,并在图像上叠加虚拟对象或信息的技术。Three.js是一个基于WebGL的开源JavaScript库,可以用于创建和显示三维图形和动画。 AR室内导航系统利用摄像头捕捉室内环境的图像,通过图像处理和计算机视觉技术,识别出室内场景中的相关特征,如墙壁、楼梯、门等。然后,通过Three.js库创建虚拟导航界面,将用户当前位置和目标位置在AR视图中显示出来。 用户只需要将手机摄像头对准室内环境,系统会通过AR技术识别出当前位置,并在屏幕上叠加导航界面。导航界面可以显示当前位置的平面示意图,以及路径指示标记,指引用户前往目标位置。用户可以通过手机屏幕上的虚拟按钮或手势交互,进行导航的操作,如放大和缩小地图、切换楼层、选择目标位置等。 AR室内导航系统的优势在于可以提供更直观、准确的室内导航体验。用户只需要通过手机摄像头观察现实场景,并在屏幕上看到导航界面,就可以轻松地找到目标位置,不再需要依赖图纸或标识牌。同时,Three.js库的强大功能也为导航界面的设计和交互提供了灵活性和自定义性。 总而言之,AR室内导航系统结合了增强现实技术和Three.js库,为用户提供了更直观、准确的室内导航体验,使得在复杂的室内环境中找到目标位置变得更加简单和便捷。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

野火少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值