From adf5e308c32086b61fa5fb1b1c7ac4cbe891f9ee Mon Sep 17 00:00:00 2001 From: Riley Jones Date: Thu, 8 Dec 2022 21:36:39 +0000 Subject: [PATCH] Initial porting of View and Res for AccessibilityMenu Bug: 261252772 Test: adb install the APK, then enable the menu from the Settings Accessibility page Change-Id: I20b11bcaa631a5c78f00e870bef5d25d02fe86dc --- .../accessibilitymenu/Android.bp | 11 + .../res/color/footer_icon_tint_color.xml | 6 + .../res/drawable/a11ymenu_intro.png | Bin 0 -> 7033 bytes .../footer_button_background_left.xml | 13 + .../footer_button_background_right.xml | 13 + .../res/drawable/ic_a11y_menu_round.xml | 20 + .../res/drawable/ic_add_32dp.xml | 5 + .../res/drawable/ic_arrow_back_24dp.xml | 9 + .../res/drawable/ic_arrow_forward_24dp.xml | 9 + .../res/drawable/ic_logo_a11y_menu.xml | 33 ++ .../res/drawable/ic_logo_assistant_32dp.xml | 8 + .../res/drawable/shadow_0deg.9.png | Bin 0 -> 273 bytes .../res/drawable/shadow_270deg.9.png | Bin 0 -> 275 bytes .../res/drawable/shadow_90deg.9.png | Bin 0 -> 276 bytes .../res/drawable/view_background.xml | 5 + .../res/layout/footerlayout_switch_page.xml | 59 +++ .../res/layout/grid_item.xml | 32 ++ .../res/layout/grid_view.xml | 11 + .../res/layout/paged_menu.xml | 27 ++ .../res/values-land/dimens.xml | 12 + .../res/values-night/colors.xml | 23 ++ .../res/values-night/styles.xml | 24 ++ .../accessibilitymenu/res/values/bool.xml | 5 + .../accessibilitymenu/res/values/colors.xml | 25 ++ .../accessibilitymenu/res/values/dimens.xml | 25 ++ .../res/values/donottranslate.xml | 17 + .../accessibilitymenu/res/values/strings.xml | 73 ++++ .../accessibilitymenu/res/values/styles.xml | 31 ++ .../res/xml/accessibilitymenu_service.xml | 10 +- .../AccessibilityMenuService.java | 27 +- .../model/A11yMenuShortcut.java | 91 +++++ .../utils/ShortcutDrawableUtils.java | 98 +++++ .../view/A11yMenuAdapter.java | 145 +++++++ .../view/A11yMenuFooter.java | 125 ++++++ .../view/A11yMenuOverlayLayout.java | 269 +++++++++++++ .../view/A11yMenuViewPager.java | 356 ++++++++++++++++++ .../view/ViewPagerAdapter.java | 70 ++++ 37 files changed, 1684 insertions(+), 3 deletions(-) create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/color/footer_icon_tint_color.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/drawable/a11ymenu_intro.png create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/drawable/footer_button_background_left.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/drawable/footer_button_background_right.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_a11y_menu_round.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_add_32dp.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_arrow_back_24dp.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_arrow_forward_24dp.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_menu.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_assistant_32dp.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/drawable/shadow_0deg.9.png create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/drawable/shadow_270deg.9.png create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/drawable/shadow_90deg.9.png create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/drawable/view_background.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_view.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/layout/paged_menu.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/values-land/dimens.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/values-night/styles.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/values/bool.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/values/dimens.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/values/donottranslate.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/values/strings.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/utils/ShortcutDrawableUtils.java create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuFooter.java create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java create mode 100644 packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java diff --git a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp index a494f5e086ae8..0b1a3e272d01e 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp +++ b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp @@ -20,6 +20,17 @@ package { android_app { name: "AccessibilityMenu", + + static_libs: [ + "androidx.coordinatorlayout_coordinatorlayout", + "androidx.core_core", + "androidx.viewpager_viewpager", + ], + + uses_libs: [ + "org.apache.http.legacy", + ], + srcs: [ "src/**/*.java", ], diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/color/footer_icon_tint_color.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/color/footer_icon_tint_color.xml new file mode 100644 index 0000000000000..c89e4c3188050 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/color/footer_icon_tint_color.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/a11ymenu_intro.png b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/a11ymenu_intro.png new file mode 100644 index 0000000000000000000000000000000000000000..6149ee4b2365bddb0a023e09950db53ab3faaec9 GIT binary patch literal 7033 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 literal 0 HcmV?d00001 diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/footer_button_background_left.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/footer_button_background_left.xml new file mode 100644 index 0000000000000..5ff245d6a11e7 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/footer_button_background_left.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/footer_button_background_right.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/footer_button_background_right.xml new file mode 100644 index 0000000000000..5ff245d6a11e7 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/footer_button_background_right.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_a11y_menu_round.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_a11y_menu_round.xml new file mode 100644 index 0000000000000..a2eaf95dcef0d --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_a11y_menu_round.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_add_32dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_add_32dp.xml new file mode 100644 index 0000000000000..7e1262c2b4e7e --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_add_32dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_arrow_back_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_arrow_back_24dp.xml new file mode 100644 index 0000000000000..f6af270095c30 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_arrow_back_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_arrow_forward_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_arrow_forward_24dp.xml new file mode 100644 index 0000000000000..2f7b632d6adc2 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_arrow_forward_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_menu.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_menu.xml new file mode 100644 index 0000000000000..79e0e08d88992 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_menu.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_assistant_32dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_assistant_32dp.xml new file mode 100644 index 0000000000000..ebeebf81eedcf --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_assistant_32dp.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/shadow_0deg.9.png b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/shadow_0deg.9.png new file mode 100644 index 0000000000000000000000000000000000000000..b0d169642461186787ea372a2e96293897980b39 GIT binary patch literal 273 zcmeAS@N?(olHy`uVBq!ia0vp^CxG}Y3p0@9->oDEq*MZYLR^6~5HvJ2gocKCdU`rI z1h~7qJ3Bi&IXO8vxY;^8|NsBLqD18oP!V@YkY6yve!4Xs_*&aVUtPxN$g z45?sDQjj#@;+nLk-LvzVLu2F3)@KJA8cp}kIkrHjp)vWImQ?dpUY*i31gLP?+GTFb~cFtHa=(Ft5-_6j@>^z`zg^ Xc;oh0Mz;k(I~Y7&{an^LB{Ts5L*7c5 literal 0 HcmV?d00001 diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/shadow_270deg.9.png b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/shadow_270deg.9.png new file mode 100644 index 0000000000000000000000000000000000000000..b777ffee62fd9697e4700ba36d975e8afa6f1f49 GIT binary patch literal 275 zcmeAS@N?(olHy`uVBq!ia0vp^XMy+x3p0@18DH@dNT~<-gt!7}Ao&0Pzmt=bgM&k8 zXlO%2gQKH|ySuxmr>C8Zi?g${t+TTc^R4?pb-X1(e!)Q1$^vXm>O$87EqV@BcnUAOE-}}iht!#hCjnixW`%}Zz Rp8#FP;OXk;vd$@?2>_}eUR?kH literal 0 HcmV?d00001 diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/shadow_90deg.9.png b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/shadow_90deg.9.png new file mode 100644 index 0000000000000000000000000000000000000000..998bd90374629d1eed54994a5c683d7689c74cd4 GIT binary patch literal 276 zcmeAS@N?(olHy`uVBq!ia0vp^XMy+x3p0@18DH@dNT~<-gt!7}AZTc42n`K&aBy&P za{B-Ozo)0CySuxiqldGzvz?2Jt+VrE#r*3)b-X1(e!&d?nb-uB|Fa9J3u|iC6t+$S zO7wfWIEGX(CMhr*aB*=>Skrk@MW^?hLqp@rB&h{+t+z2TFg0mZcrA!&)pU%l`mm$n zC$G + + diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml new file mode 100644 index 0000000000000..658c03bd388ff --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml new file mode 100644 index 0000000000000..39e5a8c6876b6 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml @@ -0,0 +1,32 @@ + + + + + + + + diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_view.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_view.xml new file mode 100644 index 0000000000000..c198443415dd4 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_view.xml @@ -0,0 +1,11 @@ + + + diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/paged_menu.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/paged_menu.xml new file mode 100644 index 0000000000000..28a633e5d17ae --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/paged_menu.xml @@ -0,0 +1,27 @@ + + + + + + + + + diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-land/dimens.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-land/dimens.xml new file mode 100644 index 0000000000000..69f09343b1d1f --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-land/dimens.xml @@ -0,0 +1,12 @@ + + + 0dp + 388dp + 45dp + 45dp + 1dp + + + 4dp + + diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml new file mode 100644 index 0000000000000..33c0cca5131ea --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/colors.xml @@ -0,0 +1,23 @@ + + + + #dadce0 + #78d9ec + #d9affe + #f0a5dd + #85e4a0 + #7ae3d4 + #f496ac + #adcbff + #F1F3F4 + #fdd663 + + #10FFFFFF + + #313235 + #E8EAED + #E8EAED + #5F6368 + #202124 + + diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/styles.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/styles.xml new file mode 100644 index 0000000000000..81b3152375ff7 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-night/styles.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/bool.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/bool.xml new file mode 100644 index 0000000000000..2f9d6b5c8a198 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/bool.xml @@ -0,0 +1,5 @@ + + + true + + \ No newline at end of file diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml new file mode 100644 index 0000000000000..36d1fc1263c44 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/colors.xml @@ -0,0 +1,25 @@ + + + + #757575 + #2196F3 + #5806C9 + #AD2EC6 + #0F9D58 + #01A2A0 + #F15B8D + #26459C + #F1F3F4 + #E59810 + #1a73e8 + + #1f000000 + + @android:color/white + @android:color/black + @android:color/black + #ddd + @android:color/white + + #3AA757 + diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/dimens.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/dimens.xml new file mode 100644 index 0000000000000..7ed18977cd542 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/dimens.xml @@ -0,0 +1,25 @@ + + + + 22dp + @dimen/custom_match_parent + 60dp + 60dp + 2dp + 4dp + -1px + + + 2dp + 10dp + 48dp + 8dp + + + 24dp + + + 18sp + 14sp + + diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/donottranslate.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/donottranslate.xml new file mode 100644 index 0000000000000..0c25ec4353a5d --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/donottranslate.xml @@ -0,0 +1,17 @@ + + + + accessibility_menu_user_shortcuts + + pref_user_shortcuts_key + + [] + + + + pref_large_buttons + + + pref_help + + diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/strings.xml new file mode 100644 index 0000000000000..30fd0173ff3fc --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/strings.xml @@ -0,0 +1,73 @@ + + + + + Accessibility Menu + + + The Accessibility Menu provides a large on-screen menu to control your device. You can lock your device, control volume and brightness, take screenshots, and more. + + + Assistant + + Google Assistant + + Accessibility Settings + + Volume + + Volume controls + + Power + + Power options + + Recent apps + + Lock screen + + Quick Settings + + Notifications + + Screenshot + + Take screenshot + + Volume up + Volume down + + Brightness up + Brightness down + + Go to previous screen + Go to next screen + + + The Accessibility Menu provides a large on-screen menu to control your device. You can lock your device, control volume and brightness, take screenshots, and more. + + + Control device via large menu + + + + Accessibility Menu Settings + + + Large buttons + + Increase size of Accessibility Menu Buttons + + Help & feedback + + Help + + + Brightness %1$s %% + + Music volume %1$s %% + + + Open Source Licenses + + diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml new file mode 100644 index 0000000000000..a2cf26730960e --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml @@ -0,0 +1,31 @@ + + + + + + + + + + diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/xml/accessibilitymenu_service.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/xml/accessibilitymenu_service.xml index 96882d335d4b8..3dbbb1a658c9d 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/res/xml/accessibilitymenu_service.xml +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/xml/accessibilitymenu_service.xml @@ -13,4 +13,12 @@ See the License for the specific language governing permissions and limitations under the License. --> - \ No newline at end of file + \ No newline at end of file diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java index 8b759004f6575..5c4fdcc0e5d89 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/AccessibilityMenuService.java @@ -17,16 +17,39 @@ package com.android.systemui.accessibility.accessibilitymenu; import android.accessibilityservice.AccessibilityService; +import android.view.MotionEvent; +import android.view.View; import android.view.accessibility.AccessibilityEvent; +import com.android.systemui.accessibility.accessibilitymenu.view.A11yMenuOverlayLayout; + /** @hide */ -public class AccessibilityMenuService extends AccessibilityService { +public class AccessibilityMenuService extends AccessibilityService implements View.OnTouchListener { + private static final String TAG = "A11yMenuService"; + + private A11yMenuOverlayLayout mA11yMenuLayout; @Override - public void onAccessibilityEvent(AccessibilityEvent event) { + public void onCreate() { + super.onCreate(); } + @Override + protected void onServiceConnected() { + mA11yMenuLayout = new A11yMenuOverlayLayout(this); + super.onServiceConnected(); + mA11yMenuLayout.toggleVisibility(); + } + + @Override + public void onAccessibilityEvent(AccessibilityEvent event) {} + @Override public void onInterrupt() { } + + @Override + public boolean onTouch(View v, MotionEvent event) { + return false; + } } diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java new file mode 100644 index 0000000000000..fa42e61899fd7 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java @@ -0,0 +1,91 @@ +/* + * 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. + */ +package com.android.systemui.accessibility.accessibilitymenu.model; + +import com.android.systemui.accessibility.accessibilitymenu.R; + +/** Provides a data structure for a11y menu shortcuts. */ +public class A11yMenuShortcut { + + public enum ShortcutId { + UNSPECIFIED_ID_VALUE, + ID_ASSISTANT_VALUE, + ID_A11YSETTING_VALUE, + ID_POWER_VALUE, + ID_VOLUME_DOWN_VALUE, + ID_VOLUME_UP_VALUE, + ID_RECENT_VALUE, + ID_BRIGHTNESS_DOWN_VALUE, + ID_BRIGHTNESS_UP_VALUE, + ID_LOCKSCREEN_VALUE, + ID_QUICKSETTING_VALUE, + ID_NOTIFICATION_VALUE, + ID_SCREENSHOT_VALUE + } + + private static final String TAG = "A11yMenuShortcut"; + + /** Shortcut id used to identify. */ + private int mShortcutId = ShortcutId.UNSPECIFIED_ID_VALUE.ordinal(); + + // Resource IDs of shortcut button and label. + public int imageSrc; + public int imageColor; + public int imgContentDescription; + public int labelText; + + public A11yMenuShortcut(int id) { + setId(id); + } + + /** + * Sets Id to shortcut, checks the value first and updates shortcut resources. It will set id to + * + * @param id id set to shortcut + */ + public void setId(int id) { + mShortcutId = id; + + // TODO(jonesriley) load the proper resources based on id + imageSrc = R.drawable.ic_logo_assistant_32dp; + imageColor = android.R.color.darker_gray; + imgContentDescription = R.string.empty_content; + labelText = R.string.empty_content; + } + + public int getId() { + return mShortcutId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof A11yMenuShortcut)) { + return false; + } + + A11yMenuShortcut targetObject = (A11yMenuShortcut) o; + + return mShortcutId == targetObject.mShortcutId; + } + + @Override + public int hashCode() { + return mShortcutId; + } +} diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/utils/ShortcutDrawableUtils.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/utils/ShortcutDrawableUtils.java new file mode 100644 index 0000000000000..28ba4b54107f2 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/utils/ShortcutDrawableUtils.java @@ -0,0 +1,98 @@ +/* + * 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. + */ +package com.android.systemui.accessibility.accessibilitymenu.utils; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.drawable.AdaptiveIconDrawable; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.graphics.drawable.RippleDrawable; + +import com.android.systemui.accessibility.accessibilitymenu.R; + +/** Creates background drawable for a11y menu shortcut. */ +public class ShortcutDrawableUtils { + + /** + * To make the circular background of shortcut icons have higher resolution. The higher value of + * LENGTH is, the higher resolution of the circular background are. + */ + private static final int LENGTH = 480; + + private static final int RADIUS = LENGTH / 2; + private static final int COORDINATE = LENGTH / 2; + private static final int RIPPLE_COLOR_ID = R.color.ripple_material_color; + + private final Context mContext; + private final ColorStateList mRippleColorStateList; + + // Placeholder of drawable to prevent NullPointerException + private final ColorDrawable mTransparentDrawable = new ColorDrawable(Color.TRANSPARENT); + + public ShortcutDrawableUtils(Context context) { + this.mContext = context; + + int rippleColor = context.getColor(RIPPLE_COLOR_ID); + mRippleColorStateList = ColorStateList.valueOf(rippleColor); + } + + /** + * Creates a circular drawable in specific color for shortcut. + * + * @param colorResId color resource ID + * @return drawable circular drawable + */ + public Drawable createCircularDrawable(int colorResId) { + Bitmap output = Bitmap.createBitmap(LENGTH, LENGTH, Config.ARGB_8888); + Canvas canvas = new Canvas(output); + int color = mContext.getColor(colorResId); + Paint paint = new Paint(); + paint.setColor(color); + paint.setStrokeCap(Paint.Cap.ROUND); + paint.setStyle(Style.FILL); + canvas.drawCircle(COORDINATE, COORDINATE, RADIUS, paint); + + BitmapDrawable drawable = new BitmapDrawable(mContext.getResources(), output); + return drawable; + } + + /** + * Creates an adaptive icon drawable in specific color for shortcut. + * + * @param colorResId color resource ID + * @return drawable for adaptive icon + */ + public Drawable createAdaptiveIconDrawable(int colorResId) { + Drawable circleLayer = createCircularDrawable(colorResId); + RippleDrawable rippleLayer = new RippleDrawable(mRippleColorStateList, null, null); + + AdaptiveIconDrawable adaptiveIconDrawable = + new AdaptiveIconDrawable(circleLayer, mTransparentDrawable); + + Drawable[] layers = {adaptiveIconDrawable, rippleLayer}; + LayerDrawable drawable = new LayerDrawable(layers); + return drawable; + } +} diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java new file mode 100644 index 0000000000000..e3401a9a79151 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java @@ -0,0 +1,145 @@ +/* + * 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.accessibility.accessibilitymenu.view; + +import android.graphics.Rect; +import android.view.LayoutInflater; +import android.view.TouchDelegate; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageButton; +import android.widget.TextView; + +import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService; +import com.android.systemui.accessibility.accessibilitymenu.R; +import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut; +import com.android.systemui.accessibility.accessibilitymenu.utils.ShortcutDrawableUtils; + +import java.util.List; + +/** GridView Adapter for a11y menu overlay. */ +public class A11yMenuAdapter extends BaseAdapter { + + // The large scale of shortcut icon and label. + private static final float LARGE_BUTTON_SCALE = 1.5f; + private final int mLargeTextSize; + + private final AccessibilityMenuService mService; + private final LayoutInflater mInflater; + private final List mShortcutDataList; + private final ShortcutDrawableUtils mShortcutDrawableUtils; + + public A11yMenuAdapter( + AccessibilityMenuService service, List shortcutDataList) { + this.mService = service; + this.mShortcutDataList = shortcutDataList; + mInflater = LayoutInflater.from(service); + + mShortcutDrawableUtils = new ShortcutDrawableUtils(service); + + mLargeTextSize = + service.getResources().getDimensionPixelOffset(R.dimen.large_label_text_size); + } + + @Override + public int getCount() { + return mShortcutDataList.size(); + } + + @Override + public Object getItem(int position) { + return mShortcutDataList.get(position); + } + + @Override + public long getItemId(int position) { + return mShortcutDataList.get(position).getId(); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + convertView = mInflater.inflate(R.layout.grid_item, null); + + A11yMenuShortcut shortcutItem = (A11yMenuShortcut) getItem(position); + // Sets shortcut icon and label resource. + configureShortcutView(convertView, shortcutItem); + + expandIconTouchArea(convertView); + setActionForMenuShortcut(convertView); + return convertView; + } + + /** + * Expand shortcut icon touch area to the border of grid item. + * The height is from the top of icon to the bottom of label. + * The width is from the left border of grid item to the right border of grid item. + */ + private void expandIconTouchArea(View convertView) { + ImageButton shortcutIconButton = convertView.findViewById(R.id.shortcutIconBtn); + TextView shortcutLabel = convertView.findViewById(R.id.shortcutLabel); + + shortcutIconButton.post( + () -> { + Rect iconHitRect = new Rect(); + shortcutIconButton.getHitRect(iconHitRect); + Rect labelHitRect = new Rect(); + shortcutLabel.getHitRect(labelHitRect); + + final int widthAdjustment = iconHitRect.left; + iconHitRect.left = 0; + iconHitRect.right += widthAdjustment; + iconHitRect.top = 0; + iconHitRect.bottom = labelHitRect.bottom; + ((View) shortcutIconButton.getParent()) + .setTouchDelegate(new TouchDelegate(iconHitRect, shortcutIconButton)); + }); + } + + private void setActionForMenuShortcut(View convertView) { + ImageButton shortcutIconButton = convertView.findViewById(R.id.shortcutIconBtn); + + shortcutIconButton.setOnClickListener( + (View v) -> { + // Handles shortcut click event by AccessibilityMenuService. + // service.handleClick(v); + }); + } + + private void configureShortcutView(View convertView, A11yMenuShortcut shortcutItem) { + ImageButton shortcutIconButton = convertView.findViewById(R.id.shortcutIconBtn); + TextView shortcutLabel = convertView.findViewById(R.id.shortcutLabel); + + // TODO: Enlarge shortcut icon & label when large button setting is on. + + if (shortcutItem.getId() == A11yMenuShortcut.ShortcutId.UNSPECIFIED_ID_VALUE.ordinal()) { + // Sets empty shortcut icon and label when the shortcut is ADD_ITEM. + shortcutIconButton.setImageResource(android.R.color.transparent); + shortcutIconButton.setBackground(null); + } else { + // Sets shortcut ID as tagId, to handle menu item click in AccessibilityMenuService. + shortcutIconButton.setTag(shortcutItem.getId()); + shortcutIconButton.setContentDescription( + mService.getString(shortcutItem.imgContentDescription)); + shortcutLabel.setText(shortcutItem.labelText); + shortcutIconButton.setImageResource(shortcutItem.imageSrc); + + shortcutIconButton.setBackground( + mShortcutDrawableUtils.createAdaptiveIconDrawable(shortcutItem.imageColor)); + } + } +} diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuFooter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuFooter.java new file mode 100644 index 0000000000000..20c63df885d26 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuFooter.java @@ -0,0 +1,125 @@ +/* + * 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.accessibility.accessibilitymenu.view; + +import android.graphics.Rect; +import android.view.TouchDelegate; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.view.ViewTreeObserver.OnGlobalLayoutListener; +import android.widget.ImageButton; + +import androidx.annotation.Nullable; + +import com.android.systemui.accessibility.accessibilitymenu.R; + +/** + * This class is for Accessibility menu footer layout. Handles switching between a11y menu pages. + */ +public class A11yMenuFooter { + + /** Provides an interface for footer of a11yMenu. */ + public interface A11yMenuFooterCallBack { + + /** Calls back when user clicks the left button. */ + void onLeftButtonClicked(); + + /** Calls back when user clicks the right button. */ + void onRightButtonClicked(); + } + + private final FooterButtonClickListener mFooterButtonClickListener; + + private ImageButton mPreviousPageBtn; + private ImageButton mNextPageBtn; + private View mTopListDivider; + private View mBottomListDivider; + private final A11yMenuFooterCallBack mCallBack; + + public A11yMenuFooter(ViewGroup menuLayout, A11yMenuFooterCallBack callBack) { + this.mCallBack = callBack; + mFooterButtonClickListener = new FooterButtonClickListener(); + configureFooterLayout(menuLayout); + } + + public @Nullable ImageButton getPreviousPageBtn() { + return mPreviousPageBtn; + } + + public @Nullable ImageButton getNextPageBtn() { + return mNextPageBtn; + } + + private void configureFooterLayout(ViewGroup menuLayout) { + ViewGroup footerContainer = menuLayout.findViewById(R.id.footerlayout); + footerContainer.setVisibility(View.VISIBLE); + + mPreviousPageBtn = menuLayout.findViewById(R.id.menu_prev_button); + mNextPageBtn = menuLayout.findViewById(R.id.menu_next_button); + mTopListDivider = menuLayout.findViewById(R.id.top_listDivider); + mBottomListDivider = menuLayout.findViewById(R.id.bottom_listDivider); + + // Registers listeners for footer buttons. + setListener(mPreviousPageBtn); + setListener(mNextPageBtn); + + menuLayout + .getViewTreeObserver() + .addOnGlobalLayoutListener( + new OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + menuLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this); + expandBtnTouchArea(mPreviousPageBtn, menuLayout); + expandBtnTouchArea(mNextPageBtn, (View) mNextPageBtn.getParent()); + } + }); + } + + private void expandBtnTouchArea(ImageButton btn, View btnParent) { + Rect btnRect = new Rect(); + btn.getHitRect(btnRect); + btnRect.top -= getHitRectHeight(mTopListDivider); + btnRect.bottom += getHitRectHeight(mBottomListDivider); + btnParent.setTouchDelegate(new TouchDelegate(btnRect, btn)); + } + + private static int getHitRectHeight(View listDivider) { + Rect hitRect = new Rect(); + listDivider.getHitRect(hitRect); + return hitRect.height(); + } + + private void setListener(@Nullable View view) { + if (view != null) { + view.setOnClickListener(mFooterButtonClickListener); + } + } + + /** Handles click event for footer buttons. */ + private class FooterButtonClickListener implements OnClickListener { + @Override + public void onClick(View view) { + if (view.getId() == R.id.menu_prev_button) { + mCallBack.onLeftButtonClicked(); + } else if (view.getId() == R.id.menu_next_button) { + mCallBack.onRightButtonClicked(); + } + } + } +} diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java new file mode 100644 index 0000000000000..740bc8a412af9 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java @@ -0,0 +1,269 @@ +/* + * 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.accessibility.accessibilitymenu.view; + +import static java.lang.Math.max; + +import android.content.res.Configuration; +import android.graphics.Insets; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.view.Display; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.Surface; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.view.WindowMetrics; +import android.widget.FrameLayout; +import android.widget.Toast; + +import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService; +import com.android.systemui.accessibility.accessibilitymenu.R; +import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut; + +import java.util.ArrayList; +import java.util.List; + +/** + * Provides functionality for Accessibility menu layout in a11y menu overlay. There are functions to + * configure or update Accessibility menu layout when orientation and display size changed, and + * functions to toggle menu visibility when button clicked or screen off. + */ +public class A11yMenuOverlayLayout { + + /** Predefined default shortcuts when large button setting is off. */ + private static final int[] SHORTCUT_LIST_DEFAULT = { + A11yMenuShortcut.ShortcutId.ID_ASSISTANT_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_A11YSETTING_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_POWER_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_VOLUME_DOWN_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_VOLUME_UP_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_RECENT_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_BRIGHTNESS_DOWN_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_BRIGHTNESS_UP_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_LOCKSCREEN_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_QUICKSETTING_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_NOTIFICATION_VALUE.ordinal(), + A11yMenuShortcut.ShortcutId.ID_SCREENSHOT_VALUE.ordinal() + }; + + private final AccessibilityMenuService mService; + private final WindowManager mWindowManager; + private ViewGroup mLayout; + private WindowManager.LayoutParams mLayoutParameter; + private A11yMenuViewPager mA11yMenuViewPager; + + public A11yMenuOverlayLayout(AccessibilityMenuService service) { + mService = service; + mWindowManager = mService.getSystemService(WindowManager.class); + configureLayout(); + } + + /** Creates Accessibility menu layout and configure layout parameters. */ + public View configureLayout() { + return configureLayout(A11yMenuViewPager.DEFAULT_PAGE_INDEX); + } + + // TODO(b/78292783): Find a better way to inflate layout in the test. + /** + * Creates Accessibility menu layout, configure layout parameters and apply index to ViewPager. + * + * @param pageIndex the index of the ViewPager to show. + */ + public View configureLayout(int pageIndex) { + + int lastVisibilityState = View.GONE; + if (mLayout != null) { + lastVisibilityState = mLayout.getVisibility(); + mWindowManager.removeView(mLayout); + mLayout = null; + } + + if (mLayoutParameter == null) { + initLayoutParams(); + } + + mLayout = new FrameLayout(mService); + updateLayoutPosition(); + inflateLayoutAndSetOnTouchListener(mLayout); + mA11yMenuViewPager = new A11yMenuViewPager(mService); + mA11yMenuViewPager.configureViewPagerAndFooter(mLayout, createShortcutList(), pageIndex); + mWindowManager.addView(mLayout, mLayoutParameter); + mLayout.setVisibility(lastVisibilityState); + + return mLayout; + } + + /** Updates view layout with new layout parameters only. */ + public void updateViewLayout() { + if (mLayout == null || mLayoutParameter == null) { + return; + } + updateLayoutPosition(); + mWindowManager.updateViewLayout(mLayout, mLayoutParameter); + } + + private void initLayoutParams() { + mLayoutParameter = new WindowManager.LayoutParams(); + mLayoutParameter.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; + mLayoutParameter.format = PixelFormat.TRANSLUCENT; + mLayoutParameter.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; + mLayoutParameter.flags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; + mLayoutParameter.setTitle(mService.getString(R.string.accessibility_menu_service_name)); + } + + private void inflateLayoutAndSetOnTouchListener(ViewGroup view) { + LayoutInflater inflater = LayoutInflater.from(mService); + inflater.inflate(R.layout.paged_menu, view); + view.setOnTouchListener(mService); + } + + /** + * Loads shortcut data from default shortcut ID array. + * + * @return A list of default shortcuts + */ + private List createShortcutList() { + List shortcutList = new ArrayList<>(); + for (int shortcutId : SHORTCUT_LIST_DEFAULT) { + shortcutList.add(new A11yMenuShortcut(shortcutId)); + } + return shortcutList; + } + + /** Updates a11y menu layout position by configuring layout params. */ + private void updateLayoutPosition() { + Display display = mLayout.getDisplay(); + final int orientation = mService.getResources().getConfiguration().orientation; + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + switch (display.getRotation()) { + case Surface.ROTATION_90: + case Surface.ROTATION_180: + mLayoutParameter.gravity = + Gravity.END | Gravity.BOTTOM + | Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL; + mLayoutParameter.width = WindowManager.LayoutParams.WRAP_CONTENT; + mLayoutParameter.height = WindowManager.LayoutParams.MATCH_PARENT; + mLayoutParameter.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; + mLayoutParameter.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; + mLayout.setBackgroundResource(R.drawable.shadow_90deg); + break; + case Surface.ROTATION_0: + case Surface.ROTATION_270: + mLayoutParameter.gravity = + Gravity.START | Gravity.BOTTOM + | Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL; + mLayoutParameter.width = WindowManager.LayoutParams.WRAP_CONTENT; + mLayoutParameter.height = WindowManager.LayoutParams.MATCH_PARENT; + mLayoutParameter.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; + mLayoutParameter.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; + mLayout.setBackgroundResource(R.drawable.shadow_270deg); + break; + default: + break; + } + } else { + mLayoutParameter.gravity = Gravity.BOTTOM; + mLayoutParameter.width = WindowManager.LayoutParams.MATCH_PARENT; + mLayoutParameter.height = WindowManager.LayoutParams.WRAP_CONTENT; + mLayout.setBackgroundResource(R.drawable.shadow_0deg); + } + + // Adjusts the y position of a11y menu layout to make the layout not to overlap bottom + // navigation bar window. + updateLayoutByWindowInsetsIfNeeded(); + mLayout.setOnApplyWindowInsetsListener( + (view, insets) -> { + if (updateLayoutByWindowInsetsIfNeeded()) { + mWindowManager.updateViewLayout(mLayout, mLayoutParameter); + } + return view.onApplyWindowInsets(insets); + }); + } + + /** + * Returns {@code true} if the a11y menu layout params + * should be updated by {@link WindowManager} immediately due to window insets change. + * This method adjusts the layout position and size to + * make a11y menu not to overlap navigation bar window. + */ + private boolean updateLayoutByWindowInsetsIfNeeded() { + boolean shouldUpdateLayout = false; + WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics(); + Insets windowInsets = windowMetrics.getWindowInsets().getInsetsIgnoringVisibility( + WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout()); + int xOffset = max(windowInsets.left, windowInsets.right); + int yOffset = windowInsets.bottom; + Rect windowBound = windowMetrics.getBounds(); + if (mLayoutParameter.x != xOffset || mLayoutParameter.y != yOffset) { + mLayoutParameter.x = xOffset; + mLayoutParameter.y = yOffset; + shouldUpdateLayout = true; + } + // for gestural navigation mode and the landscape mode, + // the layout height should be decreased by system bar + // and display cutout inset to fit the new + // frame size that doesn't overlap the navigation bar window. + int orientation = mService.getResources().getConfiguration().orientation; + if (mLayout.getHeight() != mLayoutParameter.height + && orientation == Configuration.ORIENTATION_LANDSCAPE) { + mLayoutParameter.height = windowBound.height() - yOffset; + shouldUpdateLayout = true; + } + return shouldUpdateLayout; + } + + /** + * Gets the current page index when device configuration changed. {@link + * AccessibilityMenuService#onConfigurationChanged(Configuration)} + * + * @return the current index of the ViewPager. + */ + public int getPageIndex() { + if (mA11yMenuViewPager != null) { + return mA11yMenuViewPager.mViewPager.getCurrentItem(); + } + return A11yMenuViewPager.DEFAULT_PAGE_INDEX; + } + + /** + * Hides a11y menu layout. And return if layout visibility has been changed. + * + * @return {@code true} layout visibility is toggled off; {@code false} is unchanged + */ + public boolean hideMenu() { + if (mLayout.getVisibility() == View.VISIBLE) { + mLayout.setVisibility(View.GONE); + return true; + } + return false; + } + + /** Toggles a11y menu layout visibility. */ + public void toggleVisibility() { + mLayout.setVisibility((mLayout.getVisibility() == View.VISIBLE) ? View.GONE : View.VISIBLE); + } + + /** Shows hint text on Toast. */ + public void showToast(String text) { + final View viewPos = mLayout.findViewById(R.id.coordinatorLayout); + Toast.makeText(viewPos.getContext(), text, Toast.LENGTH_SHORT).show(); + } +} diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java new file mode 100644 index 0000000000000..c510b876e8470 --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java @@ -0,0 +1,356 @@ +/* + * 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.accessibility.accessibilitymenu.view; + +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Insets; +import android.util.DisplayMetrics; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver.OnGlobalLayoutListener; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.view.WindowMetrics; +import android.widget.GridView; + +import androidx.viewpager.widget.ViewPager; + +import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService; +import com.android.systemui.accessibility.accessibilitymenu.R; +import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut; +import com.android.systemui.accessibility.accessibilitymenu.view.A11yMenuFooter.A11yMenuFooterCallBack; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class handles UI for viewPager and footer. + * It displays grid pages containing all shortcuts in viewPager, + * and handles the click events from footer to switch between pages. + */ +public class A11yMenuViewPager { + + /** The default index of the ViewPager. */ + public static final int DEFAULT_PAGE_INDEX = 0; + + /** + * The class holds the static parameters for grid view when large button settings is on/off. + */ + public static final class GridViewParams { + /** Total shortcuts count in the grid view when large button settings is off. */ + public static final int GRID_ITEM_COUNT = 9; + + /** The number of columns in the grid view when large button settings is off. */ + public static final int GRID_COLUMN_COUNT = 3; + + /** Total shortcuts count in the grid view when large button settings is on. */ + public static final int LARGE_GRID_ITEM_COUNT = 4; + + /** The number of columns in the grid view when large button settings is on. */ + public static final int LARGE_GRID_COLUMN_COUNT = 2; + + /** Temporary measure to test both item types. */ + private static final boolean USE_LARGE_ITEMS = true; + + /** + * Returns the number of items in the grid view. + * + * @param context The parent context + * @return Grid item count + */ + public static int getGridItemCount(Context context) { + return USE_LARGE_ITEMS + ? LARGE_GRID_ITEM_COUNT + : GRID_ITEM_COUNT; + } + + /** + * Returns the number of columns in the grid view. + * + * @param context The parent context + * @return Grid column count + */ + public static int getGridColumnCount(Context context) { + return USE_LARGE_ITEMS + ? LARGE_GRID_COLUMN_COUNT + : GRID_COLUMN_COUNT; + } + + /** + * Returns the number of rows in the grid view. + * + * @param context The parent context + * @return Grid row count + */ + public static int getGridRowCount(Context context) { + return USE_LARGE_ITEMS + ? (LARGE_GRID_ITEM_COUNT / LARGE_GRID_COLUMN_COUNT) + : (GRID_ITEM_COUNT / GRID_COLUMN_COUNT); + } + + /** + * Separates a provided list of accessibility shortcuts into multiple sub-lists. + * Does not modify the original list. + * + * @param pageItemCount The maximum size of an individual sub-list. + * @param shortcutList The list of shortcuts to be separated into sub-lists. + * @return A list of shortcut sub-lists. + */ + public static List> generateShortcutSubLists( + int pageItemCount, List shortcutList) { + int start = 0; + int end; + int shortcutListSize = shortcutList.size(); + List> subLists = new ArrayList<>(); + while (start < shortcutListSize) { + end = Math.min(start + pageItemCount, shortcutListSize); + subLists.add(shortcutList.subList(start, end)); + start = end; + } + return subLists; + } + + private GridViewParams() {} + } + + private final AccessibilityMenuService mService; + + /** + * The pager widget, which handles animation and allows swiping horizontally to access previous + * and next gridView pages. + */ + protected ViewPager mViewPager; + + private ViewPagerAdapter mViewPagerAdapter; + private final List mGridPageList = new ArrayList<>(); + + /** The footer, which provides buttons to switch between pages */ + protected A11yMenuFooter mA11yMenuFooter; + + /** The shortcut list intended to show in grid pages of viewPager */ + private List mA11yMenuShortcutList; + + /** The container layout for a11y menu. */ + private ViewGroup mA11yMenuLayout; + + public A11yMenuViewPager(AccessibilityMenuService service) { + this.mService = service; + } + + /** + * Configures UI for view pager and footer. + * + * @param a11yMenuLayout the container layout for a11y menu + * @param shortcutDataList the data list need to show in view pager + * @param pageIndex the index of ViewPager to show + */ + public void configureViewPagerAndFooter( + ViewGroup a11yMenuLayout, List shortcutDataList, int pageIndex) { + this.mA11yMenuLayout = a11yMenuLayout; + mA11yMenuShortcutList = shortcutDataList; + initViewPager(); + initChildPage(); + mA11yMenuFooter = new A11yMenuFooter(a11yMenuLayout, mFooterCallbacks); + updateFooterState(); + registerOnGlobalLayoutListener(); + goToPage(pageIndex); + } + + /** Initializes viewPager and its adapter. */ + private void initViewPager() { + mViewPager = mA11yMenuLayout.findViewById(R.id.view_pager); + mViewPagerAdapter = new ViewPagerAdapter<>(); + mViewPager.setAdapter(mViewPagerAdapter); + mViewPager.setOverScrollMode(View.OVER_SCROLL_NEVER); + mViewPager.addOnPageChangeListener( + new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrollStateChanged(int state) {} + + @Override + public void onPageScrolled( + int position, float positionOffset, int positionOffsetPixels) {} + + @Override + public void onPageSelected(int position) { + updateFooterState(); + } + }); + } + + /** Creates child pages of viewPager by the length of shortcuts and initializes them. */ + private void initChildPage() { + if (mA11yMenuShortcutList == null || mA11yMenuShortcutList.isEmpty()) { + return; + } + + if (!mGridPageList.isEmpty()) { + mGridPageList.clear(); + } + + // Generate pages by calculating # of items per grid. + for (List page : GridViewParams.generateShortcutSubLists( + GridViewParams.getGridItemCount(mService), mA11yMenuShortcutList) + ) { + addGridPage(page); + } + + mViewPagerAdapter.set(mGridPageList); + } + + private void addGridPage(List shortcutDataListInPage) { + LayoutInflater inflater = LayoutInflater.from(mService); + View view = inflater.inflate(R.layout.grid_view, null); + GridView gridView = view.findViewById(R.id.gridview); + A11yMenuAdapter adapter = new A11yMenuAdapter(mService, shortcutDataListInPage); + gridView.setNumColumns(GridViewParams.getGridColumnCount(mService)); + gridView.setAdapter(adapter); + mGridPageList.add(gridView); + } + + /** Updates footer's state by index of current page in view pager. */ + private void updateFooterState() { + int currentPage = mViewPager.getCurrentItem(); + int lastPage = mViewPager.getAdapter().getCount() - 1; + mA11yMenuFooter.getPreviousPageBtn().setEnabled(currentPage > 0); + mA11yMenuFooter.getNextPageBtn().setEnabled(currentPage < lastPage); + } + + private void goToPage(int pageIndex) { + if (mViewPager == null) { + return; + } + if ((pageIndex >= 0) && (pageIndex < mViewPager.getAdapter().getCount())) { + mViewPager.setCurrentItem(pageIndex); + } + } + + /** Registers OnGlobalLayoutListener to adjust menu UI by running callback at first time. */ + private void registerOnGlobalLayoutListener() { + mA11yMenuLayout + .getViewTreeObserver() + .addOnGlobalLayoutListener( + new OnGlobalLayoutListener() { + + boolean mIsFirstTime = true; + + @Override + public void onGlobalLayout() { + if (!mIsFirstTime) { + return; + } + + if (mGridPageList.isEmpty()) { + return; + } + + GridView firstGridView = mGridPageList.get(0); + if (firstGridView == null + || firstGridView.getChildAt(0) == null) { + return; + } + + mIsFirstTime = false; + + int gridItemHeight = firstGridView.getChildAt(0) + .getMeasuredHeight(); + adjustMenuUISize(gridItemHeight); + } + }); + } + + /** + * Adjusts menu UI to fit both landscape and portrait mode. + * + *
    + *
  1. Adjust view pager's height. + *
  2. Adjust vertical interval between grid items. + *
  3. Adjust padding in view pager. + *
+ */ + private void adjustMenuUISize(int gridItemHeight) { + final int rowsInGridView = GridViewParams.getGridRowCount(mService); + final int defaultMargin = + (int) mService.getResources().getDimension(R.dimen.a11ymenu_layout_margin); + final int topMargin = (int) mService.getResources().getDimension(R.dimen.table_margin_top); + final int displayMode = mService.getResources().getConfiguration().orientation; + int viewPagerHeight = mViewPager.getMeasuredHeight(); + + if (displayMode == Configuration.ORIENTATION_PORTRAIT) { + // In portrait mode, we only need to adjust view pager's height to match its + // child's height. + viewPagerHeight = gridItemHeight * rowsInGridView + defaultMargin + topMargin; + } else if (displayMode == Configuration.ORIENTATION_LANDSCAPE) { + // In landscape mode, we need to adjust view pager's height to match screen height + // and adjust its child too, + // because a11y menu layout height is limited by the screen height. + DisplayMetrics displayMetrics = mService.getResources().getDisplayMetrics(); + float densityScale = (float) displayMetrics.densityDpi + / DisplayMetrics.DENSITY_DEVICE_STABLE; + View footerLayout = mA11yMenuLayout.findViewById(R.id.footerlayout); + // Keeps footer window height unchanged no matter the density is changed. + footerLayout.getLayoutParams().height = + (int) (footerLayout.getLayoutParams().height / densityScale); + // Adjust the view pager height for system bar and display cutout insets. + WindowManager windowManager = mService.getSystemService(WindowManager.class); + WindowMetrics windowMetric = windowManager.getCurrentWindowMetrics(); + Insets windowInsets = windowMetric.getWindowInsets().getInsetsIgnoringVisibility( + WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout()); + viewPagerHeight = + windowMetric.getBounds().height() + - footerLayout.getLayoutParams().height + - windowInsets.bottom; + // Sets vertical interval between grid items. + int interval = + (viewPagerHeight - topMargin - defaultMargin + - (rowsInGridView * gridItemHeight)) + / (rowsInGridView + 1); + for (GridView gridView : mGridPageList) { + gridView.setVerticalSpacing(interval); + } + + // Sets padding to view pager. + final int finalMarginTop = interval + topMargin; + mViewPager.setPadding(defaultMargin, finalMarginTop, defaultMargin, defaultMargin); + } + final ViewGroup.LayoutParams layoutParams = mViewPager.getLayoutParams(); + layoutParams.height = viewPagerHeight; + mViewPager.setLayoutParams(layoutParams); + } + + /** Callback object to handle click events from A11yMenuFooter */ + protected A11yMenuFooterCallBack mFooterCallbacks = + new A11yMenuFooterCallBack() { + @Override + public void onLeftButtonClicked() { + // Moves to previous page. + int targetPage = mViewPager.getCurrentItem() - 1; + goToPage(targetPage); + updateFooterState(); + } + + @Override + public void onRightButtonClicked() { + // Moves to next page. + int targetPage = mViewPager.getCurrentItem() + 1; + goToPage(targetPage); + updateFooterState(); + } + }; +} diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java new file mode 100644 index 0000000000000..5670d72842f4e --- /dev/null +++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/ViewPagerAdapter.java @@ -0,0 +1,70 @@ +/* + * 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.accessibility.accessibilitymenu.view; + +import android.view.View; +import android.view.ViewGroup; + +import androidx.viewpager.widget.PagerAdapter; + +import java.util.List; + +/** The pager adapter, which provides the pages to the view pager widget. */ +class ViewPagerAdapter extends PagerAdapter { + + /** The widget list in each page of view pager. */ + private List mWidgetList; + + ViewPagerAdapter() {} + + public void set(List tList) { + mWidgetList = tList; + notifyDataSetChanged(); + } + + @Override + public int getCount() { + if (mWidgetList == null) { + return 0; + } + return mWidgetList.size(); + } + + @Override + public int getItemPosition(Object object) { + return POSITION_NONE; + } + + @Override + public boolean isViewFromObject(View view, Object object) { + return view == object; + } + + @Override + public Object instantiateItem(ViewGroup container, int position) { + if (mWidgetList == null) { + return null; + } + container.addView(mWidgetList.get(position)); + return mWidgetList.get(position); + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + container.removeView((View) object); + } +}