Merge "Allow non-resizable apps in split-screen (12/n)" into sc-dev

This commit is contained in:
Chris Li
2021-02-22 18:32:20 +00:00
committed by Android (Google) Code Review
9 changed files with 432 additions and 191 deletions

View File

@@ -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" />
android:gravity="center"
android:clipToPadding="false"
android:padding="@dimen/bubble_elevation">
</LinearLayout>
<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>

View File

@@ -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();
}
}

View File

@@ -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;
}
}
}

View File

@@ -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));
}

View File

@@ -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();
}
}
/** Releases the button window. */
/** Creates the restart button hint window. */
private void createSizeCompatHint() {
if (mHint != null) {
// Hint already shown.
return;
}
mHintWindowManager = createHintWindowManager();
mHint = mHintWindowManager.createSizeCompatHint();
updateHintSurfacePosition();
}
@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);
}
}

View File

@@ -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) {

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}