Implement new shortcut search list UI

1. Add an EditText for shortcuts search purpose.
2. Add new shortcut categories.
3. Create N to 1 shortcut mapping mechanism.
4. Fn based shortcuts shouldn't be showed.

Demo New UI: go/shortcutlist_demo

Flag: SHORTCUT_LIST_SEARCH_LAYOUT
Bug: 259352579
Test: manual and atest
Change-Id: I688365d5c408ba07b457eec66b3b5cc497390498
This commit is contained in:
danielwbhuang
2022-11-11 15:22:31 +08:00
parent 262d2db520
commit e2b6205b03
24 changed files with 2271 additions and 32 deletions

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="48"
android:viewportHeight="48"
android:tint="?android:attr/textColorSecondary">
<path
android:fillColor="@android:color/white"
android:pathData="M39.8,41.95 L26.65,28.8Q25.15,30.1 23.15,30.825Q21.15,31.55 18.9,31.55Q13.5,31.55 9.75,27.8Q6,24.05 6,18.75Q6,13.45 9.75,9.7Q13.5,5.95 18.85,5.95Q24.15,5.95 27.875,9.7Q31.6,13.45 31.6,18.75Q31.6,20.9 30.9,22.9Q30.2,24.9 28.8,26.65L42,39.75ZM18.85,28.55Q22.9,28.55 25.75,25.675Q28.6,22.8 28.6,18.75Q28.6,14.7 25.75,11.825Q22.9,8.95 18.85,8.95Q14.75,8.95 11.875,11.825Q9,14.7 9,18.75Q9,22.8 11.875,25.675Q14.75,28.55 18.85,28.55Z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.0dp"
android:height="24.0dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?android:attr/textColorSecondary">
<path
android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
android:fillColor="@android:color/white"/>
</vector>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<inset
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<ripple
android:color="?android:attr/colorControlHighlight">
<item>
<shape android:shape="rectangle">
<corners android:radius="16dp"/>
<solid android:color="?androidprv:attr/colorSurface"/>
</shape>
</item>
</ripple>
</inset>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<inset
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<ripple
android:color="?android:attr/colorControlHighlight">
<item>
<shape android:shape="rectangle">
<corners android:radius="16dp"/>
<solid android:color="?androidprv:attr/colorAccentPrimary"/>
</shape>
</item>
</ripple>
</inset>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="?android:attr/colorBackground"/>
<corners android:topLeftRadius="16dp"
android:topRightRadius="16dp"
android:bottomLeftRadius="0dp"
android:bottomRightRadius="0dp"/>
</shape>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License
-->
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="?androidprv:attr/colorSurface" />
<corners android:radius="24dp" />
</shape>
</item>
</layer-list>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License
-->
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_marginTop="8dp"
android:layout_marginBottom="0dp"
style="@style/ShortcutHorizontalDivider" />

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/ksh_item_padding"
android:layout_marginLeft="0dp"
android:layout_marginRight="0dp"
android:scaleType="fitXY"
android:tint="?android:attr/textColorPrimary"
style="@style/ShortcutItemBackground" />

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/ksh_item_padding"
android:layout_marginStart="@dimen/ksh_item_margin_start"
style="@style/ShortcutItemBackground"
android:textColor="?android:attr/textColorPrimary"
android:singleLine="false"
android:gravity="center"
android:textSize="@dimen/ksh_item_text_size"/>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/ksh_item_padding"
android:layout_marginLeft="0dp"
android:layout_marginRight="0dp"
android:text="+"
style="@style/ShortcutItemBackground"
android:textColor="?android:attr/textColorPrimary"
android:singleLine="true"
android:gravity="center"
android:textSize="@dimen/ksh_item_text_size"
android:textAllCaps="true"/>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/ksh_item_padding"
android:layout_marginLeft="0dp"
android:layout_marginRight="0dp"
android:text="|"
style="@style/ShortcutItemBackground"
android:textColor="?android:attr/textColorPrimary"
android:singleLine="true"
android:gravity="center"
android:textSize="@dimen/ksh_item_text_size"
android:textAllCaps="true"/>

View File

@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="@drawable/shortcut_dialog_bg"
android:layout_width="@dimen/ksh_layout_width"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/shortcut_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:layout_gravity="center_horizontal"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/keyboard_shortcut_search_list_title"/>
<FrameLayout android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/keyboard_shortcuts_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="24dp"
android:layout_marginStart="49dp"
android:layout_marginEnd="49dp"
android:padding="16dp"
android:background="@drawable/shortcut_search_background"
android:drawableStart="@drawable/ic_shortcutlist_search"
android:drawablePadding="15dp"
android:singleLine="true"
android:textColor="?android:attr/textColorPrimary"
android:inputType="text"
android:textDirection="locale"
android:textAlignment="viewStart"
android:hint="@string/keyboard_shortcut_search_list_hint"
android:textColorHint="?android:attr/textColorTertiary" />
<ImageView
android:id="@+id/keyboard_shortcuts_search_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="24dp"
android:layout_marginBottom="24dp"
android:layout_marginEnd="49dp"
android:padding="16dp"
android:contentDescription="@string/keyboard_shortcut_clear_text"
android:src="@drawable/ic_shortcutlist_search_button_cancel" />
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="37dp"/>
<Button
android:id="@+id/shortcut_system"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
style="@style/ShortCutButton"
android:text="@string/keyboard_shortcut_search_category_system"/>
<Button
android:id="@+id/shortcut_input"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
style="@style/ShortCutButton"
android:text="@string/keyboard_shortcut_search_category_input"/>
<Button
android:id="@+id/shortcut_open_apps"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
style="@style/ShortCutButton"
android:text="@string/keyboard_shortcut_search_category_open_apps"/>
<Button
android:id="@+id/shortcut_specific_app"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
style="@style/ShortCutButton"
android:text="@string/keyboard_shortcut_search_category_current_app"/>
</LinearLayout>
<TextView
android:id="@+id/shortcut_search_no_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:layout_gravity="center_horizontal"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/keyboard_shortcut_search_list_no_result"/>
<ScrollView
android:id="@+id/keyboard_shortcuts_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp">
<LinearLayout
android:id="@+id/keyboard_shortcuts_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
</ScrollView>
<!-- Required for stretching to full available height when the items in the scroll view
occupy less space then the full height -->
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>

View File

@@ -118,6 +118,7 @@
<color name="ksh_application_group_color">#fff44336</color>
<color name="ksh_key_item_color">@color/material_grey_600</color>
<color name="ksh_key_item_background">@color/material_grey_100</color>
<color name="ksh_key_item_new_background">@color/transparent</color>
<color name="instant_apps_color">#ff4d5a64</color>

View File

@@ -1698,34 +1698,105 @@
<string name="keyboard_shortcut_group_system">System</string>
<!-- User visible title for the keyboard shortcut that takes the user to the home screen. -->
<string name="keyboard_shortcut_group_system_home">Home</string>
<!-- User visible title for the the keyboard shortcut that takes the user to the recents screen. -->
<!-- User visible title for the keyboard shortcut that takes the user to the recents screen. -->
<string name="keyboard_shortcut_group_system_recents">Recents</string>
<!-- User visible title for the the keyboard shortcut that triggers the back action. -->
<!-- User visible title for the keyboard shortcut that triggers the back action. -->
<string name="keyboard_shortcut_group_system_back">Back</string>
<!-- User visible title for the the keyboard shortcut that triggers the notification shade. -->
<!-- User visible title for the keyboard shortcut that triggers the notification shade. -->
<string name="keyboard_shortcut_group_system_notifications">Notifications</string>
<!-- User visible title for the the keyboard shortcut that triggers the keyboard shortcuts helper. -->
<!-- User visible title for the keyboard shortcut that triggers the keyboard shortcuts helper. -->
<string name="keyboard_shortcut_group_system_shortcuts_helper">Keyboard Shortcuts</string>
<!-- User visible title for the the keyboard shortcut that switches to the next hardware keyboard layout. [CHAR LIMIT=30] -->
<!-- User visible title for the keyboard shortcut that switches to the next hardware keyboard layout. -->
<string name="keyboard_shortcut_group_system_switch_input">Switch keyboard layout</string>
<!-- User visible title for the system-wide applications keyboard shortcuts list. -->
<!-- Content description for the clear text button in shortcut search list. [CHAR LIMIT=NONE] -->
<string name="keyboard_shortcut_clear_text">Clear text</string>
<!-- The title for keyboard shortcut search list [CHAR LIMIT=25] -->
<string name="keyboard_shortcut_search_list_title">Shortcuts</string>
<!-- The hint for keyboard shortcut search list [CHAR LIMIT=25] -->
<string name="keyboard_shortcut_search_list_hint">Search shortcuts</string>
<!-- The description for no shortcuts results [CHAR LIMIT=25] -->
<string name="keyboard_shortcut_search_list_no_result">No shortcuts found</string>
<!-- The title of system category in shortcut search list. [CHAR LIMIT=15] -->
<string name="keyboard_shortcut_search_category_system">System</string>
<!-- The title of input category in shortcut search list. [CHAR LIMIT=15] -->
<string name="keyboard_shortcut_search_category_input">Input</string>
<!-- The title of open apps category in shortcut search list. [CHAR LIMIT=15] -->
<string name="keyboard_shortcut_search_category_open_apps">Open apps</string>
<!-- The title of current app category in shortcut search list. [CHAR LIMIT=15] -->
<string name="keyboard_shortcut_search_category_current_app">Current app</string>
<!-- User visible title for the keyboard shortcut that triggers the notification shade. [CHAR LIMIT=70] -->
<string name="group_system_access_notification_shade">Access notification shade</string>
<!-- User visible title for the keyboard shortcut that takes a full screenshot. [CHAR LIMIT=70] -->
<string name="group_system_full_screenshot">Take a full screenshot</string>
<!-- User visible title for the keyboard shortcut that access list of system / apps shortcuts. [CHAR LIMIT=70] -->
<string name="group_system_access_system_app_shortcuts">Access list of system / apps shortcuts</string>
<!-- User visible title for the keyboard shortcut that goes back to previous state. [CHAR LIMIT=70] -->
<string name="group_system_go_back">Back: go back to previous state (back button)</string>
<!-- User visible title for the keyboard shortcut that takes the user to the home screen. [CHAR LIMIT=70] -->
<string name="group_system_access_home_screen">Access home screen</string>
<!-- User visible title for the keyboard shortcut that triggers overview of open apps. [CHAR LIMIT=70] -->
<string name="group_system_overview_open_apps">Overview of open apps</string>
<!-- User visible title for the keyboard shortcut that cycles through recent apps (forward). [CHAR LIMIT=70] -->
<string name="group_system_cycle_forward">Cycle through recent apps (forward)</string>
<!-- User visible title for the keyboard shortcut that cycles through recent apps (back). [CHAR LIMIT=70] -->
<string name="group_system_cycle_back">Cycle through recent apps (back)</string>
<!-- User visible title for the keyboard shortcut that accesses list of all apps and search. [CHAR LIMIT=70] -->
<string name="group_system_access_all_apps_search">Access list of all apps and search (i.e. Search/Launcher)</string>
<!-- User visible title for the keyboard shortcut that hides and (re)showes taskbar. [CHAR LIMIT=70] -->
<string name="group_system_hide_reshow_taskbar">Hide and (re)show taskbar</string>
<!-- User visible title for the keyboard shortcut that accesses system settings. [CHAR LIMIT=70] -->
<string name="group_system_access_system_settings">Access system settings</string>
<!-- User visible title for the keyboard shortcut that accesses Google Assistant. [CHAR LIMIT=70] -->
<string name="group_system_access_google_assistant">Access Google Assistant</string>
<!-- User visible title for the keyboard shortcut that locks screen. [CHAR LIMIT=70] -->
<string name="group_system_lock_screen">Lock screen</string>
<!-- User visible title for the keyboard shortcut that pulls up Notes app for quick memo. [CHAR LIMIT=70] -->
<string name="group_system_quick_memo">Pull up Notes app for quick memo</string>
<!-- User visible title for the system multitasking keyboard shortcuts list. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_system_multitasking">System multitasking</string>
<!-- User visible title for the keyboard shortcut that enters split screen with current app to RHS [CHAR LIMIT=70] -->
<string name="system_multitasking_rhs">Enter Split screen with current app to RHS</string>
<!-- User visible title for the keyboard shortcut that enters split screen with current app to LHS [CHAR LIMIT=70] -->
<string name="system_multitasking_lhs">Enter Split screen with current app to LHS</string>
<!-- User visible title for the keyboard shortcut that switches from split screen to full screen [CHAR LIMIT=70] -->
<string name="system_multitasking_full_screen">Switch from Split screen to full screen</string>
<!-- User visible title for the keyboard shortcut that replaces an app from one to another during split screen [CHAR LIMIT=70] -->
<string name="system_multitasking_replace">During Split screen: replace an app from one to another</string>
<!-- User visible title for the input keyboard shortcuts list. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_input">Input</string>
<!-- User visible title for the keyboard shortcut that switches input language (next language). [CHAR LIMIT=70] -->
<string name="input_switch_input_language_next">Switch input language (next language)</string>
<!-- User visible title for the keyboard shortcut that switches input language (previous language). [CHAR LIMIT=70] -->
<string name="input_switch_input_language_previous">Switch input language (previous language)</string>
<!-- User visible title for the keyboard shortcut that accesses emoji. [CHAR LIMIT=70] -->
<string name="input_access_emoji">Access emoji</string>
<!-- User visible title for the keyboard shortcut that accesses voice typing. [CHAR LIMIT=70] -->
<string name="input_access_voice_typing">Access voice typing</string>
<!-- User visible title for the system-wide applications keyboard shortcuts list. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_applications">Applications</string>
<!-- User visible title for the keyboard shortcut that takes the user to the assist app. -->
<!-- User visible title for the keyboard shortcut that takes the user to the assist app. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_applications_assist">Assist</string>
<!-- User visible title for the keyboard shortcut that takes the user to the browser app. -->
<string name="keyboard_shortcut_group_applications_browser">Browser</string>
<!-- User visible title for the keyboard shortcut that takes the user to the contacts app. -->
<!-- User visible title for the keyboard shortcut that takes the user to the browser app. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_applications_browser">Browser (Chrome as default)</string>
<!-- User visible title for the keyboard shortcut that takes the user to the contacts app. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_applications_contacts">Contacts</string>
<!-- User visible title for the keyboard shortcut that takes the user to the email app. -->
<string name="keyboard_shortcut_group_applications_email">Email</string>
<!-- User visible title for the keyboard shortcut that takes the user to the SMS messaging app. -->
<!-- User visible title for the keyboard shortcut that takes the user to the email app. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_applications_email">Email (Gmail as default)</string>
<!-- User visible title for the keyboard shortcut that takes the user to the SMS messaging app. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_applications_sms">SMS</string>
<!-- User visible title for the keyboard shortcut that takes the user to the music app. -->
<!-- User visible title for the keyboard shortcut that takes the user to the music app. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_applications_music">Music</string>
<!-- User visible title for the keyboard shortcut that takes the user to the YouTube app. -->
<!-- User visible title for the keyboard shortcut that takes the user to the calendar app. -->
<!-- User visible title for the keyboard shortcut that takes the user to the calendar app. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_applications_calendar">Calendar</string>
<!-- User visible title for the keyboard shortcut that takes the user to the calculator app. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_applications_calculator">Calculator</string>
<!-- User visible title for the keyboard shortcut that takes the user to the maps app. [CHAR LIMIT=70] -->
<string name="keyboard_shortcut_group_applications_maps">Maps</string>
<!-- SysUI Tuner: Label for screen about do not disturb settings [CHAR LIMIT=60] -->
<string name="volume_and_do_not_disturb">Do Not Disturb</string>

View File

@@ -1388,4 +1388,23 @@
<item name="android:lineHeight">@dimen/magnification_setting_button_line_height</item>
<item name="android:textAlignment">center</item>
</style>
<style name="ShortCutButton" parent="@android:style/Widget.Material.Button">
<item name="android:background">@drawable/shortcut_button_colored</item>
<item name="android:stateListAnimator">@null</item>
<item name="android:textSize">16sp</item>
<item name="android:padding">4dp</item>
<item name="android:textColor">?androidprv:attr/textColorSecondary</item>
</style>
<style name="ShortcutHorizontalDivider">
<item name="android:layout_width">120dp</item>
<item name="android:layout_height">1dp</item>
<item name="android:layout_gravity">center_horizontal</item>
<item name="android:background">?android:attr/dividerHorizontal</item>
</style>
<style name="ShortcutItemBackground">
<item name="android:background">@color/ksh_key_item_new_background</item>
</style>
</resources>

View File

@@ -47,6 +47,7 @@ import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeControllerImpl;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyboardShortcutsModule;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -100,7 +101,8 @@ import dagger.Provides;
QSModule.class,
ReferenceScreenshotModule.class,
StartCentralSurfacesModule.class,
VolumeModule.class
VolumeModule.class,
KeyboardShortcutsModule.class
})
public abstract class ReferenceSystemUIModule {

View File

@@ -63,6 +63,7 @@ import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AssistUtils;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
@@ -80,7 +81,8 @@ import java.util.List;
public final class KeyboardShortcuts {
private static final String TAG = KeyboardShortcuts.class.getSimpleName();
private static final Object sLock = new Object();
private static KeyboardShortcuts sInstance;
@VisibleForTesting static KeyboardShortcuts sInstance;
private WindowManager mWindowManager;
private final SparseArray<String> mSpecialCharacterNames = new SparseArray<>();
private final SparseArray<String> mModifierNames = new SparseArray<>();
@@ -94,7 +96,7 @@ public final class KeyboardShortcuts {
};
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final Context mContext;
@VisibleForTesting Context mContext;
private final IPackageManager mPackageManager;
private final OnClickListener mDialogCloseListener = new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
@@ -123,20 +125,26 @@ public final class KeyboardShortcuts {
}
};
private Dialog mKeyboardShortcutsDialog;
@VisibleForTesting Dialog mKeyboardShortcutsDialog;
private KeyCharacterMap mKeyCharacterMap;
private KeyCharacterMap mBackupKeyCharacterMap;
private KeyboardShortcuts(Context context) {
@VisibleForTesting
KeyboardShortcuts(Context context, WindowManager windowManager) {
this.mContext = new ContextThemeWrapper(
context, android.R.style.Theme_DeviceDefault_Settings);
this.mPackageManager = AppGlobals.getPackageManager();
if (windowManager != null) {
this.mWindowManager = windowManager;
} else {
this.mWindowManager = mContext.getSystemService(WindowManager.class);
}
loadResources(context);
}
private static KeyboardShortcuts getInstance(Context context) {
if (sInstance == null) {
sInstance = new KeyboardShortcuts(context);
sInstance = new KeyboardShortcuts(context, null);
}
return sInstance;
}
@@ -371,10 +379,10 @@ public final class KeyboardShortcuts {
mKeyCharacterMap = mBackupKeyCharacterMap;
}
private void showKeyboardShortcuts(int deviceId) {
@VisibleForTesting
void showKeyboardShortcuts(int deviceId) {
retrieveKeyCharacterMap(deviceId);
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
wm.requestAppKeyboardShortcuts(new KeyboardShortcutsReceiver() {
mWindowManager.requestAppKeyboardShortcuts(new KeyboardShortcutsReceiver() {
@Override
public void onKeyboardShortcutsReceived(
final List<KeyboardShortcutGroup> result) {

View File

@@ -19,16 +19,38 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.shared.recents.utilities.Utilities;
import javax.inject.Inject;
/**
* Receiver for the Keyboard Shortcuts Helper.
*/
public class KeyboardShortcutsReceiver extends BroadcastReceiver {
private boolean mIsShortcutListSearchEnabled;
@Inject
public KeyboardShortcutsReceiver(FeatureFlags featureFlags) {
mIsShortcutListSearchEnabled = featureFlags.isEnabled(Flags.SHORTCUT_LIST_SEARCH_LAYOUT);
}
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
KeyboardShortcuts.show(context, -1 /* deviceId unknown */);
} else if (Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
KeyboardShortcuts.dismiss();
if (mIsShortcutListSearchEnabled && Utilities.isTablet(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 {
if (Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
KeyboardShortcuts.show(context, -1 /* deviceId unknown */);
} else if (Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
KeyboardShortcuts.dismiss();
}
}
}
}

View File

@@ -186,11 +186,13 @@ import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CircleReveal;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.KeyboardShortcutListSearch;
import com.android.systemui.statusbar.KeyboardShortcuts;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.LiftReveal;
@@ -299,6 +301,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private CentralSurfacesCommandQueueCallbacks mCommandQueueCallbacks;
private float mTransitionToFullShadeProgress = 0f;
private NotificationListContainer mNotifListContainer;
private boolean mIsShortcutListSearchEnabled;
private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
new KeyguardStateController.Callback() {
@@ -833,6 +836,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mCameraLauncherLazy = cameraLauncherLazy;
mAlternateBouncerInteractor = alternateBouncerInteractor;
mUserTracker = userTracker;
mIsShortcutListSearchEnabled = featureFlags.isEnabled(Flags.SHORTCUT_LIST_SEARCH_LAYOUT);
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mStartingSurfaceOptional = startingSurfaceOptional;
@@ -2546,7 +2550,11 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
String action = intent.getAction();
String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
KeyboardShortcuts.dismiss();
if (mIsShortcutListSearchEnabled && Utilities.isTablet(mContext)) {
KeyboardShortcutListSearch.dismiss();
} else {
KeyboardShortcuts.dismiss();
}
mRemoteInputManager.closeRemoteInputs();
if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) {
int flags = CommandQueue.FLAG_EXCLUDE_NONE;
@@ -3893,11 +3901,19 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
protected void toggleKeyboardShortcuts(int deviceId) {
KeyboardShortcuts.toggle(mContext, deviceId);
if (mIsShortcutListSearchEnabled && Utilities.isTablet(mContext)) {
KeyboardShortcutListSearch.toggle(mContext, deviceId);
} else {
KeyboardShortcuts.toggle(mContext, deviceId);
}
}
protected void dismissKeyboardShortcuts() {
KeyboardShortcuts.dismiss();
if (mIsShortcutListSearchEnabled && Utilities.isTablet(mContext)) {
KeyboardShortcutListSearch.dismiss();
} else {
KeyboardShortcuts.dismiss();
}
}
/**

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.view.WindowManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class KeyboardShortcutListSearchTest extends SysuiTestCase {
@Rule public MockitoRule mockito = MockitoJUnit.rule();
private static int DEVICE_ID = 1;
private KeyboardShortcutListSearch mKeyboardShortcutListSearch;
@Mock private BottomSheetDialog mBottomSheetDialog;
@Mock WindowManager mWindowManager;
@Before
public void setUp() {
mKeyboardShortcutListSearch = new KeyboardShortcutListSearch(mContext, mWindowManager);
mKeyboardShortcutListSearch.sInstance = mKeyboardShortcutListSearch;
mKeyboardShortcutListSearch.mKeyboardShortcutsBottomSheetDialog = mBottomSheetDialog;
mKeyboardShortcutListSearch.mContext = mContext;
}
@Test
public void toggle_isShowingTrue_instanceShouldBeNull() {
when(mBottomSheetDialog.isShowing()).thenReturn(true);
mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID);
assertThat(mKeyboardShortcutListSearch.sInstance).isNull();
}
@Test
public void toggle_isShowingFalse_showKeyboardShortcuts() {
when(mBottomSheetDialog.isShowing()).thenReturn(false);
mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID);
verify(mWindowManager).requestAppKeyboardShortcuts(any(), anyInt());
}
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright (c) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Intent;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.shared.recents.utilities.Utilities;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.mockito.quality.Strictness;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class KeyboardShortcutsReceiverTest extends SysuiTestCase {
@Rule public MockitoRule mockito = MockitoJUnit.rule();
private KeyboardShortcutsReceiver mKeyboardShortcutsReceiver;
private Intent mIntent;
private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
@Mock private KeyboardShortcuts mKeyboardShortcuts;
@Mock private KeyboardShortcutListSearch mKeyboardShortcutListSearch;
@Before
public void setUp() {
mIntent = new Intent(Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS);
mKeyboardShortcuts.mContext = mContext;
mKeyboardShortcutListSearch.mContext = mContext;
KeyboardShortcuts.sInstance = mKeyboardShortcuts;
KeyboardShortcutListSearch.sInstance = mKeyboardShortcutListSearch;
}
@Test
public void onReceive_whenFlagOffDeviceIsTablet_showKeyboardShortcuts() {
MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
.spyStatic(Utilities.class)
.strictness(Strictness.LENIENT)
.startMocking();
mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false);
mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags));
when(Utilities.isTablet(mContext)).thenReturn(true);
mKeyboardShortcutsReceiver.onReceive(mContext, mIntent);
verify(mKeyboardShortcuts).showKeyboardShortcuts(anyInt());
verify(mKeyboardShortcutListSearch, never()).showKeyboardShortcuts(anyInt());
mockitoSession.finishMocking();
}
@Test
public void onReceive_whenFlagOffDeviceIsNotTablet_showKeyboardShortcuts() {
MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
.spyStatic(Utilities.class)
.strictness(Strictness.LENIENT)
.startMocking();
mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false);
mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags));
when(Utilities.isTablet(mContext)).thenReturn(false);
mKeyboardShortcutsReceiver.onReceive(mContext, mIntent);
verify(mKeyboardShortcuts).showKeyboardShortcuts(anyInt());
verify(mKeyboardShortcutListSearch, never()).showKeyboardShortcuts(anyInt());
mockitoSession.finishMocking();
}
@Test
public void onReceive_whenFlagOnDeviceIsTablet_showKeyboardShortcutListSearch() {
MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
.spyStatic(Utilities.class)
.strictness(Strictness.LENIENT)
.startMocking();
mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, true);
mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags));
when(Utilities.isTablet(mContext)).thenReturn(true);
mKeyboardShortcutsReceiver.onReceive(mContext, mIntent);
verify(mKeyboardShortcuts, never()).showKeyboardShortcuts(anyInt());
verify(mKeyboardShortcutListSearch).showKeyboardShortcuts(anyInt());
mockitoSession.finishMocking();
}
@Test
public void onReceive_whenFlagOnDeviceIsNotTablet_showKeyboardShortcuts() {
MockitoSession mockitoSession = ExtendedMockito.mockitoSession()
.spyStatic(Utilities.class)
.strictness(Strictness.LENIENT)
.startMocking();
mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, true);
mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags));
when(Utilities.isTablet(mContext)).thenReturn(false);
mKeyboardShortcutsReceiver.onReceive(mContext, mIntent);
verify(mKeyboardShortcuts).showKeyboardShortcuts(anyInt());
verify(mKeyboardShortcutListSearch, never()).showKeyboardShortcuts(anyInt());
mockitoSession.finishMocking();
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Dialog;
import android.view.WindowManager;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class KeyboardShortcutsTest extends SysuiTestCase {
@Rule public MockitoRule mockito = MockitoJUnit.rule();
private static int DEVICE_ID = 1;
private KeyboardShortcuts mKeyboardShortcuts;
@Mock private Dialog mDialog;
@Mock WindowManager mWindowManager;
@Before
public void setUp() {
mKeyboardShortcuts = new KeyboardShortcuts(mContext, mWindowManager);
mKeyboardShortcuts.sInstance = mKeyboardShortcuts;
mKeyboardShortcuts.mKeyboardShortcutsDialog = mDialog;
mKeyboardShortcuts.mContext = mContext;
}
@Test
public void toggle_isShowingTrue_instanceShouldBeNull() {
when(mDialog.isShowing()).thenReturn(true);
mKeyboardShortcuts.toggle(mContext, DEVICE_ID);
assertThat(mKeyboardShortcuts.sInstance).isNull();
}
@Test
public void toggle_isShowingFalse_showKeyboardShortcuts() {
when(mDialog.isShowing()).thenReturn(false);
mKeyboardShortcuts.toggle(mContext, DEVICE_ID);
verify(mWindowManager).requestAppKeyboardShortcuts(any(), anyInt());
}
}

View File

@@ -327,6 +327,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
// CentralSurfacesImpl's runtime flag check fails if the flag is absent.
// This value is unused, because test manifest is opted in.
mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI, false);
// Set default value to avoid IllegalStateException.
mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false);
IThermalService thermalService = mock(IThermalService.class);
mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService,