summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/res/drawable/decor_caption_menu_background.xml21
-rw-r--r--libs/WindowManager/Shell/res/layout/caption_handle_menu.xml2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java93
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java94
4 files changed, 184 insertions, 26 deletions
diff --git a/libs/WindowManager/Shell/res/drawable/decor_caption_menu_background.xml b/libs/WindowManager/Shell/res/drawable/decor_caption_menu_background.xml
new file mode 100644
index 000000000000..416287d2cbb3
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/decor_caption_menu_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<shape android:shape="rectangle"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@android:color/white" />
+ <corners android:radius="20dp" />
+</shape>
diff --git a/libs/WindowManager/Shell/res/layout/caption_handle_menu.xml b/libs/WindowManager/Shell/res/layout/caption_handle_menu.xml
index d9a140b810f8..582a11cfdb8e 100644
--- a/libs/WindowManager/Shell/res/layout/caption_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/caption_handle_menu.xml
@@ -20,7 +20,7 @@ android:id="@+id/handle_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
-android:background="@drawable/decor_caption_title">
+android:background="@drawable/decor_caption_menu_background">
<Button
style="@style/CaptionButtonStyle"
android:id="@+id/fullscreen_button"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index ca15f0002fac..6acb6b49082b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -44,6 +44,8 @@ import android.view.View;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import androidx.annotation.Nullable;
+
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -71,6 +73,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
private DesktopModeController mDesktopModeController;
private EventReceiver mEventReceiver;
private InputMonitor mInputMonitor;
+ private boolean mTransitionDragActive;
private final SparseArray<CaptionWindowDecoration> mWindowDecorByTaskId = new SparseArray<>();
private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl();
@@ -91,6 +94,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
mDisplayController = displayController;
mSyncQueue = syncQueue;
mDesktopModeController = desktopModeController;
+ mTransitionDragActive = false;
}
@Override
@@ -280,7 +284,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
mDragResizeCallback.onDragResizeEnd(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
if (e.getRawY(dragPointerIdx) <= statusBarHeight
- && windowingMode == WINDOWING_MODE_FREEFORM) {
+ && DesktopModeStatus.isActive(mContext)) {
mDesktopModeController.setDesktopModeActive(false);
}
break;
@@ -298,24 +302,95 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
@Override
public void onInputEvent(InputEvent event) {
boolean handled = false;
- if (event instanceof MotionEvent
- && ((MotionEvent) event).getActionMasked() == MotionEvent.ACTION_UP) {
+ if (event instanceof MotionEvent) {
handled = true;
- CaptionWindowDecorViewModel.this.handleMotionEvent((MotionEvent) event);
+ CaptionWindowDecorViewModel.this.handleReceivedMotionEvent((MotionEvent) event);
}
finishInputEvent(event, handled);
}
}
- // If any input received is outside of caption bounds, turn off handle menu
- private void handleMotionEvent(MotionEvent ev) {
+ /**
+ * Handle MotionEvents relevant to focused task's caption that don't directly touch it
+ * @param ev the {@link MotionEvent} received by {@link EventReceiver}
+ */
+ private void handleReceivedMotionEvent(MotionEvent ev) {
+ if (!DesktopModeStatus.isActive(mContext)) {
+ handleCaptionThroughStatusBar(ev);
+ }
+ handleEventOutsideFocusedCaption(ev);
+ // Prevent status bar from reacting to a caption drag.
+ if (mTransitionDragActive && !DesktopModeStatus.isActive(mContext)) {
+ mInputMonitor.pilferPointers();
+ }
+ }
+
+ // If an UP/CANCEL action is received outside of caption bounds, turn off handle menu
+ private void handleEventOutsideFocusedCaption(MotionEvent ev) {
+ int action = ev.getActionMasked();
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ CaptionWindowDecoration focusedDecor = getFocusedDecor();
+ if (focusedDecor == null) {
+ return;
+ }
+
+ if (!mTransitionDragActive) {
+ focusedDecor.closeHandleMenuIfNeeded(ev);
+ }
+ }
+ }
+
+ /**
+ * Perform caption actions if not able to through normal means.
+ * Turn on desktop mode if handle is dragged below status bar.
+ */
+ private void handleCaptionThroughStatusBar(MotionEvent ev) {
+ switch (ev.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ // Begin drag through status bar if applicable.
+ CaptionWindowDecoration focusedDecor = getFocusedDecor();
+ if (focusedDecor != null && !DesktopModeStatus.isActive(mContext)
+ && focusedDecor.checkTouchEventInHandle(ev)) {
+ mTransitionDragActive = true;
+ }
+ break;
+ }
+ case MotionEvent.ACTION_UP: {
+ CaptionWindowDecoration focusedDecor = getFocusedDecor();
+ if (focusedDecor == null) {
+ mTransitionDragActive = false;
+ return;
+ }
+ if (mTransitionDragActive) {
+ mTransitionDragActive = false;
+ int statusBarHeight = mDisplayController
+ .getDisplayLayout(focusedDecor.mTaskInfo.displayId).stableInsets().top;
+ if (ev.getY() > statusBarHeight) {
+ mDesktopModeController.setDesktopModeActive(true);
+ return;
+ }
+ }
+ focusedDecor.checkClickEvent(ev);
+ break;
+ }
+ case MotionEvent.ACTION_CANCEL: {
+ mTransitionDragActive = false;
+ }
+ }
+ }
+
+ @Nullable
+ private CaptionWindowDecoration getFocusedDecor() {
int size = mWindowDecorByTaskId.size();
+ CaptionWindowDecoration focusedDecor = null;
for (int i = 0; i < size; i++) {
- CaptionWindowDecoration decoration = mWindowDecorByTaskId.valueAt(i);
- if (decoration != null) {
- decoration.closeHandleMenuIfNeeded(ev);
+ CaptionWindowDecoration decor = mWindowDecorByTaskId.valueAt(i);
+ if (decor != null && decor.isFocused()) {
+ focusedDecor = decor;
+ break;
}
}
+ return focusedDecor;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 03cad043ed67..d5ade46d2208 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -23,6 +23,7 @@ import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.VectorDrawable;
import android.os.Handler;
@@ -234,7 +235,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
* Sets the visibility of buttons and color of caption based on desktop mode status
*
*/
- public void setButtonVisibility() {
+ void setButtonVisibility() {
mDesktopActive = DesktopModeStatus.isActive(mContext);
int v = mDesktopActive ? View.VISIBLE : View.GONE;
View caption = mResult.mRootView.findViewById(R.id.caption);
@@ -253,7 +254,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
caption.getBackground().setTint(v == View.VISIBLE ? Color.WHITE : Color.TRANSPARENT);
}
- public boolean isHandleMenuActive() {
+ boolean isHandleMenuActive() {
return mHandleMenu != null;
}
@@ -268,7 +269,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
/**
* Create and display handle menu window
*/
- public void createHandleMenu() {
+ void createHandleMenu() {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
final Resources resources = mDecorWindowContext.getResources();
int x = mRelayoutParams.mCaptionX;
@@ -289,7 +290,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
/**
* Close the handle menu window
*/
- public void closeHandleMenu() {
+ void closeHandleMenu() {
if (!isHandleMenuActive()) return;
mHandleMenu.releaseView();
mHandleMenu = null;
@@ -304,24 +305,85 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
/**
* Close an open handle menu if input is outside of menu coordinates
* @param ev the tapped point to compare against
- * @return
*/
- public void closeHandleMenuIfNeeded(MotionEvent ev) {
- if (mHandleMenu != null) {
- Point positionInParent = mTaskOrganizer.getRunningTaskInfo(mTaskInfo.taskId)
- .positionInParent;
- final Resources resources = mDecorWindowContext.getResources();
- ev.offsetLocation(-mRelayoutParams.mCaptionX, -mRelayoutParams.mCaptionY);
- ev.offsetLocation(-positionInParent.x, -positionInParent.y);
- int width = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionWidthId);
- int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId);
- if (!(ev.getX() >= 0 && ev.getY() >= 0
- && ev.getX() <= width && ev.getY() <= height)) {
+ void closeHandleMenuIfNeeded(MotionEvent ev) {
+ if (isHandleMenuActive()) {
+ if (!checkEventInCaptionView(ev, R.id.caption)) {
closeHandleMenu();
}
}
}
+ boolean isFocused() {
+ return mTaskInfo.isFocused;
+ }
+
+ /**
+ * Offset the coordinates of a {@link MotionEvent} to be in the same coordinate space as caption
+ * @param ev the {@link MotionEvent} to offset
+ * @return the point of the input in local space
+ */
+ private PointF offsetCaptionLocation(MotionEvent ev) {
+ PointF result = new PointF(ev.getX(), ev.getY());
+ Point positionInParent = mTaskOrganizer.getRunningTaskInfo(mTaskInfo.taskId)
+ .positionInParent;
+ result.offset(-mRelayoutParams.mCaptionX, -mRelayoutParams.mCaptionY);
+ result.offset(-positionInParent.x, -positionInParent.y);
+ return result;
+ }
+
+ /**
+ * Determine if a passed MotionEvent is in a view in caption
+ * @param ev the {@link MotionEvent} to check
+ * @param layoutId the id of the view
+ * @return {@code true} if event is inside the specified view, {@code false} if not
+ */
+ private boolean checkEventInCaptionView(MotionEvent ev, int layoutId) {
+ if (mResult.mRootView == null) return false;
+ PointF inputPoint = offsetCaptionLocation(ev);
+ View view = mResult.mRootView.findViewById(layoutId);
+ return view != null && view.pointInView(inputPoint.x, inputPoint.y, 0);
+ }
+
+ boolean checkTouchEventInHandle(MotionEvent ev) {
+ if (isHandleMenuActive()) return false;
+ return checkEventInCaptionView(ev, R.id.caption_handle);
+ }
+
+ /**
+ * Check a passed MotionEvent if a click has occurred on any button on this caption
+ * Note this should only be called when a regular onClick is not possible
+ * (i.e. the button was clicked through status bar layer)
+ * @param ev the MotionEvent to compare
+ */
+ void checkClickEvent(MotionEvent ev) {
+ if (mResult.mRootView == null) return;
+ View caption = mResult.mRootView.findViewById(R.id.caption);
+ PointF inputPoint = offsetCaptionLocation(ev);
+ if (!isHandleMenuActive()) {
+ View handle = caption.findViewById(R.id.caption_handle);
+ clickIfPointInView(inputPoint, handle);
+ } else {
+ View menu = mHandleMenu.mWindowViewHost.getView();
+ View fullscreen = menu.findViewById(R.id.fullscreen_button);
+ if (clickIfPointInView(inputPoint, fullscreen)) return;
+ View desktop = menu.findViewById(R.id.desktop_button);
+ if (clickIfPointInView(inputPoint, desktop)) return;
+ View split = menu.findViewById(R.id.split_screen_button);
+ if (clickIfPointInView(inputPoint, split)) return;
+ View more = menu.findViewById(R.id.more_button);
+ clickIfPointInView(inputPoint, more);
+ }
+ }
+
+ private boolean clickIfPointInView(PointF inputPoint, View v) {
+ if (v.pointInView(inputPoint.x - v.getLeft(), inputPoint.y, 0)) {
+ mOnCaptionButtonClickListener.onClick(v);
+ return true;
+ }
+ return false;
+ }
+
@Override
public void close() {
closeDragResizeListener();