summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Maryam Dehaini <mdehaini@google.com> 2024-02-23 22:22:24 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-02-23 22:22:24 +0000
commitbae19a90a6da1b99171622b9ea9dfee64d37578b (patch)
treec74591cd67311a0515d6f48473d22a1e29fa2e1e
parent73396979df21b71d582b7845a2e0c6d67e7cb3a9 (diff)
parent853a8e3ed9492b8fb987956ad160c387ed840dd2 (diff)
Merge "Add hover support to maximize menu" into main
-rw-r--r--libs/WindowManager/Shell/res/color/desktop_mode_caption_button_color_selector_dark.xml21
-rw-r--r--libs/WindowManager/Shell/res/color/desktop_mode_caption_button_color_selector_light.xml21
-rw-r--r--libs/WindowManager/Shell/res/drawable/circular_progress.xml33
-rw-r--r--libs/WindowManager/Shell/res/drawable/rounded_button.xml19
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml16
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml1
-rw-r--r--libs/WindowManager/Shell/res/layout/maximize_menu_button.xml36
-rw-r--r--libs/WindowManager/Shell/res/values/colors.xml5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java53
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt106
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt28
13 files changed, 381 insertions, 30 deletions
diff --git a/libs/WindowManager/Shell/res/color/desktop_mode_caption_button_color_selector_dark.xml b/libs/WindowManager/Shell/res/color/desktop_mode_caption_button_color_selector_dark.xml
new file mode 100644
index 000000000000..52a59671baa1
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/desktop_mode_caption_button_color_selector_dark.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_hovered="true"
+ android:color="@color/desktop_mode_caption_button_on_hover_dark"/>
+ <item android:color="@color/desktop_mode_caption_button"/>
+</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/color/desktop_mode_caption_button_color_selector_light.xml b/libs/WindowManager/Shell/res/color/desktop_mode_caption_button_color_selector_light.xml
new file mode 100644
index 000000000000..6d8a51cd6f8f
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/desktop_mode_caption_button_color_selector_light.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_hovered="true"
+ android:color="@color/desktop_mode_caption_button_on_hover_light"/>
+ <item android:color="@color/desktop_mode_caption_button"/>
+</selector> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/circular_progress.xml b/libs/WindowManager/Shell/res/drawable/circular_progress.xml
new file mode 100644
index 000000000000..948264579e1d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/circular_progress.xml
@@ -0,0 +1,33 @@
+<?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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/progress">
+ <rotate
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:fromDegrees="275"
+ android:toDegrees="275">
+ <shape
+ android:shape="ring"
+ android:thickness="3dp"
+ android:innerRadius="17dp"
+ android:useLevel="true">
+ </shape>
+ </rotate>
+ </item>
+</layer-list> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/rounded_button.xml b/libs/WindowManager/Shell/res/drawable/rounded_button.xml
new file mode 100644
index 000000000000..17a0bab56a74
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/rounded_button.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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <corners android:radius="20dp" />
+</shape> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
index e4f793c2665b..d1b1af3e77ab 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
@@ -74,17 +74,11 @@
android:layout_height="40dp"
android:layout_weight="1"/>
- <ImageButton
- android:id="@+id/maximize_window"
- android:layout_width="40dp"
- android:layout_height="40dp"
- android:padding="9dp"
- android:layout_marginEnd="8dp"
- android:contentDescription="@string/maximize_button_text"
- android:src="@drawable/decor_desktop_mode_maximize_button_dark"
- android:scaleType="fitCenter"
- android:gravity="end"
- android:background="@null"/>
+ <com.android.wm.shell.windowdecor.MaximizeButtonView
+ android:id="@+id/maximize_button_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end"/>
<ImageButton
android:id="@+id/close_window"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
index 0db72f7be8e6..dbfd6e5d8d94 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
@@ -15,6 +15,7 @@
~ limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/maximize_menu"
style="?android:attr/buttonBarStyle"
android:layout_width="@dimen/desktop_mode_maximize_menu_width"
android:layout_height="@dimen/desktop_mode_maximize_menu_height"
diff --git a/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
new file mode 100644
index 000000000000..bb6efcec1a70
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
@@ -0,0 +1,36 @@
+<?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.
+ -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <ProgressBar
+ android:id="@+id/progress_bar"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:progressDrawable="@drawable/circular_progress"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:indeterminate="false"
+ android:visibility="invisible"/>
+
+ <ImageButton
+ android:id="@+id/maximize_window"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:padding="9dp"
+ android:contentDescription="@string/maximize_button_text"
+ android:src="@drawable/decor_desktop_mode_maximize_button_dark"
+ android:scaleType="fitCenter"
+ android:background="@drawable/rounded_button"/>
+</merge> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index fae71efe3b39..758dbfd5f3c5 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -66,4 +66,9 @@
<color name="desktop_mode_maximize_menu_button_outline">#797869</color>
<color name="desktop_mode_maximize_menu_button_outline_on_hover">#606219</color>
<color name="desktop_mode_maximize_menu_button_on_hover">#E7E790</color>
+ <color name="desktop_mode_maximize_menu_progress_light">#33000000</color>
+ <color name="desktop_mode_maximize_menu_progress_dark">#33FFFFFF</color>
+ <color name="desktop_mode_caption_button_on_hover_light">#11000000</color>
+ <color name="desktop_mode_caption_button_on_hover_dark">#11FFFFFF</color>
+ <color name="desktop_mode_caption_button">#00000000</color>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 57e964f15d50..5b8ffb30dc4a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -22,6 +22,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
+import static android.view.MotionEvent.ACTION_HOVER_ENTER;
+import static android.view.MotionEvent.ACTION_HOVER_EXIT;
import static android.view.WindowInsets.Type.statusBars;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -311,8 +313,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener
implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener,
- DragDetector.MotionEventHandler {
-
+ View.OnGenericMotionListener , DragDetector.MotionEventHandler {
+ private static final int CLOSE_MAXIMIZE_MENU_DELAY_MS = 150;
private final int mTaskId;
private final WindowContainerToken mTaskToken;
private final DragPositioningCallback mDragPositioningCallback;
@@ -323,6 +325,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
private boolean mHasLongClicked;
private boolean mShouldClick;
private int mDragPointerId = -1;
+ private final Runnable mCloseMaximizeWindowRunnable;
private DesktopModeTouchEventListener(
RunningTaskInfo taskInfo,
@@ -332,6 +335,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mDragPositioningCallback = dragPositioningCallback;
mDragDetector = new DragDetector(this);
mGestureDetector = new GestureDetector(mContext, this);
+ mCloseMaximizeWindowRunnable = () -> {
+ final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
+ if (decoration == null) return;
+ decoration.closeMaximizeMenu();
+ };
}
@Override
@@ -387,13 +395,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mDesktopTasksController.ifPresent(c -> c.moveToNextDisplay(mTaskId));
}
} else if (id == R.id.maximize_window) {
- if (decoration.isMaximizeMenuActive()) {
- decoration.closeMaximizeMenu();
- return;
- }
final RunningTaskInfo taskInfo = decoration.mTaskInfo;
- mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo));
decoration.closeHandleMenu();
+ decoration.closeMaximizeMenu();
+ mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo));
} else if (id == R.id.maximize_menu_maximize_button) {
final RunningTaskInfo taskInfo = decoration.mTaskInfo;
mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo));
@@ -460,6 +465,36 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
return false;
}
+ @Override
+ public boolean onGenericMotion(View v, MotionEvent ev) {
+ final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
+ final int id = v.getId();
+ if (ev.getAction() == ACTION_HOVER_ENTER) {
+ if (!decoration.isMaximizeMenuActive() && id == R.id.maximize_window) {
+ decoration.onMaximizeWindowHoverEnter();
+ } else if (id == R.id.maximize_window
+ || MaximizeMenu.Companion.isMaximizeMenuView(id)) {
+ // Re-hovering over any of the maximize menu views should keep the menu open by
+ // cancelling any attempts to close the menu.
+ mMainHandler.removeCallbacks(mCloseMaximizeWindowRunnable);
+ }
+ return true;
+ } else if (ev.getAction() == ACTION_HOVER_EXIT) {
+ if (!decoration.isMaximizeMenuActive() && id == R.id.maximize_window) {
+ decoration.onMaximizeWindowHoverExit();
+ } else if (id == R.id.maximize_window
+ || MaximizeMenu.Companion.isMaximizeMenuView(id)) {
+ // Close menu if not hovering over maximize menu or maximize button after a
+ // delay to give user a chance to re-enter view or to move from one maximize
+ // menu view to another.
+ mMainHandler.postDelayed(mCloseMaximizeWindowRunnable,
+ CLOSE_MAXIMIZE_MENU_DELAY_MS);
+ }
+ return true;
+ }
+ return false;
+ }
+
private void moveTaskToFront(RunningTaskInfo taskInfo) {
if (!taskInfo.isFocused) {
mDesktopTasksController.ifPresent(c -> c.moveTaskToFront(taskInfo));
@@ -990,7 +1025,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
new DesktopModeTouchEventListener(taskInfo, dragPositioningCallback);
windowDecoration.setCaptionListeners(
- touchEventListener, touchEventListener, touchEventListener);
+ touchEventListener, touchEventListener, touchEventListener, touchEventListener);
windowDecoration.setExclusionRegionListener(mExclusionRegionListener);
windowDecoration.setDragPositioningCallback(dragPositioningCallback);
windowDecoration.setDragDetector(touchEventListener.mDragDetector);
@@ -1036,6 +1071,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
return;
}
decoration.showResizeVeil(t, bounds);
+ decoration.setAnimatingTaskResize(true);
}
@Override
@@ -1050,6 +1086,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
if (decoration == null) return;
decoration.hideResizeVeil();
+ decoration.setAnimatingTaskResize(false);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 185365b2a501..74f460bf1226 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -60,6 +60,8 @@ import com.android.wm.shell.windowdecor.viewholder.DesktopModeAppControlsWindowD
import com.android.wm.shell.windowdecor.viewholder.DesktopModeFocusedWindowDecorationViewHolder;
import com.android.wm.shell.windowdecor.viewholder.DesktopModeWindowDecorationViewHolder;
+import kotlin.Unit;
+
import java.util.function.Supplier;
/**
@@ -79,6 +81,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private View.OnClickListener mOnCaptionButtonClickListener;
private View.OnTouchListener mOnCaptionTouchListener;
private View.OnLongClickListener mOnCaptionLongClickListener;
+ private View.OnGenericMotionListener mOnCaptionGenericMotionListener;
private DragPositioningCallback mDragPositioningCallback;
private DragResizeInputListener mDragResizeListener;
private DragDetector mDragDetector;
@@ -152,10 +155,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
void setCaptionListeners(
View.OnClickListener onCaptionButtonClickListener,
View.OnTouchListener onCaptionTouchListener,
- View.OnLongClickListener onLongClickListener) {
+ View.OnLongClickListener onLongClickListener,
+ View.OnGenericMotionListener onGenericMotionListener) {
mOnCaptionButtonClickListener = onCaptionButtonClickListener;
mOnCaptionTouchListener = onCaptionTouchListener;
mOnCaptionLongClickListener = onLongClickListener;
+ mOnCaptionGenericMotionListener = onGenericMotionListener;
}
void setExclusionRegionListener(ExclusionRegionListener exclusionRegionListener) {
@@ -225,9 +230,15 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mOnCaptionTouchListener,
mOnCaptionButtonClickListener,
mOnCaptionLongClickListener,
+ mOnCaptionGenericMotionListener,
mAppName,
- mAppIconBitmap
- );
+ mAppIconBitmap,
+ () -> {
+ if (!isMaximizeMenuActive()) {
+ createMaximizeMenu();
+ }
+ return Unit.INSTANCE;
+ });
} else {
throw new IllegalArgumentException("Unexpected layout resource id");
}
@@ -548,7 +559,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
*/
void createMaximizeMenu() {
mMaximizeMenu = new MaximizeMenu(mSyncQueue, mRootTaskDisplayAreaOrganizer,
- mDisplayController, mTaskInfo, mOnCaptionButtonClickListener, mContext,
+ mDisplayController, mTaskInfo, mOnCaptionButtonClickListener,
+ mOnCaptionGenericMotionListener, mOnCaptionTouchListener, mContext,
calculateMaximizeMenuPosition(), mSurfaceControlTransactionSupplier);
mMaximizeMenu.show();
}
@@ -776,6 +788,22 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
return R.id.desktop_mode_caption;
}
+ void setAnimatingTaskResize(boolean animatingTaskResize) {
+ if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_focused_window_decor) return;
+ ((DesktopModeAppControlsWindowDecorationViewHolder) mWindowDecorViewHolder)
+ .setAnimatingTaskResize(animatingTaskResize);
+ }
+
+ void onMaximizeWindowHoverExit() {
+ ((DesktopModeAppControlsWindowDecorationViewHolder) mWindowDecorViewHolder)
+ .onMaximizeWindowHoverExit();
+ }
+
+ void onMaximizeWindowHoverEnter() {
+ ((DesktopModeAppControlsWindowDecorationViewHolder) mWindowDecorViewHolder)
+ .onMaximizeWindowHoverEnter();
+ }
+
@Override
public String toString() {
return "{"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
new file mode 100644
index 000000000000..b2f8cfdbfb7a
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.AnimatorSet
+import android.animation.ObjectAnimator
+import android.animation.ValueAnimator
+import android.content.Context
+import android.content.res.ColorStateList
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import android.widget.ImageButton
+import android.widget.ProgressBar
+import androidx.core.animation.doOnEnd
+import androidx.core.animation.doOnStart
+import androidx.core.content.ContextCompat
+import com.android.wm.shell.R
+
+private const val OPEN_MAXIMIZE_MENU_DELAY_ON_HOVER_MS = 350
+private const val MAX_DRAWABLE_ALPHA = 255
+
+class MaximizeButtonView(
+ context: Context,
+ attrs: AttributeSet
+) : FrameLayout(context, attrs) {
+ lateinit var onHoverAnimationFinishedListener: () -> Unit
+ private val hoverProgressAnimatorSet = AnimatorSet()
+ var hoverDisabled = false
+
+ private val progressBar: ProgressBar
+ private val maximizeWindow: ImageButton
+
+ init {
+ LayoutInflater.from(context).inflate(R.layout.maximize_menu_button, this, true)
+
+ progressBar = requireViewById(R.id.progress_bar)
+ maximizeWindow = requireViewById(R.id.maximize_window)
+ }
+
+ fun startHoverAnimation() {
+ if (hoverDisabled) return
+ if (hoverProgressAnimatorSet.isRunning) {
+ cancelHoverAnimation()
+ }
+
+ maximizeWindow.background.alpha = 0
+
+ hoverProgressAnimatorSet.playSequentially(
+ ValueAnimator.ofInt(0, MAX_DRAWABLE_ALPHA)
+ .setDuration(50)
+ .apply {
+ addUpdateListener {
+ maximizeWindow.background.alpha = animatedValue as Int
+ }
+ },
+ ObjectAnimator.ofInt(progressBar, "progress", 100)
+ .setDuration(OPEN_MAXIMIZE_MENU_DELAY_ON_HOVER_MS.toLong())
+ .apply {
+ doOnStart {
+ progressBar.setProgress(0, false)
+ progressBar.visibility = View.VISIBLE
+ }
+ doOnEnd {
+ progressBar.visibility = View.INVISIBLE
+ onHoverAnimationFinishedListener()
+ }
+ }
+ )
+ hoverProgressAnimatorSet.start()
+ }
+
+ fun cancelHoverAnimation() {
+ hoverProgressAnimatorSet.removeAllListeners()
+ hoverProgressAnimatorSet.cancel()
+ progressBar.visibility = View.INVISIBLE
+ }
+
+ fun setAnimationTints(darkMode: Boolean) {
+ if (darkMode) {
+ progressBar.progressTintList = ColorStateList.valueOf(
+ resources.getColor(R.color.desktop_mode_maximize_menu_progress_dark))
+ maximizeWindow.background?.setTintList(ContextCompat.getColorStateList(context,
+ R.color.desktop_mode_caption_button_color_selector_dark))
+ } else {
+ progressBar.progressTintList = ColorStateList.valueOf(
+ resources.getColor(R.color.desktop_mode_maximize_menu_progress_light))
+ maximizeWindow.background?.setTintList(ContextCompat.getColorStateList(context,
+ R.color.desktop_mode_caption_button_color_selector_light))
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
index 794b357c9f16..b82f7ca47ef3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.windowdecor
+import android.annotation.IdRes
import android.app.ActivityManager.RunningTaskInfo
import android.content.Context
import android.content.res.Resources
@@ -27,6 +28,8 @@ import android.view.SurfaceControl
import android.view.SurfaceControl.Transaction
import android.view.SurfaceControlViewHost
import android.view.View.OnClickListener
+import android.view.View.OnGenericMotionListener
+import android.view.View.OnTouchListener
import android.view.WindowManager
import android.view.WindowlessWindowManager
import android.widget.Button
@@ -49,6 +52,8 @@ class MaximizeMenu(
private val displayController: DisplayController,
private val taskInfo: RunningTaskInfo,
private val onClickListener: OnClickListener,
+ private val onGenericMotionListener: OnGenericMotionListener,
+ private val onTouchListener: OnTouchListener,
private val decorWindowContext: Context,
private val menuPosition: PointF,
private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() }
@@ -142,15 +147,26 @@ class MaximizeMenu(
private fun setupMaximizeMenu() {
val maximizeMenuView = maximizeMenu?.mWindowViewHost?.view ?: return
- maximizeMenuView.requireViewById<Button>(
+ maximizeMenuView.setOnGenericMotionListener(onGenericMotionListener)
+ maximizeMenuView.setOnTouchListener(onTouchListener)
+
+ val maximizeButton = maximizeMenuView.requireViewById<Button>(
R.id.maximize_menu_maximize_button
- ).setOnClickListener(onClickListener)
- maximizeMenuView.requireViewById<Button>(
+ )
+ maximizeButton.setOnClickListener(onClickListener)
+ maximizeButton.setOnGenericMotionListener(onGenericMotionListener)
+
+ val snapRightButton = maximizeMenuView.requireViewById<Button>(
R.id.maximize_menu_snap_right_button
- ).setOnClickListener(onClickListener)
- maximizeMenuView.requireViewById<Button>(
+ )
+ snapRightButton.setOnClickListener(onClickListener)
+ snapRightButton.setOnGenericMotionListener(onGenericMotionListener)
+
+ val snapLeftButton = maximizeMenuView.requireViewById<Button>(
R.id.maximize_menu_snap_left_button
- ).setOnClickListener(onClickListener)
+ )
+ snapLeftButton.setOnClickListener(onClickListener)
+ snapLeftButton.setOnGenericMotionListener(onGenericMotionListener)
}
/**
@@ -173,4 +189,12 @@ class MaximizeMenu(
private fun viewsLaidOut(): Boolean {
return maximizeMenu?.mWindowViewHost?.view?.isLaidOut ?: false
}
+
+ companion object {
+ fun isMaximizeMenuView(@IdRes viewId: Int): Boolean {
+ return viewId == R.id.maximize_menu || viewId == R.id.maximize_menu_maximize_button ||
+ viewId == R.id.maximize_menu_snap_left_button ||
+ viewId == R.id.maximize_menu_snap_right_button
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
index 2309c54b6591..7e5b9bd649f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
@@ -21,6 +21,7 @@ import com.android.internal.R.attr.materialColorSurfaceContainerHigh
import com.android.internal.R.attr.materialColorSurfaceContainerLow
import com.android.internal.R.attr.materialColorSurfaceDim
import com.android.wm.shell.R
+import com.android.wm.shell.windowdecor.MaximizeButtonView
/**
* A desktop mode window decoration used when the window is floating (i.e. freeform). It hosts
@@ -32,8 +33,10 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder(
onCaptionTouchListener: View.OnTouchListener,
onCaptionButtonClickListener: View.OnClickListener,
onLongClickListener: OnLongClickListener,
+ onCaptionGenericMotionListener: View.OnGenericMotionListener,
appName: CharSequence,
- appIconBitmap: Bitmap
+ appIconBitmap: Bitmap,
+ onMaximizeHoverAnimationFinishedListener: () -> Unit
) : DesktopModeWindowDecorationViewHolder(rootView) {
private val captionView: View = rootView.requireViewById(R.id.desktop_mode_caption)
@@ -41,6 +44,8 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder(
private val openMenuButton: View = rootView.requireViewById(R.id.open_menu_button)
private val closeWindowButton: ImageButton = rootView.requireViewById(R.id.close_window)
private val expandMenuButton: ImageButton = rootView.requireViewById(R.id.expand_menu_button)
+ private val maximizeButtonView: MaximizeButtonView =
+ rootView.requireViewById(R.id.maximize_button_view)
private val maximizeWindowButton: ImageButton = rootView.requireViewById(R.id.maximize_window)
private val appNameTextView: TextView = rootView.requireViewById(R.id.application_name)
private val appIconImageView: ImageView = rootView.requireViewById(R.id.application_icon)
@@ -55,10 +60,13 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder(
closeWindowButton.setOnClickListener(onCaptionButtonClickListener)
maximizeWindowButton.setOnClickListener(onCaptionButtonClickListener)
maximizeWindowButton.setOnTouchListener(onCaptionTouchListener)
+ maximizeWindowButton.setOnGenericMotionListener(onCaptionGenericMotionListener)
maximizeWindowButton.onLongClickListener = onLongClickListener
closeWindowButton.setOnTouchListener(onCaptionTouchListener)
appNameTextView.text = appName
appIconImageView.setImageBitmap(appIconBitmap)
+ maximizeButtonView.onHoverAnimationFinishedListener =
+ onMaximizeHoverAnimationFinishedListener
}
override fun bindData(taskInfo: RunningTaskInfo) {
@@ -73,12 +81,30 @@ internal class DesktopModeAppControlsWindowDecorationViewHolder(
maximizeWindowButton.imageAlpha = alpha
closeWindowButton.imageAlpha = alpha
expandMenuButton.imageAlpha = alpha
+
+ maximizeButtonView.setAnimationTints(isDarkMode())
}
override fun onHandleMenuOpened() {}
override fun onHandleMenuClosed() {}
+ fun setAnimatingTaskResize(animatingTaskResize: Boolean) {
+ // If animating a task resize, cancel any running hover animations
+ if (animatingTaskResize) {
+ maximizeButtonView.cancelHoverAnimation()
+ }
+ maximizeButtonView.hoverDisabled = animatingTaskResize
+ }
+
+ fun onMaximizeWindowHoverExit() {
+ maximizeButtonView.cancelHoverAnimation()
+ }
+
+ fun onMaximizeWindowHoverEnter() {
+ maximizeButtonView.startHoverAnimation()
+ }
+
@ColorInt
private fun getCaptionBackgroundColor(taskInfo: RunningTaskInfo): Int {
if (isTransparentBackgroundRequested(taskInfo)) {