Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9b5c0c67 authored by Matt Sziklay's avatar Matt Sziklay Committed by Android (Google) Code Review
Browse files

Merge "Add hover/press animations to handle." into main

parents c289ee89 7a4ac85c
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -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
+3 −2
Original line number Diff line number Diff line
@@ -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>
+4 −1
Original line number Diff line number Diff line
@@ -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;

@@ -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);
        }
+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
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -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.