diff --git a/packages/SystemUI/res/drawable/btn_restart.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/btn_restart.xml
rename to libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml
diff --git a/packages/SystemUI/res/layout/size_compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
similarity index 100%
rename from packages/SystemUI/res/layout/size_compat_mode_hint.xml
rename to libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 30ef72c2a3521..b1425e4eeb287 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -146,4 +146,10 @@
Bubble dismissed.
+
+
+ Tap to restart this app and go full screen.
+
+
+ Got it
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
new file mode 100644
index 0000000000000..e47e1ac71c733
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java
@@ -0,0 +1,172 @@
+/*
+ * 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.app.ActivityClient;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.RippleDrawable;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.PopupWindow;
+
+import com.android.wm.shell.R;
+
+/** Button to restart the size compat activity. */
+class SizeCompatRestartButton extends ImageButton implements View.OnClickListener,
+ View.OnLongClickListener {
+ private static final String TAG = "SizeCompatRestartButton";
+
+ final WindowManager.LayoutParams mWinParams;
+ final boolean mShouldShowHint;
+ final int mDisplayId;
+ final int mPopupOffsetX;
+ final int mPopupOffsetY;
+
+ private IBinder mLastActivityToken;
+ private PopupWindow mShowingHint;
+
+ SizeCompatRestartButton(Context context, int displayId, boolean hasShownHint) {
+ super(context);
+ mDisplayId = displayId;
+ mShouldShowHint = !hasShownHint;
+ final Drawable drawable = context.getDrawable(R.drawable.size_compat_restart_button);
+ setImageDrawable(drawable);
+ setContentDescription(context.getString(R.string.restart_button_description));
+
+ final int drawableW = drawable.getIntrinsicWidth();
+ final int drawableH = drawable.getIntrinsicHeight();
+ mPopupOffsetX = drawableW / 2;
+ mPopupOffsetY = drawableH * 2;
+
+ final ColorStateList color = ColorStateList.valueOf(Color.LTGRAY);
+ final GradientDrawable mask = new GradientDrawable();
+ mask.setShape(GradientDrawable.OVAL);
+ mask.setColor(color);
+ setBackground(new RippleDrawable(color, null /* content */, mask));
+ setOnClickListener(this);
+ setOnLongClickListener(this);
+
+ mWinParams = new WindowManager.LayoutParams();
+ mWinParams.gravity = getGravity(getResources().getConfiguration().getLayoutDirection());
+ mWinParams.width = drawableW * 2;
+ mWinParams.height = drawableH * 2;
+ mWinParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+ mWinParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+ mWinParams.format = PixelFormat.TRANSLUCENT;
+ mWinParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ mWinParams.setTitle(SizeCompatRestartButton.class.getSimpleName()
+ + context.getDisplayId());
+ }
+
+ void updateLastTargetActivity(IBinder activityToken) {
+ mLastActivityToken = activityToken;
+ }
+
+ /** @return {@code false} if the target display is invalid. */
+ boolean show() {
+ try {
+ getContext().getSystemService(WindowManager.class).addView(this, mWinParams);
+ } catch (WindowManager.InvalidDisplayException e) {
+ // The target display may have been removed when the callback has just arrived.
+ Log.w(TAG, "Cannot show on display " + getContext().getDisplayId(), e);
+ return false;
+ }
+ return true;
+ }
+
+ void remove() {
+ if (mShowingHint != null) {
+ mShowingHint.dismiss();
+ }
+ getContext().getSystemService(WindowManager.class).removeViewImmediate(this);
+ }
+
+ @Override
+ public void onClick(View v) {
+ ActivityClient.getInstance().restartActivityProcessIfVisible(mLastActivityToken);
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ showHint();
+ return true;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (mShouldShowHint) {
+ showHint();
+ }
+ }
+
+ @Override
+ public void setLayoutDirection(int layoutDirection) {
+ final int gravity = getGravity(layoutDirection);
+ if (mWinParams.gravity != gravity) {
+ mWinParams.gravity = gravity;
+ if (mShowingHint != null) {
+ mShowingHint.dismiss();
+ showHint();
+ }
+ getContext().getSystemService(WindowManager.class).updateViewLayout(this,
+ mWinParams);
+ }
+ super.setLayoutDirection(layoutDirection);
+ }
+
+ void showHint() {
+ if (mShowingHint != null) {
+ return;
+ }
+
+ final View popupView = LayoutInflater.from(getContext()).inflate(
+ R.layout.size_compat_mode_hint, null /* root */);
+ 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 -> popupWindow.dismiss());
+ popupWindow.showAtLocation(this, mWinParams.gravity, mPopupOffsetX, mPopupOffsetY);
+ }
+
+ private 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/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java
index 44f391c953a09..8cb16c87ce6f9 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
@@ -19,7 +19,12 @@ package com.android.wm.shell.sizecompatui;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
import android.os.IBinder;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -27,6 +32,8 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.ShellExecutor;
+import java.lang.ref.WeakReference;
+
/**
* Shows a restart-activity button on Task when the foreground activity is in size compatibility
* mode.
@@ -35,6 +42,11 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
DisplayImeController.ImePositionProcessor {
private static final String TAG = "SizeCompatUI";
+ /** The showing buttons by task id. */
+ private final SparseArray mActiveButtons = new SparseArray<>(1);
+ /** Avoid creating display context frequently for non-default display. */
+ private final SparseArray> mDisplayContextCache = new SparseArray<>(0);
+
@VisibleForTesting
final SizeCompatUI mImpl = new SizeCompatUIImpl();
private final Context mContext;
@@ -42,6 +54,9 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
private final DisplayController mDisplayController;
private final DisplayImeController mImeController;
+ /** Only show once automatically in the process life. */
+ private boolean mHasShownHint;
+
/** Creates the {@link SizeCompatUIController}. */
public static SizeCompatUI create(Context context,
DisplayController displayController,
@@ -67,16 +82,101 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang
private void onSizeCompatInfoChanged(int displayId, int taskId, @Nullable Rect taskBounds,
@Nullable IBinder sizeCompatActivity,
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
- // TODO need to deduplicate task info changed
+ // TODO Draw button on Task surface
+ if (taskBounds == null || sizeCompatActivity == null || taskListener == null) {
+ // Null token means the current foreground activity is not in size compatibility mode.
+ removeRestartButton(taskId);
+ } else {
+ updateRestartButton(displayId, taskId, sizeCompatActivity);
+ }
}
// TODO move from SizeCompatModeActivityController from system UI.
@Override
public void onDisplayRemoved(int displayId) {
+ mDisplayContextCache.remove(displayId);
+ for (int i = 0; i < mActiveButtons.size(); i++) {
+ final int taskId = mActiveButtons.keyAt(i);
+ final SizeCompatRestartButton button = mActiveButtons.get(taskId);
+ if (button != null && button.mDisplayId == displayId) {
+ removeRestartButton(taskId);
+ }
+ }
}
@Override
public void onImeVisibilityChanged(int displayId, boolean isShowing) {
+ final int newVisibility = isShowing ? View.GONE : View.VISIBLE;
+ for (int i = 0; i < mActiveButtons.size(); i++) {
+ final int taskId = mActiveButtons.keyAt(i);
+ final SizeCompatRestartButton button = mActiveButtons.get(taskId);
+ if (button == null || button.mDisplayId != displayId) {
+ continue;
+ }
+
+ // Hide the button when input method is showing.
+ if (button.getVisibility() != newVisibility) {
+ button.setVisibility(newVisibility);
+ }
+ }
+ }
+
+ private void updateRestartButton(int displayId, int taskId, IBinder activityToken) {
+ SizeCompatRestartButton restartButton = mActiveButtons.get(taskId);
+ if (restartButton != null) {
+ restartButton.updateLastTargetActivity(activityToken);
+ return;
+ }
+
+ final Context context = getOrCreateDisplayContext(displayId);
+ if (context == null) {
+ Log.i(TAG, "Cannot get context for display " + displayId);
+ return;
+ }
+
+ restartButton = createRestartButton(context, displayId);
+ restartButton.updateLastTargetActivity(activityToken);
+ if (restartButton.show()) {
+ mActiveButtons.append(taskId, restartButton);
+ } else {
+ onDisplayRemoved(displayId);
+ }
+ }
+
+ @VisibleForTesting
+ SizeCompatRestartButton createRestartButton(Context context, int displayId) {
+ final SizeCompatRestartButton button = new SizeCompatRestartButton(context, displayId,
+ mHasShownHint);
+ // Only show hint for the first time.
+ mHasShownHint = true;
+ return button;
+ }
+
+ private void removeRestartButton(int taskId) {
+ final SizeCompatRestartButton button = mActiveButtons.get(taskId);
+ if (button != null) {
+ button.remove();
+ mActiveButtons.remove(taskId);
+ }
+ }
+
+ private Context getOrCreateDisplayContext(int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ return mContext;
+ }
+ Context context = null;
+ final WeakReference ref = mDisplayContextCache.get(displayId);
+ if (ref != null) {
+ context = ref.get();
+ }
+ if (context == null) {
+ Display display = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
+ if (display != null) {
+ context = mContext.createDisplayContext(display);
+ mDisplayContextCache.put(displayId, new WeakReference<>(context));
+ }
+ }
+ return context;
}
private class SizeCompatUIImpl implements SizeCompatUI {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
new file mode 100644
index 0000000000000..98f01ff08debc
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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 org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link SizeCompatUIController}.
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:SizeCompatUIControllerTest
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class SizeCompatUIControllerTest extends ShellTestCase {
+ private static final int DISPLAY_ID = 0;
+
+ private final TestShellExecutor mShellMainExecutor = new TestShellExecutor();
+
+ private SizeCompatUIController mController;
+ private @Mock DisplayController mMockDisplayController;
+ private @Mock DisplayImeController mMockImeController;
+ private @Mock SizeCompatRestartButton mMockButton;
+ private @Mock IBinder mMockActivityToken;
+ private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ doReturn(true).when(mMockButton).show();
+
+ mController = new SizeCompatUIController(mContext, mMockDisplayController,
+ mMockImeController, mShellMainExecutor) {
+ @Override
+ SizeCompatRestartButton createRestartButton(Context context, int displayId) {
+ return mMockButton;
+ }
+ };
+ }
+
+ @Test
+ public void testListenerRegistered() {
+ verify(mMockDisplayController).addDisplayWindowListener(mController);
+ verify(mMockImeController).addPositionProcessor(mController);
+ }
+
+ @Test
+ public void testOnSizeCompatInfoChanged() {
+ final int taskId = 12;
+ final Rect taskBounds = new Rect(0, 0, 1000, 2000);
+
+ // Verify that the restart button is added with non-null size compat activity.
+ mController.mImpl.onSizeCompatInfoChanged(DISPLAY_ID, taskId, taskBounds,
+ mMockActivityToken, mMockTaskListener);
+ mShellMainExecutor.flushAll();
+
+ verify(mMockButton).show();
+ verify(mMockButton).updateLastTargetActivity(eq(mMockActivityToken));
+
+ // Verify that the restart button is removed with null size compat activity.
+ mController.mImpl.onSizeCompatInfoChanged(DISPLAY_ID, taskId, null, null, null);
+
+ mShellMainExecutor.flushAll();
+ verify(mMockButton).remove();
+ }
+
+ @Test
+ public void testChangeButtonVisibilityOnImeShowHide() {
+ final int taskId = 12;
+ final Rect taskBounds = new Rect(0, 0, 1000, 2000);
+ mController.mImpl.onSizeCompatInfoChanged(DISPLAY_ID, taskId, taskBounds,
+ mMockActivityToken, mMockTaskListener);
+ mShellMainExecutor.flushAll();
+
+ // Verify that the restart button is hidden when IME is visible.
+ doReturn(View.VISIBLE).when(mMockButton).getVisibility();
+ mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
+
+ verify(mMockButton).setVisibility(eq(View.GONE));
+
+ // Verify that the restart button is visible when IME is hidden.
+ doReturn(View.GONE).when(mMockButton).getVisibility();
+ mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);
+
+ verify(mMockButton).setVisibility(eq(View.VISIBLE));
+ }
+}
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4baa06a393a85..5f1afda0afca1 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2580,9 +2580,6 @@
No title
-
- Tap to restart this app and go full screen.
-
Move
diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
index 5384ddfd18bf6..7a52d270f92c4 100644
--- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
@@ -16,284 +16,27 @@
package com.android.systemui;
-import android.app.ActivityClient;
import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.RippleDrawable;
-import android.hardware.display.DisplayManager;
-import android.inputmethodservice.InputMethodService;
-import android.os.IBinder;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.PopupWindow;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.TaskStackChangeListeners;
-import com.android.systemui.statusbar.CommandQueue;
-
-import java.lang.ref.WeakReference;
import javax.inject.Inject;
-/** Shows a restart-activity button when the foreground activity is in size compatibility mode. */
+/**
+ * Shows a restart-activity button when the foreground activity is in size compatibility mode.
+ *
+ * // TODO remove this class after cleanup all dependencies.
+ * @deprecated Use {@link com.android.wm.shell.sizecompatui.SizeCompatUIController}
+ */
+@Deprecated
@SysUISingleton
-public class SizeCompatModeActivityController extends SystemUI implements CommandQueue.Callbacks {
- private static final String TAG = "SizeCompatMode";
+public class SizeCompatModeActivityController extends SystemUI {
- /** The showing buttons by display id. */
- private final SparseArray mActiveButtons = new SparseArray<>(1);
- /** Avoid creating display context frequently for non-default display. */
- private final SparseArray> mDisplayContextCache = new SparseArray<>(0);
- private final CommandQueue mCommandQueue;
-
- /** Only show once automatically in the process life. */
- private boolean mHasShownHint;
-
- @VisibleForTesting
@Inject
- SizeCompatModeActivityController(Context context, TaskStackChangeListeners listeners,
- CommandQueue commandQueue) {
+ SizeCompatModeActivityController(Context context) {
super(context);
- mCommandQueue = commandQueue;
- listeners.registerTaskStackListener(new TaskStackChangeListener() {
- @Override
- public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) {
- // Note the callback already runs on main thread.
- updateRestartButton(displayId, activityToken);
- }
- });
}
@Override
- public void start() {
- mCommandQueue.addCallback(this);
- }
-
- @Override
- public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
- boolean showImeSwitcher) {
- RestartActivityButton button = mActiveButtons.get(displayId);
- if (button == null) {
- return;
- }
- boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
- int newVisibility = imeShown ? View.GONE : View.VISIBLE;
- // Hide the button when input method is showing.
- if (button.getVisibility() != newVisibility) {
- button.setVisibility(newVisibility);
- }
- }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- mDisplayContextCache.remove(displayId);
- removeRestartButton(displayId);
- }
-
- private void removeRestartButton(int displayId) {
- RestartActivityButton button = mActiveButtons.get(displayId);
- if (button != null) {
- button.remove();
- mActiveButtons.remove(displayId);
- }
- }
-
- private void updateRestartButton(int displayId, IBinder activityToken) {
- if (activityToken == null) {
- // Null token means the current foreground activity is not in size compatibility mode.
- removeRestartButton(displayId);
- return;
- }
-
- RestartActivityButton restartButton = mActiveButtons.get(displayId);
- if (restartButton != null) {
- restartButton.updateLastTargetActivity(activityToken);
- return;
- }
-
- Context context = getOrCreateDisplayContext(displayId);
- if (context == null) {
- Log.i(TAG, "Cannot get context for display " + displayId);
- return;
- }
-
- restartButton = createRestartButton(context);
- restartButton.updateLastTargetActivity(activityToken);
- if (restartButton.show()) {
- mActiveButtons.append(displayId, restartButton);
- } else {
- onDisplayRemoved(displayId);
- }
- }
-
- @VisibleForTesting
- RestartActivityButton createRestartButton(Context context) {
- RestartActivityButton button = new RestartActivityButton(context, mHasShownHint);
- mHasShownHint = true;
- return button;
- }
-
- private Context getOrCreateDisplayContext(int displayId) {
- if (displayId == Display.DEFAULT_DISPLAY) {
- return mContext;
- }
- Context context = null;
- WeakReference ref = mDisplayContextCache.get(displayId);
- if (ref != null) {
- context = ref.get();
- }
- if (context == null) {
- Display display = mContext.getSystemService(DisplayManager.class).getDisplay(displayId);
- if (display != null) {
- context = mContext.createDisplayContext(display);
- mDisplayContextCache.put(displayId, new WeakReference(context));
- }
- }
- return context;
- }
-
- @VisibleForTesting
- static class RestartActivityButton extends ImageButton implements View.OnClickListener,
- View.OnLongClickListener {
-
- final WindowManager.LayoutParams mWinParams;
- final boolean mShouldShowHint;
- IBinder mLastActivityToken;
-
- final int mPopupOffsetX;
- final int mPopupOffsetY;
- PopupWindow mShowingHint;
-
- RestartActivityButton(Context context, boolean hasShownHint) {
- super(context);
- mShouldShowHint = !hasShownHint;
- Drawable drawable = context.getDrawable(R.drawable.btn_restart);
- setImageDrawable(drawable);
- setContentDescription(context.getString(R.string.restart_button_description));
-
- int drawableW = drawable.getIntrinsicWidth();
- int drawableH = drawable.getIntrinsicHeight();
- mPopupOffsetX = drawableW / 2;
- mPopupOffsetY = drawableH * 2;
-
- ColorStateList color = ColorStateList.valueOf(Color.LTGRAY);
- GradientDrawable mask = new GradientDrawable();
- mask.setShape(GradientDrawable.OVAL);
- mask.setColor(color);
- setBackground(new RippleDrawable(color, null /* content */, mask));
- setOnClickListener(this);
- setOnLongClickListener(this);
-
- mWinParams = new WindowManager.LayoutParams();
- mWinParams.gravity = getGravity(getResources().getConfiguration().getLayoutDirection());
- mWinParams.width = drawableW * 2;
- mWinParams.height = drawableH * 2;
- mWinParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
- mWinParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
- mWinParams.format = PixelFormat.TRANSLUCENT;
- mWinParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
- mWinParams.setTitle(SizeCompatModeActivityController.class.getSimpleName()
- + context.getDisplayId());
- }
-
- void updateLastTargetActivity(IBinder activityToken) {
- mLastActivityToken = activityToken;
- }
-
- /** @return {@code false} if the target display is invalid. */
- boolean show() {
- try {
- getContext().getSystemService(WindowManager.class).addView(this, mWinParams);
- } catch (WindowManager.InvalidDisplayException e) {
- // The target display may have been removed when the callback has just arrived.
- Log.w(TAG, "Cannot show on display " + getContext().getDisplayId(), e);
- return false;
- }
- return true;
- }
-
- void remove() {
- if (mShowingHint != null) {
- mShowingHint.dismiss();
- }
- getContext().getSystemService(WindowManager.class).removeViewImmediate(this);
- }
-
- @Override
- public void onClick(View v) {
- ActivityClient.getInstance().restartActivityProcessIfVisible(mLastActivityToken);
- }
-
- @Override
- public boolean onLongClick(View v) {
- showHint();
- return true;
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (mShouldShowHint) {
- showHint();
- }
- }
-
- @Override
- public void setLayoutDirection(int layoutDirection) {
- int gravity = getGravity(layoutDirection);
- if (mWinParams.gravity != gravity) {
- mWinParams.gravity = gravity;
- if (mShowingHint != null) {
- mShowingHint.dismiss();
- showHint();
- }
- getContext().getSystemService(WindowManager.class).updateViewLayout(this,
- mWinParams);
- }
- super.setLayoutDirection(layoutDirection);
- }
-
- void showHint() {
- if (mShowingHint != null) {
- return;
- }
-
- View popupView = LayoutInflater.from(getContext()).inflate(
- R.layout.size_compat_mode_hint, null /* root */);
- 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;
-
- Button gotItButton = popupView.findViewById(R.id.got_it);
- gotItButton.setBackground(new RippleDrawable(ColorStateList.valueOf(Color.LTGRAY),
- null /* content */, null /* mask */));
- gotItButton.setOnClickListener(view -> popupWindow.dismiss());
- popupWindow.showAtLocation(this, mWinParams.gravity, mPopupOffsetX, mPopupOffsetY);
- }
-
- private static int getGravity(int layoutDirection) {
- return Gravity.BOTTOM
- | (layoutDirection == View.LAYOUT_DIRECTION_RTL ? Gravity.START : Gravity.END);
- }
- }
+ public void start() { }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
deleted file mode 100644
index 71a0434c41b42..0000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/SizeCompatModeActivityControllerTest.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2019 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.systemui;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.inputmethodservice.InputMethodService;
-import android.os.IBinder;
-import android.testing.AndroidTestingRunner;
-import android.view.View;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SizeCompatModeActivityController.RestartActivityButton;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.TaskStackChangeListeners;
-import com.android.systemui.statusbar.CommandQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * runtest systemui -c com.android.systemui.SizeCompatModeActivityControllerTest
- */
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class SizeCompatModeActivityControllerTest extends SysuiTestCase {
- private static final int DISPLAY_ID = 0;
-
- private SizeCompatModeActivityController mController;
- private TaskStackChangeListener mTaskStackListener;
- private @Mock TaskStackChangeListeners mMockTaskListeners;
- private @Mock RestartActivityButton mMockButton;
- private @Mock IBinder mMockActivityToken;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- doReturn(true).when(mMockButton).show();
-
- mController = new SizeCompatModeActivityController(mContext, mMockTaskListeners,
- new CommandQueue(mContext)) {
- @Override
- RestartActivityButton createRestartButton(Context context) {
- return mMockButton;
- };
- };
-
- ArgumentCaptor listenerCaptor =
- ArgumentCaptor.forClass(TaskStackChangeListener.class);
- verify(mMockTaskListeners).registerTaskStackListener(listenerCaptor.capture());
- mTaskStackListener = listenerCaptor.getValue();
- }
-
- @Test
- public void testOnSizeCompatModeActivityChanged() {
- // Verifies that the restart button is added with non-null component name.
- mTaskStackListener.onSizeCompatModeActivityChanged(DISPLAY_ID, mMockActivityToken);
- verify(mMockButton).show();
- verify(mMockButton).updateLastTargetActivity(eq(mMockActivityToken));
-
- // Verifies that the restart button is removed with null component name.
- mTaskStackListener.onSizeCompatModeActivityChanged(DISPLAY_ID, null /* activityToken */);
- verify(mMockButton).remove();
- }
-
- @Test
- public void testChangeButtonVisibilityOnImeShowHide() {
- mTaskStackListener.onSizeCompatModeActivityChanged(DISPLAY_ID, mMockActivityToken);
-
- // Verifies that the restart button is hidden when IME is visible.
- doReturn(View.VISIBLE).when(mMockButton).getVisibility();
- mController.setImeWindowStatus(DISPLAY_ID, null /* token */, InputMethodService.IME_VISIBLE,
- 0 /* backDisposition */, false /* showImeSwitcher */);
- verify(mMockButton).setVisibility(eq(View.GONE));
-
- // Verifies that the restart button is visible when IME is hidden.
- doReturn(View.GONE).when(mMockButton).getVisibility();
- mController.setImeWindowStatus(DISPLAY_ID, null /* token */, 0 /* vis */,
- 0 /* backDisposition */, false /* showImeSwitcher */);
- verify(mMockButton).setVisibility(eq(View.VISIBLE));
- }
-}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 11cc2c6e856d6..9582fd24c3c95 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5491,12 +5491,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// The callback is only interested in the foreground changes of fullscreen activity.
return;
}
+ // TODO(b/178327644) Update for per Task size compat
if (!r.inSizeCompatMode()) {
if (mLastCompatModeActivity != null) {
- // TODO(b/178327644) Remove notifySizeCompatModeActivityChanged
- mAtmService.getTaskChangeNotificationController()
- .notifySizeCompatModeActivityChanged(mDisplayId, null /* activityToken */);
- // This will do nothing until SizeCompatModeActivityController is moved to shell
organizedTask.onSizeCompatActivityChanged();
}
mLastCompatModeActivity = null;
@@ -5506,10 +5503,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return;
}
mLastCompatModeActivity = r;
- // TODO(b/178327644) Remove notifySizeCompatModeActivityChanged
- mAtmService.getTaskChangeNotificationController()
- .notifySizeCompatModeActivityChanged(mDisplayId, r.appToken);
- // This will do nothing until SizeCompatModeActivityController is moved to shell
organizedTask.onSizeCompatActivityChanged();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 6dfbbc700e9ad..f6e614201a0de 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -51,14 +51,12 @@ import static org.mockito.Mockito.doCallRealMethod;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
-import android.app.TaskStackListener;
import android.app.WindowConfiguration;
import android.compat.testing.PlatformCompatChangeRule;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
-import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.view.WindowManager;
@@ -71,8 +69,6 @@ import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
-import java.util.ArrayList;
-
/**
* Tests for Size Compatibility mode.
*
@@ -477,52 +473,6 @@ public class SizeCompatTests extends WindowTestsBase {
eq(mActivity.app.mName), eq(mActivity.app.mUid), anyString());
}
- /**
- * Ensures that {@link TaskStackListener} can receive callback about the activity in size
- * compatibility mode.
- *
- * TODO(b/178327644) Remove after update DC#handleActivitySizeCompatModeIfNeeded
- */
- @Test
- public void testHandleActivitySizeCompatMode() {
- setUpDisplaySizeWithApp(1000, 2000);
- doReturn(true).when(mTask).isOrganized();
- ActivityRecord activity = mActivity;
- activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
- prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
- assertFitted();
-
- final ArrayList compatTokens = new ArrayList<>();
- mAtm.getTaskChangeNotificationController().registerTaskStackListener(
- new TaskStackListener() {
- @Override
- public void onSizeCompatModeActivityChanged(int displayId,
- IBinder activityToken) {
- compatTokens.add(activityToken);
- }
- });
-
- // Resize the display so that the activity exercises size-compat mode.
- resizeDisplay(mTask.mDisplayContent, 1000, 2500);
-
- // Expect the exact token when the activity is in size compatibility mode.
- assertEquals(1, compatTokens.size());
- assertEquals(activity.appToken, compatTokens.get(0));
-
- compatTokens.clear();
- // Make the activity resizable again by restarting it
- activity.info.resizeMode = RESIZE_MODE_RESIZEABLE;
- activity.mVisibleRequested = true;
- activity.restartProcessIfVisible();
- // The full lifecycle isn't hooked up so manually set state to resumed
- activity.setState(Task.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
- mTask.mDisplayContent.handleActivitySizeCompatModeIfNeeded(activity);
-
- // Expect null token when switching to non-size-compat mode activity.
- assertEquals(1, compatTokens.size());
- assertEquals(null, compatTokens.get(0));
- }
-
/**
* Ensures that {@link TaskOrganizerController} can receive callback about the activity in size
* compatibility mode.