summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml73
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java71
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java87
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java149
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java33
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java85
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java34
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java77
9 files changed, 433 insertions, 192 deletions
diff --git a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
index 347c2b47767e..0dea87c6b7fc 100644
--- a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
+++ b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
@@ -14,35 +14,52 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.wm.shell.sizecompatui.SizeCompatHintPopup
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@android:color/background_light"
- android:orientation="vertical">
+ android:layout_height="wrap_content">
- <TextView
- android:layout_width="180dp"
- android:layout_height="wrap_content"
- android:paddingLeft="10dp"
- android:paddingRight="10dp"
- android:paddingTop="10dp"
- android:text="@string/restart_button_description"
- android:textAlignment="viewStart"
- android:textColor="@android:color/primary_text_light"
- android:textSize="16sp" />
-
- <Button
- android:id="@+id/got_it"
+ <FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:includeFontPadding="false"
- android:layout_gravity="end"
- android:minHeight="36dp"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/got_it"
- android:textAllCaps="true"
- android:textColor="#3c78d8"
- android:textSize="16sp"
- android:textStyle="bold" />
-
-</LinearLayout>
+ android:gravity="center"
+ android:clipToPadding="false"
+ android:padding="@dimen/bubble_elevation">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@android:color/background_light"
+ android:elevation="@dimen/bubble_elevation"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="180dp"
+ android:layout_height="wrap_content"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:paddingTop="10dp"
+ android:text="@string/restart_button_description"
+ android:textAlignment="viewStart"
+ android:textColor="@android:color/primary_text_light"
+ android:textSize="16sp"/>
+
+ <Button
+ android:id="@+id/got_it"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:includeFontPadding="false"
+ android:layout_gravity="end"
+ android:minHeight="36dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:text="@string/got_it"
+ android:textAllCaps="true"
+ android:textColor="#3c78d8"
+ android:textSize="16sp"
+ android:textStyle="bold"/>
+
+ </LinearLayout>
+
+ </FrameLayout>
+
+</com.android.wm.shell.sizecompatui.SizeCompatHintPopup>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java
new file mode 100644
index 000000000000..78af9df30e6a
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2021 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.sizecompatui;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.drawable.RippleDrawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.Button;
+import android.widget.FrameLayout;
+
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.R;
+
+/** Popup to show the hint about the {@link SizeCompatRestartButton}. */
+public class SizeCompatHintPopup extends FrameLayout implements View.OnClickListener {
+
+ private SizeCompatUILayout mLayout;
+
+ public SizeCompatHintPopup(Context context) {
+ super(context);
+ }
+
+ public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public SizeCompatHintPopup(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ void inject(SizeCompatUILayout layout) {
+ mLayout = layout;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ final Button gotItButton = findViewById(R.id.got_it);
+ gotItButton.setBackground(new RippleDrawable(ColorStateList.valueOf(Color.LTGRAY),
+ null /* content */, null /* mask */));
+ gotItButton.setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View v) {
+ mLayout.dismissHint();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
index 9094d7de8d63..08a840297df1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
@@ -22,19 +22,13 @@ import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
-import android.view.LayoutInflater;
import android.view.View;
-import android.view.WindowManager;
-import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.PopupWindow;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
/** Button to restart the size compat activity. */
@@ -42,10 +36,6 @@ public class SizeCompatRestartButton extends FrameLayout implements View.OnClick
View.OnLongClickListener {
private SizeCompatUILayout mLayout;
- private ImageButton mRestartButton;
- @VisibleForTesting
- PopupWindow mShowingHint;
- private WindowManager.LayoutParams mWinParams;
public SizeCompatRestartButton(@NonNull Context context) {
super(context);
@@ -67,24 +57,19 @@ public class SizeCompatRestartButton extends FrameLayout implements View.OnClick
void inject(SizeCompatUILayout layout) {
mLayout = layout;
- mWinParams = layout.getWindowLayoutParams();
- }
-
- void remove() {
- dismissHint();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mRestartButton = findViewById(R.id.size_compat_restart_button);
+ final ImageButton restartButton = findViewById(R.id.size_compat_restart_button);
final ColorStateList color = ColorStateList.valueOf(Color.LTGRAY);
final GradientDrawable mask = new GradientDrawable();
mask.setShape(GradientDrawable.OVAL);
mask.setColor(color);
- mRestartButton.setBackground(new RippleDrawable(color, null /* content */, mask));
- mRestartButton.setOnClickListener(this);
- mRestartButton.setOnLongClickListener(this);
+ restartButton.setBackground(new RippleDrawable(color, null /* content */, mask));
+ restartButton.setOnClickListener(this);
+ restartButton.setOnLongClickListener(this);
}
@Override
@@ -94,69 +79,7 @@ public class SizeCompatRestartButton extends FrameLayout implements View.OnClick
@Override
public boolean onLongClick(View v) {
- showHint();
+ mLayout.onRestartButtonLongClicked();
return true;
}
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (mLayout.mShouldShowHint) {
- mLayout.mShouldShowHint = false;
- showHint();
- }
- }
-
- @Override
- public void setVisibility(@Visibility int visibility) {
- if (visibility == View.GONE && mShowingHint != null) {
- // Also dismiss the popup.
- dismissHint();
- }
- super.setVisibility(visibility);
- }
-
- @Override
- public void setLayoutDirection(int layoutDirection) {
- final int gravity = SizeCompatUILayout.getGravity(layoutDirection);
- if (mWinParams.gravity != gravity) {
- mWinParams.gravity = gravity;
- getContext().getSystemService(WindowManager.class).updateViewLayout(this,
- mWinParams);
- }
- super.setLayoutDirection(layoutDirection);
- }
-
- void showHint() {
- if (mShowingHint != null) {
- return;
- }
-
- // TODO: popup is not attached to the button surface. Need to handle this differently for
- // non-fullscreen task.
- final View popupView = LayoutInflater.from(getContext()).inflate(
- R.layout.size_compat_mode_hint, null);
- final PopupWindow popupWindow = new PopupWindow(popupView,
- LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
- popupWindow.setWindowLayoutType(mWinParams.type);
- popupWindow.setElevation(getResources().getDimension(R.dimen.bubble_elevation));
- popupWindow.setAnimationStyle(android.R.style.Animation_InputMethod);
- popupWindow.setClippingEnabled(false);
- popupWindow.setOnDismissListener(() -> mShowingHint = null);
- mShowingHint = popupWindow;
-
- final Button gotItButton = popupView.findViewById(R.id.got_it);
- gotItButton.setBackground(new RippleDrawable(ColorStateList.valueOf(Color.LTGRAY),
- null /* content */, null /* mask */));
- gotItButton.setOnClickListener(view -> dismissHint());
- popupWindow.showAtLocation(mRestartButton, mWinParams.gravity, mLayout.mPopupOffsetX,
- mLayout.mPopupOffsetY);
- }
-
- void dismissHint() {
- if (mShowingHint != null) {
- mShowingHint.dismiss();
- mShowingHint = null;
- }
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
index a3880f497ff3..c981adee9b5c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
@@ -50,7 +50,7 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
/** Whether the IME is shown on display id. */
private final Set<Integer> mDisplaysWithIme = new ArraySet<>(1);
- /** The showing buttons by task id. */
+ /** The showing UIs by task id. */
private final SparseArray<SizeCompatUILayout> mActiveLayouts = new SparseArray<>(0);
/** Avoid creating display context frequently for non-default display. */
@@ -77,12 +77,12 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
}
/**
- * Called when the Task info changed. Creates and updates the restart button if there is an
- * activity in size compat, or removes the restart button if there is no size compat activity.
+ * Called when the Task info changed. Creates and updates the size compat UI if there is an
+ * activity in size compat, or removes the UI if there is no size compat activity.
*
* @param displayId display the task and activity are in.
* @param taskId task the activity is in.
- * @param taskConfig task config to place the restart button with.
+ * @param taskConfig task config to place the size compat UI with.
* @param sizeCompatActivity the size compat activity in the task. Can be {@code null} if the
* top activity in this Task is not in size compat.
* @param taskListener listener to handle the Task Surface placement.
@@ -94,10 +94,10 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
// Null token means the current foreground activity is not in size compatibility mode.
removeLayout(taskId);
} else if (mActiveLayouts.contains(taskId)) {
- // Button already exists, update the button layout.
+ // UI already exists, update the UI layout.
updateLayout(taskId, taskConfig, sizeCompatActivity, taskListener);
} else {
- // Create a new restart button.
+ // Create a new size compat UI.
createLayout(displayId, taskId, taskConfig, sizeCompatActivity, taskListener);
}
}
@@ -106,7 +106,7 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
public void onDisplayRemoved(int displayId) {
mDisplayContextCache.remove(displayId);
- // Remove all buttons on the removed display.
+ // Remove all size compat UIs on the removed display.
final List<Integer> toRemoveTaskIds = new ArrayList<>();
forAllLayoutsOnDisplay(displayId, layout -> toRemoveTaskIds.add(layout.getTaskId()));
for (int i = toRemoveTaskIds.size() - 1; i >= 0; i--) {
@@ -128,7 +128,7 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
mDisplaysWithIme.remove(displayId);
}
- // Hide the button when input method is showing.
+ // Hide the size compat UIs when input method is showing.
forAllLayoutsOnDisplay(displayId, layout -> layout.updateImeVisibility(isShowing));
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
index 5924b53f822c..32f3648be19a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java
@@ -30,7 +30,6 @@ import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
-import android.view.Gravity;
import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
@@ -43,7 +42,7 @@ import com.android.wm.shell.common.SyncTransactionQueue;
/**
* Records and handles layout of size compat UI on a task with size compat activity. Helps to
- * calculate proper bounds when configuration or button position changes.
+ * calculate proper bounds when configuration or UI position changes.
*/
class SizeCompatUILayout {
private static final String TAG = "SizeCompatUILayout";
@@ -56,12 +55,18 @@ class SizeCompatUILayout {
private IBinder mActivityToken;
private ShellTaskOrganizer.TaskListener mTaskListener;
private DisplayLayout mDisplayLayout;
- @VisibleForTesting
- final SizeCompatUIWindowManager mWindowManager;
@VisibleForTesting
+ final SizeCompatUIWindowManager mButtonWindowManager;
+ @VisibleForTesting
+ @Nullable
+ SizeCompatUIWindowManager mHintWindowManager;
+ @VisibleForTesting
@Nullable
SizeCompatRestartButton mButton;
+ @VisibleForTesting
+ @Nullable
+ SizeCompatHintPopup mHint;
final int mButtonSize;
final int mPopupOffsetX;
final int mPopupOffsetY;
@@ -79,7 +84,7 @@ class SizeCompatUILayout {
mTaskListener = taskListener;
mDisplayLayout = displayLayout;
mShouldShowHint = !hasShownHint;
- mWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this);
+ mButtonWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this);
mButtonSize =
mContext.getResources().getDimensionPixelSize(R.dimen.size_compat_button_size);
@@ -87,21 +92,52 @@ class SizeCompatUILayout {
mPopupOffsetY = mButtonSize;
}
- /** Creates the button window. */
+ /** Creates the activity restart button window. */
void createSizeCompatButton(boolean isImeShowing) {
if (isImeShowing || mButton != null) {
// When ime is showing, wait until ime is dismiss to create UI.
return;
}
- mButton = mWindowManager.createSizeCompatUI();
- updateSurfacePosition();
+ mButton = mButtonWindowManager.createSizeCompatButton();
+ updateButtonSurfacePosition();
+
+ if (mShouldShowHint) {
+ // Only show by default for the first time.
+ mShouldShowHint = false;
+ createSizeCompatHint();
+ }
+ }
+
+ /** Creates the restart button hint window. */
+ private void createSizeCompatHint() {
+ if (mHint != null) {
+ // Hint already shown.
+ return;
+ }
+ mHintWindowManager = createHintWindowManager();
+ mHint = mHintWindowManager.createSizeCompatHint();
+ updateHintSurfacePosition();
}
- /** Releases the button window. */
+ @VisibleForTesting
+ SizeCompatUIWindowManager createHintWindowManager() {
+ return new SizeCompatUIWindowManager(mContext, mTaskConfig, this);
+ }
+
+ /** Dismisses the hint window. */
+ void dismissHint() {
+ mHint = null;
+ if (mHintWindowManager != null) {
+ mHintWindowManager.release();
+ mHintWindowManager = null;
+ }
+ }
+
+ /** Releases the UI windows. */
void release() {
- mButton.remove();
+ dismissHint();
mButton = null;
- mWindowManager.release();
+ mButtonWindowManager.release();
}
/** Called when size compat info changed. */
@@ -115,7 +151,10 @@ class SizeCompatUILayout {
// Update configuration.
mContext = mContext.createConfigurationContext(taskConfig);
- mWindowManager.setConfiguration(taskConfig);
+ mButtonWindowManager.setConfiguration(taskConfig);
+ if (mHintWindowManager != null) {
+ mHintWindowManager.setConfiguration(taskConfig);
+ }
if (mButton == null || prevTaskListener != taskListener) {
// TaskListener changed, recreate the button for new surface parent.
@@ -126,14 +165,19 @@ class SizeCompatUILayout {
if (!taskConfig.windowConfiguration.getBounds()
.equals(prevTaskConfig.windowConfiguration.getBounds())) {
- // Reposition the button surface.
- updateSurfacePosition();
+ // Reposition the UI surfaces.
+ updateButtonSurfacePosition();
+ updateHintSurfacePosition();
}
if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) {
// Update layout for RTL.
mButton.setLayoutDirection(taskConfig.getLayoutDirection());
- updateSurfacePosition();
+ updateButtonSurfacePosition();
+ if (mHint != null) {
+ mHint.setLayoutDirection(taskConfig.getLayoutDirection());
+ updateHintSurfacePosition();
+ }
}
}
@@ -149,8 +193,9 @@ class SizeCompatUILayout {
displayLayout.getStableBounds(curStableBounds);
mDisplayLayout = displayLayout;
if (!prevStableBounds.equals(curStableBounds)) {
- // Stable bounds changed, update button surface position.
- updateSurfacePosition();
+ // Stable bounds changed, update UI surface positions.
+ updateButtonSurfacePosition();
+ updateHintSurfacePosition();
}
}
@@ -162,27 +207,46 @@ class SizeCompatUILayout {
return;
}
+ // Hide size compat UIs when IME is showing.
final int newVisibility = isImeShowing ? View.GONE : View.VISIBLE;
if (mButton.getVisibility() != newVisibility) {
mButton.setVisibility(newVisibility);
}
+ if (mHint != null && mHint.getVisibility() != newVisibility) {
+ mHint.setVisibility(newVisibility);
+ }
}
/** Gets the layout params for restart button. */
- WindowManager.LayoutParams getWindowLayoutParams() {
+ WindowManager.LayoutParams getButtonWindowLayoutParams() {
final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
+ // Cannot be wrap_content as this determines the actual window size
mButtonSize, mButtonSize,
TYPE_APPLICATION_OVERLAY,
FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT);
- winParams.gravity = getGravity(getLayoutDirection());
winParams.token = new Binder();
- winParams.setTitle(SizeCompatRestartButton.class.getSimpleName() + mContext.getDisplayId());
+ winParams.setTitle(SizeCompatRestartButton.class.getSimpleName() + getTaskId());
winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
return winParams;
}
- /** Called when it is ready to be placed button surface button. */
+ /** Gets the layout params for hint popup. */
+ WindowManager.LayoutParams getHintWindowLayoutParams(SizeCompatHintPopup hint) {
+ final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
+ // Cannot be wrap_content as this determines the actual window size
+ hint.getMeasuredWidth(), hint.getMeasuredHeight(),
+ TYPE_APPLICATION_OVERLAY,
+ FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
+ PixelFormat.TRANSLUCENT);
+ winParams.token = new Binder();
+ winParams.setTitle(SizeCompatHintPopup.class.getSimpleName() + getTaskId());
+ winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ winParams.windowAnimations = android.R.style.Animation_InputMethod;
+ return winParams;
+ }
+
+ /** Called when it is ready to be placed size compat UI surface. */
void attachToParentSurface(SurfaceControl.Builder b) {
mTaskListener.attachChildSurfaceToTask(mTaskId, b);
}
@@ -192,13 +256,17 @@ class SizeCompatUILayout {
ActivityClient.getInstance().restartActivityProcessIfVisible(mActivityToken);
}
+ /** Called when the restart button is long clicked. */
+ void onRestartButtonLongClicked() {
+ createSizeCompatHint();
+ }
+
@VisibleForTesting
- void updateSurfacePosition() {
- if (mButton == null || mWindowManager.getSurfaceControl() == null) {
+ void updateButtonSurfacePosition() {
+ if (mButton == null || mButtonWindowManager.getSurfaceControl() == null) {
return;
}
- // The hint popup won't be at the correct position.
- mButton.dismissHint();
+ final SurfaceControl leash = mButtonWindowManager.getSurfaceControl();
// Use stable bounds to prevent the button from overlapping with system bars.
final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
@@ -212,8 +280,30 @@ class SizeCompatUILayout {
: stableBounds.right - taskBounds.left - mButtonSize;
final int positionY = stableBounds.bottom - taskBounds.top - mButtonSize;
- mSyncQueue.runInSync(t ->
- t.setPosition(mWindowManager.getSurfaceControl(), positionX, positionY));
+ mSyncQueue.runInSync(t -> t.setPosition(leash, positionX, positionY));
+ }
+
+ void updateHintSurfacePosition() {
+ if (mHint == null || mHintWindowManager == null
+ || mHintWindowManager.getSurfaceControl() == null) {
+ return;
+ }
+ final SurfaceControl leash = mHintWindowManager.getSurfaceControl();
+
+ // Use stable bounds to prevent the hint from overlapping with system bars.
+ final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
+ final Rect stableBounds = new Rect();
+ mDisplayLayout.getStableBounds(stableBounds);
+ stableBounds.intersect(taskBounds);
+
+ // Position of the hint in the container coordinate.
+ final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+ ? stableBounds.left - taskBounds.left + mPopupOffsetX
+ : stableBounds.right - taskBounds.left - mPopupOffsetX - mHint.getMeasuredWidth();
+ final int positionY =
+ stableBounds.bottom - taskBounds.top - mPopupOffsetY - mHint.getMeasuredHeight();
+
+ mSyncQueue.runInSync(t -> t.setPosition(leash, positionX, positionY));
}
int getDisplayId() {
@@ -227,9 +317,4 @@ class SizeCompatUILayout {
private int getLayoutDirection() {
return mContext.getResources().getConfiguration().getLayoutDirection();
}
-
- static int getGravity(int layoutDirection) {
- return Gravity.BOTTOM
- | (layoutDirection == View.LAYOUT_DIRECTION_RTL ? Gravity.START : Gravity.END);
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
index a7ad982a4736..f634c4586e39 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java
@@ -24,12 +24,14 @@ import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceSession;
+import android.view.View;
import android.view.WindowlessWindowManager;
import com.android.wm.shell.R;
/**
- * Holds view hierarchy of a root surface and helps to inflate {@link SizeCompatRestartButton}.
+ * Holds view hierarchy of a root surface and helps to inflate {@link SizeCompatRestartButton} or
+ * {@link SizeCompatHintPopup}.
*/
class SizeCompatUIWindowManager extends WindowlessWindowManager {
@@ -67,18 +69,39 @@ class SizeCompatUIWindowManager extends WindowlessWindowManager {
}
/** Inflates {@link SizeCompatRestartButton} on to the root surface. */
- SizeCompatRestartButton createSizeCompatUI() {
- if (mViewHost == null) {
- mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+ SizeCompatRestartButton createSizeCompatButton() {
+ if (mViewHost != null) {
+ throw new IllegalStateException(
+ "A UI has already been created with this window manager.");
}
+ mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+
final SizeCompatRestartButton button = (SizeCompatRestartButton)
LayoutInflater.from(mContext).inflate(R.layout.size_compat_ui, null);
button.inject(mLayout);
- mViewHost.setView(button, mLayout.getWindowLayoutParams());
+ mViewHost.setView(button, mLayout.getButtonWindowLayoutParams());
return button;
}
+ /** Inflates {@link SizeCompatHintPopup} on to the root surface. */
+ SizeCompatHintPopup createSizeCompatHint() {
+ if (mViewHost != null) {
+ throw new IllegalStateException(
+ "A UI has already been created with this window manager.");
+ }
+
+ mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+
+ final SizeCompatHintPopup hint = (SizeCompatHintPopup)
+ LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null);
+ // Measure how big the hint is.
+ hint.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+ hint.inject(mLayout);
+ mViewHost.setView(hint, mLayout.getHintWindowLayoutParams(hint));
+ return hint;
+ }
+
/** Releases the surface control and tears down the view hierarchy. */
void release() {
if (mViewHost != null) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java
new file mode 100644
index 000000000000..9845d4650d20
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2021 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.sizecompatui;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.verify;
+
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.testing.AndroidTestingRunner;
+import android.view.LayoutInflater;
+import android.widget.Button;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link SizeCompatHintPopup}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:SizeCompatHintPopupTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class SizeCompatHintPopupTest extends ShellTestCase {
+
+ @Mock private SyncTransactionQueue mSyncTransactionQueue;
+ @Mock private IBinder mActivityToken;
+ @Mock private ShellTaskOrganizer.TaskListener mTaskListener;
+ @Mock private DisplayLayout mDisplayLayout;
+
+ private SizeCompatUILayout mLayout;
+ private SizeCompatHintPopup mHint;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ final int taskId = 1;
+ mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mContext, new Configuration(),
+ taskId, mActivityToken, mTaskListener, mDisplayLayout, false /* hasShownHint*/);
+ mHint = (SizeCompatHintPopup)
+ LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null);
+ mHint.inject(mLayout);
+
+ spyOn(mLayout);
+ }
+
+ @Test
+ public void testOnClick() {
+ doNothing().when(mLayout).dismissHint();
+
+ final Button button = mHint.findViewById(R.id.got_it);
+ button.performClick();
+
+ verify(mLayout).dismissHint();
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java
index d9086a6ccdc1..5a43925a5677 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java
@@ -19,13 +19,13 @@ package com.android.wm.shell.sizecompatui;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.content.res.Configuration;
import android.os.IBinder;
import android.testing.AndroidTestingRunner;
import android.view.LayoutInflater;
+import android.widget.ImageButton;
import androidx.test.filters.SmallTest;
@@ -71,45 +71,25 @@ public class SizeCompatRestartButtonTest extends ShellTestCase {
mButton.inject(mLayout);
spyOn(mLayout);
- spyOn(mButton);
- doNothing().when(mButton).showHint();
}
@Test
public void testOnClick() {
doNothing().when(mLayout).onRestartButtonClicked();
- mButton.onClick(mButton);
+ final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button);
+ button.performClick();
verify(mLayout).onRestartButtonClicked();
}
@Test
public void testOnLongClick() {
- verify(mButton, never()).showHint();
+ doNothing().when(mLayout).onRestartButtonLongClicked();
- mButton.onLongClick(mButton);
+ final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button);
+ button.performLongClick();
- verify(mButton).showHint();
- }
-
- @Test
- public void testOnAttachedToWindow_showHint() {
- mLayout.mShouldShowHint = false;
- mButton.onAttachedToWindow();
-
- verify(mButton, never()).showHint();
-
- mLayout.mShouldShowHint = true;
- mButton.onAttachedToWindow();
-
- verify(mButton).showHint();
- }
-
- @Test
- public void testRemove() {
- mButton.remove();
-
- verify(mButton).dismissHint();
+ verify(mLayout).onRestartButtonLongClicked();
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
index 236db44bdce0..f33cfe86224f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.sizecompatui;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
@@ -27,6 +28,7 @@ import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.ActivityClient;
@@ -68,6 +70,7 @@ public class SizeCompatUILayoutTest extends ShellTestCase {
@Mock private ShellTaskOrganizer.TaskListener mTaskListener;
@Mock private DisplayLayout mDisplayLayout;
@Mock private SizeCompatRestartButton mButton;
+ @Mock private SizeCompatHintPopup mHint;
private Configuration mTaskConfig;
private SizeCompatUILayout mLayout;
@@ -81,8 +84,13 @@ public class SizeCompatUILayoutTest extends ShellTestCase {
TASK_ID, mActivityToken, mTaskListener, mDisplayLayout, false /* hasShownHint*/);
spyOn(mLayout);
- spyOn(mLayout.mWindowManager);
- doReturn(mButton).when(mLayout.mWindowManager).createSizeCompatUI();
+ spyOn(mLayout.mButtonWindowManager);
+ doReturn(mButton).when(mLayout.mButtonWindowManager).createSizeCompatButton();
+
+ final SizeCompatUIWindowManager hintWindowManager = mLayout.createHintWindowManager();
+ spyOn(hintWindowManager);
+ doReturn(mHint).when(hintWindowManager).createSizeCompatHint();
+ doReturn(hintWindowManager).when(mLayout).createHintWindowManager();
}
@Test
@@ -90,24 +98,45 @@ public class SizeCompatUILayoutTest extends ShellTestCase {
// Not create button if IME is showing.
mLayout.createSizeCompatButton(true /* isImeShowing */);
- verify(mLayout.mWindowManager, never()).createSizeCompatUI();
+ verify(mLayout.mButtonWindowManager, never()).createSizeCompatButton();
assertNull(mLayout.mButton);
+ assertNull(mLayout.mHintWindowManager);
+ assertNull(mLayout.mHint);
+ // Not create hint popup.
+ mLayout.mShouldShowHint = false;
mLayout.createSizeCompatButton(false /* isImeShowing */);
- verify(mLayout.mWindowManager).createSizeCompatUI();
+ verify(mLayout.mButtonWindowManager).createSizeCompatButton();
assertNotNull(mLayout.mButton);
+ assertNull(mLayout.mHintWindowManager);
+ assertNull(mLayout.mHint);
+
+ // Create hint popup.
+ mLayout.release();
+ mLayout.mShouldShowHint = true;
+ mLayout.createSizeCompatButton(false /* isImeShowing */);
+
+ verify(mLayout.mButtonWindowManager, times(2)).createSizeCompatButton();
+ assertNotNull(mLayout.mButton);
+ assertNotNull(mLayout.mHintWindowManager);
+ verify(mLayout.mHintWindowManager).createSizeCompatHint();
+ assertNotNull(mLayout.mHint);
+ assertFalse(mLayout.mShouldShowHint);
}
@Test
public void testRelease() {
mLayout.createSizeCompatButton(false /* isImeShowing */);
+ final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager;
mLayout.release();
assertNull(mLayout.mButton);
- verify(mButton).remove();
- verify(mLayout.mWindowManager).release();
+ assertNull(mLayout.mHint);
+ verify(hintWindowManager).release();
+ assertNull(mLayout.mHintWindowManager);
+ verify(mLayout.mButtonWindowManager).release();
}
@Test
@@ -119,7 +148,7 @@ public class SizeCompatUILayoutTest extends ShellTestCase {
mLayout.updateSizeCompatInfo(mTaskConfig, mActivityToken, mTaskListener,
false /* isImeShowing */);
- verify(mLayout, never()).updateSurfacePosition();
+ verify(mLayout, never()).updateButtonSurfacePosition();
verify(mLayout, never()).release();
verify(mLayout, never()).createSizeCompatButton(anyBoolean());
@@ -140,7 +169,8 @@ public class SizeCompatUILayoutTest extends ShellTestCase {
mLayout.updateSizeCompatInfo(newTaskConfiguration, mActivityToken, newTaskListener,
false /* isImeShowing */);
- verify(mLayout).updateSurfacePosition();
+ verify(mLayout).updateButtonSurfacePosition();
+ verify(mLayout).updateHintSurfacePosition();
}
@Test
@@ -152,14 +182,16 @@ public class SizeCompatUILayoutTest extends ShellTestCase {
mContext.getResources(), false, false);
mLayout.updateDisplayLayout(displayLayout1);
- verify(mLayout).updateSurfacePosition();
+ verify(mLayout).updateButtonSurfacePosition();
+ verify(mLayout).updateHintSurfacePosition();
// No update if the display bounds is the same.
clearInvocations(mLayout);
final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo,
mContext.getResources(), false, false);
mLayout.updateDisplayLayout(displayLayout2);
- verify(mLayout, never()).updateSurfacePosition();
+ verify(mLayout, never()).updateButtonSurfacePosition();
+ verify(mLayout, never()).updateHintSurfacePosition();
}
@Test
@@ -203,4 +235,29 @@ public class SizeCompatUILayoutTest extends ShellTestCase {
verify(ActivityClient.getInstance()).restartActivityProcessIfVisible(mActivityToken);
}
+
+ @Test
+ public void testOnRestartButtonLongClicked_showHint() {
+ mLayout.dismissHint();
+
+ assertNull(mLayout.mHint);
+
+ mLayout.onRestartButtonLongClicked();
+
+ assertNotNull(mLayout.mHint);
+ }
+
+ @Test
+ public void testDismissHint() {
+ mLayout.onRestartButtonLongClicked();
+ final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager;
+ assertNotNull(mLayout.mHint);
+ assertNotNull(hintWindowManager);
+
+ mLayout.dismissHint();
+
+ assertNull(mLayout.mHint);
+ assertNull(mLayout.mHintWindowManager);
+ verify(hintWindowManager).release();
+ }
}