Loading libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml +3 −2 Original line number Diff line number Diff line Loading @@ -22,14 +22,15 @@ android:layout_height="wrap_content" android:gravity="center_horizontal"> <ImageButton <com.android.wm.shell.windowdecor.HandleImageButton android:id="@+id/caption_handle" android:layout_width="@dimen/desktop_mode_fullscreen_decor_caption_width" android:layout_height="@dimen/desktop_mode_fullscreen_decor_caption_height" android:paddingVertical="16dp" android:paddingHorizontal="10dp" android:contentDescription="@string/handle_text" android:src="@drawable/decor_handle_dark" tools:tint="@color/desktop_mode_caption_handle_bar_dark" android:scaleType="fitXY" android:background="?android:selectableItemBackground"/> android:background="@android:color/transparent"/> </com.android.wm.shell.windowdecor.WindowDecorLinearLayout> No newline at end of file libs/WindowManager/Shell/res/values/dimen.xml +3 −2 Original line number Diff line number Diff line Loading @@ -423,8 +423,9 @@ <!-- Height of desktop mode caption for fullscreen tasks. --> <dimen name="desktop_mode_fullscreen_decor_caption_height">36dp</dimen> <!-- Width of desktop mode caption for fullscreen tasks. --> <dimen name="desktop_mode_fullscreen_decor_caption_width">128dp</dimen> <!-- Width of desktop mode caption for fullscreen tasks. 80 dp for handle + 20 dp for room to grow on the sides when hovered. --> <dimen name="desktop_mode_fullscreen_decor_caption_width">100dp</dimen> <!-- Required empty space to be visible for partially offscreen tasks. --> <dimen name="freeform_required_visible_empty_space_in_header">48dp</dimen> Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +4 −1 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.wm.shell.windowdecor; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.windowingModeToString; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; Loading Loading @@ -752,7 +753,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin final int action = ev.getActionMasked(); // The comparison against ACTION_UP is needed for the cancel drag to desktop case. handle.setHovered(inHandle && action != ACTION_UP); handle.setPressed(inHandle && action == ACTION_DOWN); // We want handle to remain pressed if the pointer moves outside of it during a drag. handle.setPressed((inHandle && action == ACTION_DOWN) || (handle.isPressed() && action != ACTION_UP && action != ACTION_CANCEL)); if (isHandleMenuActive()) { mHandleMenu.checkMotionEvent(ev); } Loading libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleImageButton.kt 0 → 100644 +79 −0 Original line number Diff line number Diff line /* * 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.wm.shell.windowdecor import android.animation.ValueAnimator import android.content.Context import android.util.AttributeSet import android.widget.ImageButton /** * [ImageButton] for the handle at the top of fullscreen apps. Has custom hover * and press handling to grow the handle on hover enter and shrink the handle on * hover exit and press. */ class HandleImageButton (context: Context?, attrs: AttributeSet?) : ImageButton(context, attrs) { private val handleAnimator = ValueAnimator() override fun onHoverChanged(hovered: Boolean) { super.onHoverChanged(hovered) if (hovered) { animateHandle(HANDLE_HOVER_ANIM_DURATION, HANDLE_HOVER_ENTER_SCALE) } else { if (!isPressed) { animateHandle(HANDLE_HOVER_ANIM_DURATION, HANDLE_DEFAULT_SCALE) } } } override fun setPressed(pressed: Boolean) { if (isPressed != pressed) { super.setPressed(pressed) if (pressed) { animateHandle(HANDLE_PRESS_ANIM_DURATION, HANDLE_PRESS_DOWN_SCALE) } else { animateHandle(HANDLE_PRESS_ANIM_DURATION, HANDLE_DEFAULT_SCALE) } } } private fun animateHandle(duration: Long, endScale: Float) { if (handleAnimator.isRunning) { handleAnimator.cancel() } handleAnimator.duration = duration handleAnimator.setFloatValues(scaleX, endScale) handleAnimator.addUpdateListener { animator -> scaleX = animator.animatedValue as Float } handleAnimator.start() } companion object { /** The duration of animations related to hover state. **/ private const val HANDLE_HOVER_ANIM_DURATION = 300L /** The duration of animations related to pressed state. **/ private const val HANDLE_PRESS_ANIM_DURATION = 200L /** Ending scale for hover enter. **/ private const val HANDLE_HOVER_ENTER_SCALE = 1.2f /** Ending scale for press down. **/ private const val HANDLE_PRESS_DOWN_SCALE = 0.85f /** Default scale for handle. **/ private const val HANDLE_DEFAULT_SCALE = 1f } } libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuImageButton.kt +1 −1 Original line number Diff line number Diff line Loading @@ -21,7 +21,7 @@ import android.view.MotionEvent import android.widget.ImageButton /** * A custom [ImageButton] that intentionally does not handle hover events. * A custom [ImageButton] for buttons inside handle menu that intentionally doesn't handle hovers. * This is due to the hover events being handled by [DesktopModeWindowDecorViewModel] * in order to take the status bar layer into account. Handling it in both classes results in a * flicker when the hover moves from outside to inside status bar layer. Loading Loading
libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml +3 −2 Original line number Diff line number Diff line Loading @@ -22,14 +22,15 @@ android:layout_height="wrap_content" android:gravity="center_horizontal"> <ImageButton <com.android.wm.shell.windowdecor.HandleImageButton android:id="@+id/caption_handle" android:layout_width="@dimen/desktop_mode_fullscreen_decor_caption_width" android:layout_height="@dimen/desktop_mode_fullscreen_decor_caption_height" android:paddingVertical="16dp" android:paddingHorizontal="10dp" android:contentDescription="@string/handle_text" android:src="@drawable/decor_handle_dark" tools:tint="@color/desktop_mode_caption_handle_bar_dark" android:scaleType="fitXY" android:background="?android:selectableItemBackground"/> android:background="@android:color/transparent"/> </com.android.wm.shell.windowdecor.WindowDecorLinearLayout> No newline at end of file
libs/WindowManager/Shell/res/values/dimen.xml +3 −2 Original line number Diff line number Diff line Loading @@ -423,8 +423,9 @@ <!-- Height of desktop mode caption for fullscreen tasks. --> <dimen name="desktop_mode_fullscreen_decor_caption_height">36dp</dimen> <!-- Width of desktop mode caption for fullscreen tasks. --> <dimen name="desktop_mode_fullscreen_decor_caption_width">128dp</dimen> <!-- Width of desktop mode caption for fullscreen tasks. 80 dp for handle + 20 dp for room to grow on the sides when hovered. --> <dimen name="desktop_mode_fullscreen_decor_caption_width">100dp</dimen> <!-- Required empty space to be visible for partially offscreen tasks. --> <dimen name="freeform_required_visible_empty_space_in_header">48dp</dimen> Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +4 −1 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.wm.shell.windowdecor; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.windowingModeToString; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; Loading Loading @@ -752,7 +753,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin final int action = ev.getActionMasked(); // The comparison against ACTION_UP is needed for the cancel drag to desktop case. handle.setHovered(inHandle && action != ACTION_UP); handle.setPressed(inHandle && action == ACTION_DOWN); // We want handle to remain pressed if the pointer moves outside of it during a drag. handle.setPressed((inHandle && action == ACTION_DOWN) || (handle.isPressed() && action != ACTION_UP && action != ACTION_CANCEL)); if (isHandleMenuActive()) { mHandleMenu.checkMotionEvent(ev); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleImageButton.kt 0 → 100644 +79 −0 Original line number Diff line number Diff line /* * 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.wm.shell.windowdecor import android.animation.ValueAnimator import android.content.Context import android.util.AttributeSet import android.widget.ImageButton /** * [ImageButton] for the handle at the top of fullscreen apps. Has custom hover * and press handling to grow the handle on hover enter and shrink the handle on * hover exit and press. */ class HandleImageButton (context: Context?, attrs: AttributeSet?) : ImageButton(context, attrs) { private val handleAnimator = ValueAnimator() override fun onHoverChanged(hovered: Boolean) { super.onHoverChanged(hovered) if (hovered) { animateHandle(HANDLE_HOVER_ANIM_DURATION, HANDLE_HOVER_ENTER_SCALE) } else { if (!isPressed) { animateHandle(HANDLE_HOVER_ANIM_DURATION, HANDLE_DEFAULT_SCALE) } } } override fun setPressed(pressed: Boolean) { if (isPressed != pressed) { super.setPressed(pressed) if (pressed) { animateHandle(HANDLE_PRESS_ANIM_DURATION, HANDLE_PRESS_DOWN_SCALE) } else { animateHandle(HANDLE_PRESS_ANIM_DURATION, HANDLE_DEFAULT_SCALE) } } } private fun animateHandle(duration: Long, endScale: Float) { if (handleAnimator.isRunning) { handleAnimator.cancel() } handleAnimator.duration = duration handleAnimator.setFloatValues(scaleX, endScale) handleAnimator.addUpdateListener { animator -> scaleX = animator.animatedValue as Float } handleAnimator.start() } companion object { /** The duration of animations related to hover state. **/ private const val HANDLE_HOVER_ANIM_DURATION = 300L /** The duration of animations related to pressed state. **/ private const val HANDLE_PRESS_ANIM_DURATION = 200L /** Ending scale for hover enter. **/ private const val HANDLE_HOVER_ENTER_SCALE = 1.2f /** Ending scale for press down. **/ private const val HANDLE_PRESS_DOWN_SCALE = 0.85f /** Default scale for handle. **/ private const val HANDLE_DEFAULT_SCALE = 1f } }
libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuImageButton.kt +1 −1 Original line number Diff line number Diff line Loading @@ -21,7 +21,7 @@ import android.view.MotionEvent import android.widget.ImageButton /** * A custom [ImageButton] that intentionally does not handle hover events. * A custom [ImageButton] for buttons inside handle menu that intentionally doesn't handle hovers. * This is due to the hover events being handled by [DesktopModeWindowDecorViewModel] * in order to take the status bar layer into account. Handling it in both classes results in a * flicker when the hover moves from outside to inside status bar layer. Loading