【SystemUI】新增实体键盘快捷键说明
一、问题描述
基于 Android 14平台,设备外接键盘,Android 原生中的设置 - 键盘 - 实体键盘 - 键盘快捷键中可以查看系统
、输入
、已开应用
、当前应用
四类快捷方式,但是外接键盘是有触摸板的,未介绍触摸板手势的说明,需要添加。并且默认的快捷键存在部分功能不存在需要移除,部分快捷键的图示显示错误需要修改。
二、问题分析
我们需要根据设置的入口来定位实际调用快捷键说明的界面,点击实体键盘菜单查看日志:adb logcat -s SubSettings
,可以定位到 PhysicalKeyboardFragment
<activity android:name="Settings$PhysicalKeyboardActivity"android:label="@string/physical_keyboard_title"android:exported="true"android:clearTaskOnLaunch="true"><intent-filter android:priority="1"><action android:name="android.settings.HARD_KEYBOARD_SETTINGS" /><category android:name="android.intent.category.DEFAULT" /></intent-filter><meta-data android:name="com.android.settings.FRAGMENT_CLASS"android:value="com.android.settings.inputmethod.PhysicalKeyboardFragment" /><meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"android:value="@string/menu_key_system"/>
</activity>
再定位到快捷键的点击事件执行的是 requestShowKeyboardShortcuts
Settings/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
private void toggleKeyboardShortcutsMenu() {getActivity().requestShowKeyboardShortcuts();
}
查看此方法具体执行的发送一个广播,广播的接受者为 SystemUI
frameworks/base/core/java/android/app/Activity.java/*** Request the Keyboard Shortcuts screen to show up. This will trigger* {@link #onProvideKeyboardShortcuts} to retrieve the shortcuts for the foreground activity.*/
public final void requestShowKeyboardShortcuts() {final ComponentName sysuiComponent = ComponentName.unflattenFromString(getResources().getString(com.android.internal.R.string.config_systemUIServiceComponent));Intent intent = new Intent(Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS);intent.setPackage(sysuiComponent.getPackageName());sendBroadcastAsUser(intent, Process.myUserHandle());
}
组件如下
<!-- SystemUi service component -->
<string name="config_systemUIServiceComponent" translatable="false">com.android.systemui/com.android.systemui.SystemUIService</string>
action 如下
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_SHOW_KEYBOARD_SHORTCUTS ="com.android.intent.action.SHOW_KEYBOARD_SHORTCUTS";
接下来根据 action name 查看 SystemUI 的 AndroidManifest.xml
<receiverandroid:name=".statusbar.KeyboardShortcutsReceiver"android:exported="true"><intent-filter><action android:name="com.android.intent.action.DISMISS_KEYBOARD_SHORTCUTS" /><action android:name="com.android.intent.action.SHOW_KEYBOARD_SHORTCUTS" /></intent-filter>
</receiver>
查看 src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java 中的 onReceive 中的处理,显示对话框调用的是 KeyboardShortcutListSearch.show(context, -1 /* deviceId unknown */);
public void onReceive(Context context, Intent intent) {if (mIsShortcutListSearchEnabled && Utilities.isLargeScreen(context)) {if (Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {KeyboardShortcutListSearch.show(context, -1 /* deviceId unknown */);} else if (Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(intent.getAction())) KeyboardShortcutListSearch.dismiss();}} else {...
}
查看 src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
中的 show 方法
public static void show(Context context, int deviceId) {MetricsLogger.visible(context,MetricsProto.MetricsEvent.KEYBOARD_SHORTCUTS_HELPER);synchronized (sLock) {if (sInstance != null && !sInstance.mContext.equals(context)) {dismiss();}getInstance(context).showKeyboardShortcuts(deviceId);}
}
具体执行的 showKeyboardShortcuts(int deviceId)
-> showKeyboardShortcutSearchList
-> handleShowKeyboardShortcutSearchList
,可以查看到 对话框加载的 xml 布局
final View keyboardShortcutsView = inflater.inflate(R.layout.keyboard_shortcuts_search_view, null);
创建快捷键分类的执行的是 createHardcodedShortcuts
private void createHardcodedShortcuts() {// Add system shortcutsmKeySearchResultMap.put(SHORTCUT_SYSTEM_INDEX, true);mSystemGroup.add(getMultiMappingSystemShortcuts(mContext));mSystemGroup.add(getSystemMultitaskingShortcuts(mContext));// Add input shortcutsmKeySearchResultMap.put(SHORTCUT_INPUT_INDEX, true);mInputGroup.add(getMultiMappingInputShortcuts(mContext));// Add open apps shortcutsfinal List<KeyboardShortcutMultiMappingGroup> appShortcuts =Arrays.asList(getDefaultMultiMappingApplicationShortcuts());if (appShortcuts != null && !appShortcuts.isEmpty()) {mOpenAppsGroup = appShortcuts;mKeySearchResultMap.put(SHORTCUT_OPENAPPS_INDEX, true);} else {mKeySearchResultMap.put(SHORTCUT_OPENAPPS_INDEX, false);}
}
当前应用
是动态创建的,执行 showKeyboardShortcuts
创建
// Add specific app shortcuts
if (result.isEmpty()) {mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, false);
} else {KeyboardShortcuts.sanitiseShortcuts(result);mSpecificAppGroup = reMapToKeyboardShortcutMultiMappingGroup(result);mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, true);
}
具体的创建的每条快捷键说明的逻辑都在 KeyboardShortcutListSearch
类中,我们可以使用 adb 命令直接调出快捷键的对话框
adb shell am broadcast -a com.android.intent.action.SHOW_KEYBOARD_SHORTCUTS -p com.android.systemui
三、解决方案
1. 在 keyboard_shortcuts_search_view 创建触摸板分类
res/layout/keyboard_shortcuts_search_view.xml
<Buttonandroid:id="@+id/shortcut_touch_pad"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="12dp"style="@style/ShortCutButton"android:text="@string/keyboard_shortcut_touch_pad"/>
2. 在代码创建触摸板分类
private static int SHORTCUT_TOUCHPAD_INDEX = 4;private List<KeyboardShortcutMultiMappingGroup> mTouchPadGroup = new ArrayList<>();private Button mButtonTouchPad;
3. 创建触摸板快捷键说明
private void createHardcodedShortcuts() {...// Add keyboard touchpad usagemKeySearchResultMap.put(SHORTCUT_TOUCHPAD_INDEX, true);mTouchPadGroup.add(getTouchPadShortcuts(mContext));...
}private static KeyboardShortcutMultiMappingGroup getTouchPadShortcuts(Context context) {KeyboardShortcutMultiMappingGroup touchPadGroup =new KeyboardShortcutMultiMappingGroup(context.getString(R.string.keyboard_shortcut_group_touchpad),new ArrayList<>());// TouchPad shortcuts:String[] shortcutLabels = {context.getString(R.string.touchpad_press_left),context.getString(R.string.touchpad_press_right),context.getString(R.string.touchpad_one_finger_sliding),context.getString(R.string.touchpad_one_finger_click),context.getString(R.string.touchpad_two_fingers_click),context.getString(R.string.touchpad_two_fingers_sliding_left_right),context.getString(R.string.touchpad_two_fingers_sliding_up_down),context.getString(R.string.touchpad_three_fingers_sliding_up),context.getString(R.string.touchpad_three_fingers_sliding_down),context.getString(R.string.touchpad_three_fingers_sliding_left_right),};String[] shortcutCommandLabels = {context.getString(R.string.touchpad_command_press_left),context.getString(R.string.touchpad_command_press_right),context.getString(R.string.touchpad_command_one_finger_sliding),context.getString(R.string.touchpad_command_one_finger_click),context.getString(R.string.touchpad_command_two_fingers_click),context.getString(R.string.touchpad_command_two_fingers_sliding_left_right),context.getString(R.string.touchpad_command_two_fingers_sliding_up_down),context.getString(R.string.touchpad_command_three_fingers_sliding_up),context.getString(R.string.touchpad_command_three_fingers_sliding_down),context.getString(R.string.touchpad_command_three_fingers_sliding_left_right),};for (int i = 0; i < shortcutLabels.length; i++) {List<ShortcutKeyGroup> shortcutKeyGroups = Arrays.asList(new ShortcutKeyGroup(new KeyboardShortcutInfo(shortcutLabels[i],KeyEvent.KEYCODE_DPAD_UP,KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON),shortcutCommandLabels[i]));ShortcutMultiMappingInfo shortcutMultiMappingInfo =new ShortcutMultiMappingInfo(shortcutLabels[i],null,shortcutKeyGroups);touchPadGroup.addItem(shortcutMultiMappingInfo);}return touchPadGroup;
}void showKeyboardShortcuts(int deviceId) {retrieveKeyCharacterMap(deviceId);mWindowManager.requestAppKeyboardShortcuts(new KeyboardShortcutsReceiver() {@Overridepublic void onKeyboardShortcutsReceived(final List<KeyboardShortcutGroup> result) {...//add keyboard touchpad usagemFullShortsGroup.add(SHORTCUT_TOUCHPAD_INDEX, mTouchPadGroup);...showKeyboardShortcutSearchList(mFullShortsGroup);}}, deviceId);
}
4. 添加按钮的点击事件
private void setButtonsDefaultStatus(View keyboardShortcutsView) {...mButtonTouchPad = keyboardShortcutsView.findViewById(R.id.shortcut_touch_pad);...// add keyboard touchpad usagemButtonTouchPad.setOnClickListener(v -> {setCurrentCategoryIndex(SHORTCUT_TOUCHPAD_INDEX);populateKeyboardShortcutSearchList(keyboardShortcutsView.findViewById(R.id.keyboard_shortcuts_container));});...mFullButtonList.add(mButtonTouchPad);...
}