前言:
基于高通平台定制android系统。在自己每次接触新的技术需求时,总是会出现很多低级或不懂的问题,因为第一次搞欠缺经验,所以只能花费很多时间和各种方法去尝试。例如这次设置里添加虚拟按键开关,就用了一个多星期去解决问题,主要是发送广播不成功问题。话不多说,进入正题:

首先看一下原始界面:
添加开关隐藏/显示虚拟按键

一、广播实现

1.定义广播内容

frameworks/base/core/java/android/content/Intent.java

     public static final String ACTION_SCREEN_ON = "android.intent.action.SCREEN_ON";
+    public static final String ACTION_NAVIGATION_SHOW = "android.intent.action.NAVIGATION_SHOW";
+    public static final String ACTION_NAVIGATION_HIDE = "android.intent.action.NAVIGATION_HIDE";
2.动态注册广播

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java

      filter.addAction(Intent.ACTION_SCREEN_OFF);
+     filter.addAction(Intent.ACTION_NAVIGATION_SHOW);
+     filter.addAction(Intent.ACTION_NAVIGATION_HIDE);
3.广播接收处理

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java

@@ -909,7 +910,9 @@ public class StatusBar extends SystemUI implements DemoMode,
             boolean showNav = mWindowManagerService.hasNavigationBar();
             if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
             if (showNav) {
-                createNavigationBar();
+               if(1==Settings.System.getInt(mContext.getContentResolver(), Settings.System.HIDE_NAVIGATION, 1)){
+                  createNavigationBar();
+               }
             }
         } catch (RemoteException ex) {
             // no window manager? good luck with that
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (DEBUG) Log.v(TAG, "onReceive: " + intent);
            String action = intent.getAction();
            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
                KeyboardShortcuts.dismiss();
                if (mRemoteInputManager.getController() != null) {
                    mRemoteInputManager.getController().closeRemoteInputs();
                }
                if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) {
                    int flags = CommandQueue.FLAG_EXCLUDE_NONE;
                    String reason = intent.getStringExtra("reason");
                    if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
                        flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL;
                    }
                    animateCollapsePanels(flags);
                }
            }
            else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                finishBarAnimations();
                resetUserExpandedStates();
            }
            else if (DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG.equals(action)) {
                mQSPanel.showDeviceMonitoringDialog();
            }
            else if (Intent.ACTION_NAVIGATION_HIDE.equals(action)){
                removeBar();
                Settings.System.putInt(mContext.getContentResolver(), Settings.System.HIDE_NAVIGATION, 1);
                Log.d(TAG, "android.intent.action.NAVIGATION_HIDE");
            }
            else if(Intent.ACTION_NAVIGATION_SHOW.equals(action)){
                addBarInside();
                Settings.System.putInt(mContext.getContentResolver(), Settings.System.HIDE_NAVIGATION, 0);
                Log.d(TAG, "android.intent.action.NAVIGATION_SHOW");
            }
        }
    };

二、设置到数据库

1.添加key到数据库中

frameworks/base/core/java/android/provider/Settings.java

         public static final Uri DEFAULT_NOTIFICATION_URI = getUriFor(NOTIFICATION_SOUND);

+       /**
+        * add by wuxb
+        * Whether hide navigation
+        * 0 = no
+        * 1 = yes
+        */
+       public static final String HIDE_NAVIGATION = "hide_navigation";
+       public static final Uri HIDE_NAVIGATION_URI = getUriFor(HIDE_NAVIGATION);
+       private static final Validator HIDE_NAVIGATION_VALIDATOR = URI_VALIDATOR;
@@ -4163,6 +4173,7 @@ public final class Settings {
             RINGTONE,
             LOCK_TO_APP_ENABLED,
             NOTIFICATION_SOUND,
+           HIDE_NAVIGATION,
             ACCELEROMETER_ROTATION,
             SHOW_BATTERY_PERCENT,
             NOTIFICATION_VIBRATION_INTENSITY,
@@ -4218,6 +4229,7 @@ public final class Settings {
             PUBLIC_SETTINGS.add(VOLUME_BLUETOOTH_SCO);
             PUBLIC_SETTINGS.add(RINGTONE);
             PUBLIC_SETTINGS.add(NOTIFICATION_SOUND);
+           PUBLIC_SETTINGS.add(HIDE_NAVIGATION);
             PUBLIC_SETTINGS.add(ALARM_ALERT);
             PUBLIC_SETTINGS.add(TEXT_AUTO_REPLACE);
             PUBLIC_SETTINGS.add(TEXT_AUTO_CAPS);
@@ -4315,6 +4327,7 @@ public final class Settings {
             VALIDATORS.put(HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
             VALIDATORS.put(RINGTONE, RINGTONE_VALIDATOR);
             VALIDATORS.put(NOTIFICATION_SOUND, NOTIFICATION_SOUND_VALIDATOR);
+           VALIDATORS.put(HIDE_NAVIGATION,HIDE_NAVIGATION_VALIDATOR);
             VALIDATORS.put(ALARM_ALERT, ALARM_ALERT_VALIDATOR);
             VALIDATORS.put(TEXT_AUTO_REPLACE, TEXT_AUTO_REPLACE_VALIDATOR);
             VALIDATORS.put(TEXT_AUTO_CAPS, TEXT_AUTO_CAPS_VALIDATOR);
2.设置默认值

frameworks/base/packages/SettingsProvider/res/values/defaults.xml

+    <!-- hide navigation -->
+    <integer name="def_hide_navigation">1</integer>
     <bool name="def_bluetooth_on">false</bool>
3.将默认值添加到数据库

frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java

   //set hide navigation
   loadIntegerSetting(stmt, Settings.System.HIDE_NAVIGATION,
                  R.integer.def_hide_navigation);

三、设置开关显示

1.支持中英文标题

packages/apps/Settings/res/values-zh-rCN/strings.xml

 <string name="navigation_settings_title">"虚拟按键"</string>

packages/apps/Settings/res/values/strings.xml

<string name="navigation_settings_title">Navigation settings</string>
2.开关布局UI显示

packages/apps/Settings/res/xml/display_settings.xml

<SwitchPreference
       android:key="navigation"
       android:title="@string/navigation_settings_title"
       android:persistent="false"
       settings:controller="com.android.settings.display.OperateNavigationPreferenceController" />

由于qualcomm平台不同mtk平台,并不是把代码都直接加在DisplaySettings.java文件里的,而是单独写一个实现类的方法
1.packages/apps/Settings/src/com/android/settings/display$ vim OperateNavigationPreferenceController.java

@Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        try {
            boolean isOpen = (boolean)newValue;
            if (isOpen) {
                mContext.sendBroadcast(new Intent(Intent.ACTION_NAVIGATION_SHOW));
            }else {
                mContext.sendBroadcast(new Intent(Intent.ACTION_NAVIGATION_HIDE));
            }
            Log.i(TAG, "onPreferenceChange isOpen="+isOpen);
        } catch (Exception e) {
            Log.e(TAG, "could not persist night mode setting", e);
            return false;
        }
        return true;
    }

第一次编译烧写后,发现点击开关没有反应,然后验证自己的广播是否有问题,adb发送广播是没有问题,因为 这个问题,尝试了很多方法,以为是哪个地方没有添加到,然后没有编译到这个文件,因为看log都没有信息。或者是不是系统权限问题,或是packages目录下的广播发送在frameworks目录下接收不到;陆续编译了十几次都是失败的,之前走的弯路都是错误的;最后找到问题所在:对于不同的版本,API的接口是有差异的(难怪一开始仿照系统其它开关控件不行,因为系统调节音量、亮度都有专门的API);如下图所示:
在这里插入图片描述
解决方法:

@Override
    public boolean isChecked() {
        return Settings.System.getInt(mContext.getContentResolver(), HIDE_NAVIGATION, 1) == 1;
    }

    @Override
    public boolean setChecked(boolean isChecked) {
        Log.i(TAG, "onPreferenceChange isChecked="+isChecked);
       if (isChecked) {
            mContext.sendBroadcast(new Intent(Intent.ACTION_NAVIGATION_SHOW));
        }else {
            mContext.sendBroadcast(new Intent(Intent.ACTION_NAVIGATION_HIDE));
        }
        Settings.System.putInt(mContext.getContentResolver(), HIDE_NAVIGATION, isChecked ? 1 : 0);
        return true;
    }

调试过程中还有一个有趣问题,就是开关实现广播发送时却出现只有一半的作用,即能开显示不能关隐藏或者能关隐藏不能开显示,但是按下面操作就没有问题:

sendBroadcast要发Intent.ACTION_NAVIGATION_HIDE不发android.intent.action.NAVIGATION_HIDE

adb发广播要发android.intent.action.NAVIGATION_HIDE不发android.intent.action.NAVIGATION_HIDE

最终实现的效果:

2020-07-30 Navigation

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐