diff options
9 files changed, 290 insertions, 0 deletions
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index bbc9fe47a18b..de38b8e9e833 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -1033,6 +1033,7 @@ <receiver android:name=".statusbar.KeyboardShortcutsReceiver" + android:visibleToInstantApps="true" android:exported="true"> <intent-filter> <action android:name="com.android.intent.action.DISMISS_KEYBOARD_SHORTCUTS" /> @@ -1113,5 +1114,11 @@ android:name="android.service.dream" android:resource="@xml/home_controls_dream_metadata" /> </service> + + <activity android:name="com.android.systemui.keyboard.shortcut.ShortcutHelperActivity" + android:exported="false" + android:theme="@style/ShortcutHelperTheme" + android:excludeFromRecents="true" + android:finishOnCloseSystemDialogs="true" /> </application> </manifest> diff --git a/packages/SystemUI/res/anim/shortcut_helper_close_anim.xml b/packages/SystemUI/res/anim/shortcut_helper_close_anim.xml new file mode 100644 index 000000000000..47fd78a4d368 --- /dev/null +++ b/packages/SystemUI/res/anim/shortcut_helper_close_anim.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 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. + --> + +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:interpolator="@android:anim/accelerate_interpolator" + android:zAdjustment="top"> + + <translate + android:fromYDelta="0" + android:toYDelta="100%" + android:duration="@android:integer/config_shortAnimTime" /> +</set> diff --git a/packages/SystemUI/res/anim/shortcut_helper_launch_anim.xml b/packages/SystemUI/res/anim/shortcut_helper_launch_anim.xml new file mode 100644 index 000000000000..77edf588bd2e --- /dev/null +++ b/packages/SystemUI/res/anim/shortcut_helper_launch_anim.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2024 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. + --> + +<!-- Animation for when a dock window at the bottom of the screen is entering. --> +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:interpolator="@android:anim/accelerate_decelerate_interpolator" + android:zAdjustment="top"> + + <translate android:fromYDelta="100%" + android:toYDelta="0" + android:startOffset="@android:integer/config_shortAnimTime" + android:duration="@android:integer/config_mediumAnimTime"/> +</set> diff --git a/packages/SystemUI/res/layout/activity_keyboard_shortcut_helper.xml b/packages/SystemUI/res/layout/activity_keyboard_shortcut_helper.xml new file mode 100644 index 000000000000..292e49610e2a --- /dev/null +++ b/packages/SystemUI/res/layout/activity_keyboard_shortcut_helper.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/shortcut_helper_sheet_container" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout + android:id="@+id/shortcut_helper_sheet" + style="@style/Widget.Material3.BottomSheet" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"> + + <!-- Drag handle for accessibility --> + <com.google.android.material.bottomsheet.BottomSheetDragHandleView + android:id="@+id/drag_handle" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:gravity="center" + android:textAppearance="?textAppearanceDisplayLarge" + android:background="?colorTertiaryContainer" + android:text="Shortcut Helper Content" /> + </LinearLayout> +</androidx.coordinatorlayout.widget.CoordinatorLayout> diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml index 291f8a20bdd2..546bf1c3ac95 100644 --- a/packages/SystemUI/res/values-night/styles.xml +++ b/packages/SystemUI/res/values-night/styles.xml @@ -72,4 +72,8 @@ <item name="android:textColor">@color/material_dynamic_secondary80</item> </style> + <style name="ShortcutHelperTheme" parent="@style/ShortcutHelperThemeCommon"> + <item name="android:windowLightNavigationBar">false</item> + </style> + </resources> diff --git a/packages/SystemUI/res/values-xlarge-land/config.xml b/packages/SystemUI/res/values-xlarge-land/config.xml new file mode 100644 index 000000000000..5e4304e1c13a --- /dev/null +++ b/packages/SystemUI/res/values-xlarge-land/config.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2024 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. + --> + +<resources> + <item name="shortcut_helper_screen_width_fraction" format="float" type="dimen">0.8</item> +</resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index fa9d507dbff5..7d66ae162783 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -1007,4 +1007,7 @@ <!-- Whether volume panel should use the large screen layout or not --> <bool name="volume_panel_is_large_screen">false</bool> + + <!-- The width of the shortcut helper container, as a fraction of the screen's width. --> + <item name="shortcut_helper_screen_width_fraction" format="float" type="dimen">1.0</item> </resources> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 2c9006e50f92..3d57111253f8 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -1654,4 +1654,28 @@ <style name="Theme.SystemUI.Dialog.StickyKeys" parent="@style/Theme.SystemUI.Dialog"> <item name="android:colorBackground">@color/transparent</item> </style> + + <style name="ShortcutHelperAnimation" parent="@android:style/Animation.Activity"> + <item name="android:activityOpenEnterAnimation">@anim/shortcut_helper_launch_anim</item> + <item name="android:taskOpenEnterAnimation">@anim/shortcut_helper_launch_anim</item> + <item name="android:activityOpenExitAnimation">@anim/shortcut_helper_close_anim</item> + <item name="android:taskOpenExitAnimation">@anim/shortcut_helper_close_anim</item> + </style> + + <style name="ShortcutHelperThemeCommon" parent="@style/Theme.Material3.DynamicColors.DayNight"> + <item name="android:windowAnimationStyle">@style/ShortcutHelperAnimation</item> + <item name="android:windowIsTranslucent">true</item> + <item name="android:windowNoTitle">true</item> + <item name="android:windowBackground">@android:color/transparent</item> + <item name="android:backgroundDimEnabled">true</item> + <item name="android:statusBarColor">@android:color/transparent</item> + <item name="android:windowContentOverlay">@null</item> + <item name="android:navigationBarColor">@android:color/transparent</item> + <item name="android:windowLayoutInDisplayCutoutMode">always</item> + <item name="enableEdgeToEdge">true</item> + </style> + + <style name="ShortcutHelperTheme" parent="@style/ShortcutHelperThemeCommon"> + <item name="android:windowLightNavigationBar">true</item> + </style> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperActivity.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperActivity.kt new file mode 100644 index 000000000000..692fbb06e88c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperActivity.kt @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2024 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.keyboard.shortcut + +import android.graphics.Insets +import android.os.Bundle +import android.view.View +import android.view.WindowInsets +import androidx.activity.BackEventCompat +import androidx.activity.ComponentActivity +import androidx.activity.OnBackPressedCallback +import androidx.core.view.updatePadding +import com.android.systemui.res.R +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback +import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN + +/** + * Activity that hosts the new version of the keyboard shortcut helper. It will be used both for + * small and large screen devices. + */ +class ShortcutHelperActivity : ComponentActivity() { + + private val bottomSheetContainer + get() = requireViewById<View>(R.id.shortcut_helper_sheet_container) + + private val bottomSheet + get() = requireViewById<View>(R.id.shortcut_helper_sheet) + + private val bottomSheetBehavior + get() = BottomSheetBehavior.from(bottomSheet) + + override fun onCreate(savedInstanceState: Bundle?) { + setupEdgeToEdge() + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_keyboard_shortcut_helper) + setUpBottomSheetWidth() + setUpInsets() + setUpPredictiveBack() + setUpSheetDismissListener() + setUpDismissOnTouchOutside() + } + + private fun setupEdgeToEdge() { + // Draw behind system bars + window.setDecorFitsSystemWindows(false) + } + + private fun setUpBottomSheetWidth() { + val sheetScreenWidthFraction = + resources.getFloat(R.dimen.shortcut_helper_screen_width_fraction) + // maxWidth needs to be set before the sheet is drawn, otherwise the call will have no + // effect. + val screenWidth = resources.displayMetrics.widthPixels + bottomSheetBehavior.maxWidth = (sheetScreenWidthFraction * screenWidth).toInt() + } + + private fun setUpInsets() { + bottomSheetContainer.setOnApplyWindowInsetsListener { _, insets -> + val safeDrawingInsets = insets.safeDrawing + // Make sure the bottom sheet is not covered by the status bar. + bottomSheetContainer.updatePadding(top = safeDrawingInsets.top) + // Make sure the contents inside of the bottom sheet are not hidden by system bars, or + // cutouts. + bottomSheet.updatePadding( + left = safeDrawingInsets.left, + right = safeDrawingInsets.right, + bottom = safeDrawingInsets.bottom + ) + // The bottom sheet has to be expanded only after setting up insets, otherwise there is + // a bug and it will not use full height. + expandBottomSheet() + + // Return CONSUMED if you don't want want the window insets to keep passing + // down to descendant views. + WindowInsets.CONSUMED + } + } + + private fun expandBottomSheet() { + bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED + bottomSheetBehavior.skipCollapsed = true + } + + private fun setUpPredictiveBack() { + val onBackPressedCallback = + object : OnBackPressedCallback(/* enabled= */ true) { + override fun handleOnBackStarted(backEvent: BackEventCompat) { + bottomSheetBehavior.startBackProgress(backEvent) + } + + override fun handleOnBackProgressed(backEvent: BackEventCompat) { + bottomSheetBehavior.updateBackProgress(backEvent) + } + + override fun handleOnBackPressed() { + bottomSheetBehavior.handleBackInvoked() + } + + override fun handleOnBackCancelled() { + bottomSheetBehavior.cancelBackProgress() + } + } + onBackPressedDispatcher.addCallback( + owner = this, + onBackPressedCallback = onBackPressedCallback + ) + } + + private fun setUpSheetDismissListener() { + bottomSheetBehavior.addBottomSheetCallback( + object : BottomSheetCallback() { + override fun onStateChanged(bottomSheet: View, newState: Int) { + if (newState == STATE_HIDDEN) { + finish() + } + } + + override fun onSlide(bottomSheet: View, slideOffset: Float) {} + } + ) + } + + private fun setUpDismissOnTouchOutside() { + bottomSheetContainer.setOnClickListener { finish() } + } +} + +private val WindowInsets.safeDrawing + get() = + getInsets(WindowInsets.Type.systemBars()) + .union(getInsets(WindowInsets.Type.ime())) + .union(getInsets(WindowInsets.Type.displayCutout())) + +private fun Insets.union(insets: Insets): Insets = Insets.max(this, insets) |