Merge "[1/n] Camera Compat UI: Refactor size compat mode UI controller." into sc-v2-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
aa7a8cdda5
@@ -16,6 +16,6 @@
|
||||
-->
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/size_compat_background"/>
|
||||
<corners android:radius="@dimen/size_compat_hint_corner_radius"/>
|
||||
<solid android:color="@color/compat_controls_background"/>
|
||||
<corners android:radius="@dimen/compat_hint_corner_radius"/>
|
||||
</shape>
|
||||
@@ -15,11 +15,11 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="@dimen/size_compat_hint_point_width"
|
||||
android:width="@dimen/compat_hint_point_width"
|
||||
android:height="8dp"
|
||||
android:viewportWidth="10"
|
||||
android:viewportHeight="8">
|
||||
<path
|
||||
android:fillColor="@color/size_compat_background"
|
||||
android:fillColor="@color/compat_controls_background"
|
||||
android:pathData="M10,0 l-4.1875,6.6875 a1,1 0 0,1 -1.625,0 l-4.1875,-6.6875z"/>
|
||||
</vector>
|
||||
@@ -20,16 +20,16 @@
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:fillColor="@color/size_compat_background"
|
||||
android:fillColor="@color/compat_controls_background"
|
||||
android:pathData="M0,24 a24,24 0 1,0 48,0 a24,24 0 1,0 -48,0" />
|
||||
<group
|
||||
android:translateX="12"
|
||||
android:translateY="12">
|
||||
<path
|
||||
android:fillColor="@color/size_compat_text"
|
||||
android:fillColor="@color/compat_controls_text"
|
||||
android:pathData="M6,13c0,-1.65 0.67,-3.15 1.76,-4.24L6.34,7.34C4.9,8.79 4,10.79 4,13c0,4.08 3.05,7.44 7,7.93v-2.02C8.17,18.43 6,15.97 6,13z"/>
|
||||
<path
|
||||
android:fillColor="@color/size_compat_text"
|
||||
android:fillColor="@color/compat_controls_text"
|
||||
android:pathData="M20,13c0,-4.42 -3.58,-8 -8,-8c-0.06,0 -0.12,0.01 -0.18,0.01v0l1.09,-1.09L11.5,2.5L8,6l3.5,3.5l1.41,-1.41l-1.08,-1.08C11.89,7.01 11.95,7 12,7c3.31,0 6,2.69 6,6c0,2.97 -2.17,5.43 -5,5.91v2.02C16.95,20.44 20,17.08 20,13z"/>
|
||||
</group>
|
||||
</vector>
|
||||
|
||||
46
libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
Normal file
46
libs/WindowManager/Shell/res/layout/compat_mode_hint.xml
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:clipToPadding="false"
|
||||
android:paddingEnd="@dimen/compat_hint_padding_end"
|
||||
android:paddingBottom="5dp"
|
||||
android:clickable="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/compat_mode_hint_text"
|
||||
android:layout_width="188dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:lineSpacingExtra="4sp"
|
||||
android:background="@drawable/compat_hint_bubble"
|
||||
android:padding="16dp"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/compat_controls_text"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:src="@drawable/compat_hint_point"
|
||||
android:paddingHorizontal="@dimen/compat_hint_corner_radius"
|
||||
android:contentDescription="@null"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -14,10 +14,15 @@
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<com.android.wm.shell.sizecompatui.SizeCompatRestartButton
|
||||
<com.android.wm.shell.compatui.CompatUILayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="bottom|end">
|
||||
|
||||
<include android:id="@+id/size_compat_hint"
|
||||
layout="@layout/compat_mode_hint"/>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="@dimen/size_compat_button_width"
|
||||
@@ -36,4 +41,4 @@
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</com.android.wm.shell.sizecompatui.SizeCompatRestartButton>
|
||||
</com.android.wm.shell.compatui.CompatUILayout>
|
||||
@@ -1,58 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<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">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="5dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/size_compat_hint_popup"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:clickable="true">
|
||||
|
||||
<TextView
|
||||
android:layout_width="188dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:lineSpacingExtra="4sp"
|
||||
android:background="@drawable/size_compat_hint_bubble"
|
||||
android:padding="16dp"
|
||||
android:text="@string/restart_button_description"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="@color/size_compat_text"
|
||||
android:textSize="14sp"/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:src="@drawable/size_compat_hint_point"
|
||||
android:paddingHorizontal="@dimen/size_compat_hint_corner_radius"
|
||||
android:contentDescription="@null"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</com.android.wm.shell.sizecompatui.SizeCompatHintPopup>
|
||||
@@ -30,9 +30,9 @@
|
||||
<color name="bubbles_dark">@color/GM2_grey_800</color>
|
||||
<color name="bubbles_icon_tint">@color/GM2_grey_700</color>
|
||||
|
||||
<!-- Size Compat Restart Button -->
|
||||
<color name="size_compat_background">@android:color/system_neutral1_800</color>
|
||||
<color name="size_compat_text">@android:color/system_neutral1_50</color>
|
||||
<!-- Compat controls UI -->
|
||||
<color name="compat_controls_background">@android:color/system_neutral1_800</color>
|
||||
<color name="compat_controls_text">@android:color/system_neutral1_50</color>
|
||||
|
||||
<!-- GM2 colors -->
|
||||
<color name="GM2_grey_200">#E8EAED</color>
|
||||
|
||||
@@ -206,11 +206,15 @@
|
||||
<!-- The height of the size compat restart button including padding. -->
|
||||
<dimen name="size_compat_button_height">64dp</dimen>
|
||||
|
||||
<!-- The radius of the corners of the size compat hint bubble. -->
|
||||
<dimen name="size_compat_hint_corner_radius">28dp</dimen>
|
||||
<!-- The radius of the corners of the compat hint bubble. -->
|
||||
<dimen name="compat_hint_corner_radius">28dp</dimen>
|
||||
|
||||
<!-- The width of the size compat hint point. -->
|
||||
<dimen name="size_compat_hint_point_width">10dp</dimen>
|
||||
<!-- The width of the compat hint point. -->
|
||||
<dimen name="compat_hint_point_width">10dp</dimen>
|
||||
|
||||
<!-- The end padding for the compat hint. Computed as (size_compat_button_width / 2
|
||||
- compat_hint_corner_radius - compat_hint_point_width /2). -->
|
||||
<dimen name="compat_hint_padding_end">7dp</dimen>
|
||||
|
||||
<!-- The width of the brand image on staring surface. -->
|
||||
<dimen name="starting_surface_brand_image_width">200dp</dimen>
|
||||
|
||||
@@ -52,8 +52,8 @@ import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.internal.util.FrameworkStatsLog;
|
||||
import com.android.wm.shell.common.ScreenshotUtils;
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
import com.android.wm.shell.compatui.CompatUIController;
|
||||
import com.android.wm.shell.recents.RecentTasksController;
|
||||
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
|
||||
import com.android.wm.shell.startingsurface.StartingWindowController;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@@ -69,7 +69,7 @@ import java.util.function.Consumer;
|
||||
* TODO(b/167582004): may consider consolidating this class and TaskOrganizer
|
||||
*/
|
||||
public class ShellTaskOrganizer extends TaskOrganizer implements
|
||||
SizeCompatUIController.SizeCompatUICallback {
|
||||
CompatUIController.CompatUICallback {
|
||||
|
||||
// Intentionally using negative numbers here so the positive numbers can be used
|
||||
// for task id specific listeners that will be added later.
|
||||
@@ -98,9 +98,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
|
||||
default void onTaskInfoChanged(RunningTaskInfo taskInfo) {}
|
||||
default void onTaskVanished(RunningTaskInfo taskInfo) {}
|
||||
default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {}
|
||||
/** Whether this task listener supports size compat UI. */
|
||||
default boolean supportSizeCompatUI() {
|
||||
// All TaskListeners should support size compat except PIP.
|
||||
/** Whether this task listener supports compat UI. */
|
||||
default boolean supportCompatUI() {
|
||||
// All TaskListeners should support compat UI except PIP.
|
||||
return true;
|
||||
}
|
||||
/** Attaches the a child window surface to the task surface. */
|
||||
@@ -159,11 +159,11 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
|
||||
private StartingWindowController mStartingWindow;
|
||||
|
||||
/**
|
||||
* In charge of showing size compat UI. Can be {@code null} if device doesn't support size
|
||||
* In charge of showing compat UI. Can be {@code null} if device doesn't support size
|
||||
* compat.
|
||||
*/
|
||||
@Nullable
|
||||
private final SizeCompatUIController mSizeCompatUI;
|
||||
private final CompatUIController mCompatUI;
|
||||
|
||||
@Nullable
|
||||
private final Optional<RecentTasksController> mRecentTasks;
|
||||
@@ -172,32 +172,32 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
|
||||
private RunningTaskInfo mLastFocusedTaskInfo;
|
||||
|
||||
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) {
|
||||
this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */,
|
||||
this(null /* taskOrganizerController */, mainExecutor, context, null /* compatUI */,
|
||||
Optional.empty() /* recentTasksController */);
|
||||
}
|
||||
|
||||
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
|
||||
SizeCompatUIController sizeCompatUI) {
|
||||
this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI,
|
||||
CompatUIController compatUI) {
|
||||
this(null /* taskOrganizerController */, mainExecutor, context, compatUI,
|
||||
Optional.empty() /* recentTasksController */);
|
||||
}
|
||||
|
||||
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
|
||||
SizeCompatUIController sizeCompatUI,
|
||||
CompatUIController compatUI,
|
||||
Optional<RecentTasksController> recentTasks) {
|
||||
this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI,
|
||||
this(null /* taskOrganizerController */, mainExecutor, context, compatUI,
|
||||
recentTasks);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController, ShellExecutor mainExecutor,
|
||||
Context context, @Nullable SizeCompatUIController sizeCompatUI,
|
||||
Context context, @Nullable CompatUIController compatUI,
|
||||
Optional<RecentTasksController> recentTasks) {
|
||||
super(taskOrganizerController, mainExecutor);
|
||||
mSizeCompatUI = sizeCompatUI;
|
||||
mCompatUI = compatUI;
|
||||
mRecentTasks = recentTasks;
|
||||
if (sizeCompatUI != null) {
|
||||
sizeCompatUI.setSizeCompatUICallback(this);
|
||||
if (compatUI != null) {
|
||||
compatUI.setCompatUICallback(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,7 +428,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
|
||||
listener.onTaskAppeared(info.getTaskInfo(), info.getLeash());
|
||||
}
|
||||
notifyLocusVisibilityIfNeeded(info.getTaskInfo());
|
||||
notifySizeCompatUI(info.getTaskInfo(), listener);
|
||||
notifyCompatUI(info.getTaskInfo(), listener);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -459,8 +459,8 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
|
||||
}
|
||||
notifyLocusVisibilityIfNeeded(taskInfo);
|
||||
if (updated || !taskInfo.equalsForSizeCompat(data.getTaskInfo())) {
|
||||
// Notify the size compat UI if the listener or task info changed.
|
||||
notifySizeCompatUI(taskInfo, newListener);
|
||||
// Notify the compat UI if the listener or task info changed.
|
||||
notifyCompatUI(taskInfo, newListener);
|
||||
}
|
||||
if (data.getTaskInfo().getWindowingMode() != taskInfo.getWindowingMode()) {
|
||||
// Notify the recent tasks when a task changes windowing modes
|
||||
@@ -504,8 +504,8 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
|
||||
listener.onTaskVanished(taskInfo);
|
||||
}
|
||||
notifyLocusVisibilityIfNeeded(taskInfo);
|
||||
// Pass null for listener to remove the size compat UI on this task if there is any.
|
||||
notifySizeCompatUI(taskInfo, null /* taskListener */);
|
||||
// Pass null for listener to remove the compat UI on this task if there is any.
|
||||
notifyCompatUI(taskInfo, null /* taskListener */);
|
||||
// Notify the recent tasks that a task has been removed
|
||||
mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskRemoved(taskInfo));
|
||||
}
|
||||
@@ -618,28 +618,28 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies {@link SizeCompatUIController} about the size compat info changed on the give Task
|
||||
* Notifies {@link CompatUIController} about the compat info changed on the give Task
|
||||
* to update the UI accordingly.
|
||||
*
|
||||
* @param taskInfo the new Task info
|
||||
* @param taskListener listener to handle the Task Surface placement. {@code null} if task is
|
||||
* vanished.
|
||||
*/
|
||||
private void notifySizeCompatUI(RunningTaskInfo taskInfo, @Nullable TaskListener taskListener) {
|
||||
if (mSizeCompatUI == null) {
|
||||
private void notifyCompatUI(RunningTaskInfo taskInfo, @Nullable TaskListener taskListener) {
|
||||
if (mCompatUI == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The task is vanished or doesn't support size compat UI, notify to remove size compat UI
|
||||
// The task is vanished or doesn't support compat UI, notify to remove compat UI
|
||||
// on this Task if there is any.
|
||||
if (taskListener == null || !taskListener.supportSizeCompatUI()
|
||||
if (taskListener == null || !taskListener.supportCompatUI()
|
||||
|| !taskInfo.topActivityInSizeCompat || !taskInfo.isVisible) {
|
||||
mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
|
||||
mCompatUI.onCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
|
||||
null /* taskConfig */, null /* taskListener */);
|
||||
return;
|
||||
}
|
||||
|
||||
mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
|
||||
mCompatUI.onCompatInfoChanged(taskInfo.displayId, taskInfo.taskId,
|
||||
taskInfo.configuration, taskListener);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,17 +14,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.wm.shell.sizecompatui;
|
||||
package com.android.wm.shell.compatui;
|
||||
|
||||
import com.android.wm.shell.common.annotations.ExternalThread;
|
||||
|
||||
/**
|
||||
* Interface to engage size compat UI.
|
||||
* Interface to engage compat UI.
|
||||
*/
|
||||
@ExternalThread
|
||||
public interface SizeCompatUI {
|
||||
public interface CompatUI {
|
||||
/**
|
||||
* Called when the keyguard occluded state changes. Removes all size compat UIs if the
|
||||
* Called when the keyguard occluded state changes. Removes all compat UIs if the
|
||||
* keyguard is now occluded.
|
||||
* @param occluded indicates if the keyguard is now occluded.
|
||||
*/
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.wm.shell.sizecompatui;
|
||||
package com.android.wm.shell.compatui;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
@@ -48,20 +48,20 @@ import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Controls to show/update restart-activity buttons on Tasks based on whether the foreground
|
||||
* activities are in size compatibility mode.
|
||||
* activities are in compatibility mode.
|
||||
*/
|
||||
public class SizeCompatUIController implements OnDisplaysChangedListener,
|
||||
public class CompatUIController implements OnDisplaysChangedListener,
|
||||
DisplayImeController.ImePositionProcessor {
|
||||
|
||||
/** Callback for size compat UI interaction. */
|
||||
public interface SizeCompatUICallback {
|
||||
public interface CompatUICallback {
|
||||
/** Called when the size compat restart button appears. */
|
||||
void onSizeCompatRestartButtonAppeared(int taskId);
|
||||
/** Called when the size compat restart button is clicked. */
|
||||
void onSizeCompatRestartButtonClicked(int taskId);
|
||||
}
|
||||
|
||||
private static final String TAG = "SizeCompatUIController";
|
||||
private static final String TAG = "CompatUIController";
|
||||
|
||||
/** Whether the IME is shown on display id. */
|
||||
private final Set<Integer> mDisplaysWithIme = new ArraySet<>(1);
|
||||
@@ -71,7 +71,7 @@ public class SizeCompatUIController implements OnDisplaysChangedListener,
|
||||
new SparseArray<>(0);
|
||||
|
||||
/** The showing UIs by task id. */
|
||||
private final SparseArray<SizeCompatUILayout> mActiveLayouts = new SparseArray<>(0);
|
||||
private final SparseArray<CompatUIWindowManager> mActiveLayouts = new SparseArray<>(0);
|
||||
|
||||
/** Avoid creating display context frequently for non-default display. */
|
||||
private final SparseArray<WeakReference<Context>> mDisplayContextCache = new SparseArray<>(0);
|
||||
@@ -82,17 +82,17 @@ public class SizeCompatUIController implements OnDisplaysChangedListener,
|
||||
private final DisplayImeController mImeController;
|
||||
private final SyncTransactionQueue mSyncQueue;
|
||||
private final ShellExecutor mMainExecutor;
|
||||
private final SizeCompatUIImpl mImpl = new SizeCompatUIImpl();
|
||||
private final CompatUIImpl mImpl = new CompatUIImpl();
|
||||
|
||||
private SizeCompatUICallback mCallback;
|
||||
private CompatUICallback mCallback;
|
||||
|
||||
/** Only show once automatically in the process life. */
|
||||
private boolean mHasShownHint;
|
||||
/** Indicates if the keyguard is currently occluded, in which case size compat UIs shouldn't
|
||||
/** Indicates if the keyguard is currently occluded, in which case compat UIs shouldn't
|
||||
* be shown. */
|
||||
private boolean mKeyguardOccluded;
|
||||
|
||||
public SizeCompatUIController(Context context,
|
||||
public CompatUIController(Context context,
|
||||
DisplayController displayController,
|
||||
DisplayInsetsController displayInsetsController,
|
||||
DisplayImeController imeController,
|
||||
@@ -108,35 +108,36 @@ public class SizeCompatUIController implements OnDisplaysChangedListener,
|
||||
mImeController.addPositionProcessor(this);
|
||||
}
|
||||
|
||||
public SizeCompatUI asSizeCompatUI() {
|
||||
/** Returns implementation of {@link CompatUI}. */
|
||||
public CompatUI asCompatUI() {
|
||||
return mImpl;
|
||||
}
|
||||
|
||||
/** Sets the callback for UI interactions. */
|
||||
public void setSizeCompatUICallback(SizeCompatUICallback callback) {
|
||||
public void setCompatUICallback(CompatUICallback callback) {
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the Task info changed. Creates and updates the size compat UI if there is an
|
||||
* Called when the Task info changed. Creates and updates the 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 size compat UI with.
|
||||
* @param taskConfig task config to place the compat UI with.
|
||||
* @param taskListener listener to handle the Task Surface placement.
|
||||
*/
|
||||
public void onSizeCompatInfoChanged(int displayId, int taskId,
|
||||
public void onCompatInfoChanged(int displayId, int taskId,
|
||||
@Nullable Configuration taskConfig,
|
||||
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
|
||||
if (taskConfig == null || taskListener == null) {
|
||||
// Null token means the current foreground activity is not in size compatibility mode.
|
||||
// Null token means the current foreground activity is not in compatibility mode.
|
||||
removeLayout(taskId);
|
||||
} else if (mActiveLayouts.contains(taskId)) {
|
||||
// UI already exists, update the UI layout.
|
||||
updateLayout(taskId, taskConfig, taskListener);
|
||||
} else {
|
||||
// Create a new size compat UI.
|
||||
// Create a new compat UI.
|
||||
createLayout(displayId, taskId, taskConfig, taskListener);
|
||||
}
|
||||
}
|
||||
@@ -151,7 +152,7 @@ public class SizeCompatUIController implements OnDisplaysChangedListener,
|
||||
mDisplayContextCache.remove(displayId);
|
||||
removeOnInsetsChangedListener(displayId);
|
||||
|
||||
// Remove all size compat UIs on the removed display.
|
||||
// Remove all 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--) {
|
||||
@@ -194,7 +195,7 @@ public class SizeCompatUIController implements OnDisplaysChangedListener,
|
||||
mDisplaysWithIme.remove(displayId);
|
||||
}
|
||||
|
||||
// Hide the size compat UIs when input method is showing.
|
||||
// Hide the compat UIs when input method is showing.
|
||||
forAllLayoutsOnDisplay(displayId,
|
||||
layout -> layout.updateVisibility(showOnDisplay(displayId)));
|
||||
}
|
||||
@@ -202,7 +203,7 @@ public class SizeCompatUIController implements OnDisplaysChangedListener,
|
||||
@VisibleForTesting
|
||||
void onKeyguardOccludedChanged(boolean occluded) {
|
||||
mKeyguardOccluded = occluded;
|
||||
// Hide the size compat UIs when keyguard is occluded.
|
||||
// Hide the compat UIs when keyguard is occluded.
|
||||
forAllLayouts(layout -> layout.updateVisibility(showOnDisplay(layout.getDisplayId())));
|
||||
}
|
||||
|
||||
@@ -222,34 +223,34 @@ public class SizeCompatUIController implements OnDisplaysChangedListener,
|
||||
return;
|
||||
}
|
||||
|
||||
final SizeCompatUILayout layout = createLayout(context, displayId, taskId, taskConfig,
|
||||
taskListener);
|
||||
mActiveLayouts.put(taskId, layout);
|
||||
layout.createSizeCompatButton(showOnDisplay(displayId));
|
||||
final CompatUIWindowManager compatUIWindowManager =
|
||||
createLayout(context, displayId, taskId, taskConfig, taskListener);
|
||||
mActiveLayouts.put(taskId, compatUIWindowManager);
|
||||
compatUIWindowManager.createLayout(showOnDisplay(displayId));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
SizeCompatUILayout createLayout(Context context, int displayId, int taskId,
|
||||
CompatUIWindowManager createLayout(Context context, int displayId, int taskId,
|
||||
Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
|
||||
final SizeCompatUILayout layout = new SizeCompatUILayout(mSyncQueue, mCallback, context,
|
||||
taskConfig, taskId, taskListener, mDisplayController.getDisplayLayout(displayId),
|
||||
mHasShownHint);
|
||||
final CompatUIWindowManager compatUIWindowManager = new CompatUIWindowManager(context,
|
||||
taskConfig, mSyncQueue, mCallback, taskId, taskListener,
|
||||
mDisplayController.getDisplayLayout(displayId), mHasShownHint);
|
||||
// Only show hint for the first time.
|
||||
mHasShownHint = true;
|
||||
return layout;
|
||||
return compatUIWindowManager;
|
||||
}
|
||||
|
||||
private void updateLayout(int taskId, Configuration taskConfig,
|
||||
ShellTaskOrganizer.TaskListener taskListener) {
|
||||
final SizeCompatUILayout layout = mActiveLayouts.get(taskId);
|
||||
final CompatUIWindowManager layout = mActiveLayouts.get(taskId);
|
||||
if (layout == null) {
|
||||
return;
|
||||
}
|
||||
layout.updateSizeCompatInfo(taskConfig, taskListener, showOnDisplay(layout.getDisplayId()));
|
||||
layout.updateCompatInfo(taskConfig, taskListener, showOnDisplay(layout.getDisplayId()));
|
||||
}
|
||||
|
||||
private void removeLayout(int taskId) {
|
||||
final SizeCompatUILayout layout = mActiveLayouts.get(taskId);
|
||||
final CompatUIWindowManager layout = mActiveLayouts.get(taskId);
|
||||
if (layout != null) {
|
||||
layout.release();
|
||||
mActiveLayouts.remove(taskId);
|
||||
@@ -275,19 +276,19 @@ public class SizeCompatUIController implements OnDisplaysChangedListener,
|
||||
return context;
|
||||
}
|
||||
|
||||
private void forAllLayoutsOnDisplay(int displayId, Consumer<SizeCompatUILayout> callback) {
|
||||
private void forAllLayoutsOnDisplay(int displayId, Consumer<CompatUIWindowManager> callback) {
|
||||
forAllLayouts(layout -> layout.getDisplayId() == displayId, callback);
|
||||
}
|
||||
|
||||
private void forAllLayouts(Consumer<SizeCompatUILayout> callback) {
|
||||
private void forAllLayouts(Consumer<CompatUIWindowManager> callback) {
|
||||
forAllLayouts(layout -> true, callback);
|
||||
}
|
||||
|
||||
private void forAllLayouts(Predicate<SizeCompatUILayout> condition,
|
||||
Consumer<SizeCompatUILayout> callback) {
|
||||
private void forAllLayouts(Predicate<CompatUIWindowManager> condition,
|
||||
Consumer<CompatUIWindowManager> callback) {
|
||||
for (int i = 0; i < mActiveLayouts.size(); i++) {
|
||||
final int taskId = mActiveLayouts.keyAt(i);
|
||||
final SizeCompatUILayout layout = mActiveLayouts.get(taskId);
|
||||
final CompatUIWindowManager layout = mActiveLayouts.get(taskId);
|
||||
if (layout != null && condition.test(layout)) {
|
||||
callback.accept(layout);
|
||||
}
|
||||
@@ -298,11 +299,11 @@ public class SizeCompatUIController implements OnDisplaysChangedListener,
|
||||
* The interface for calls from outside the Shell, within the host process.
|
||||
*/
|
||||
@ExternalThread
|
||||
private class SizeCompatUIImpl implements SizeCompatUI {
|
||||
private class CompatUIImpl implements CompatUI {
|
||||
@Override
|
||||
public void onKeyguardOccludedChanged(boolean occluded) {
|
||||
mMainExecutor.execute(() -> {
|
||||
SizeCompatUIController.this.onKeyguardOccludedChanged(occluded);
|
||||
CompatUIController.this.onKeyguardOccludedChanged(occluded);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.compatui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.wm.shell.R;
|
||||
|
||||
/**
|
||||
* Container for compat UI controls.
|
||||
*/
|
||||
public class CompatUILayout extends LinearLayout {
|
||||
|
||||
private CompatUIWindowManager mWindowManager;
|
||||
|
||||
public CompatUILayout(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public CompatUILayout(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public CompatUILayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
this(context, attrs, defStyleAttr, 0);
|
||||
}
|
||||
|
||||
public CompatUILayout(Context context, AttributeSet attrs, int defStyleAttr,
|
||||
int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
void inject(CompatUIWindowManager windowManager) {
|
||||
mWindowManager = windowManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
// Need to relayout after changes like hiding / showing a hint since they affect size.
|
||||
// Doing this directly in setSizeCompatHintVisibility can result in flaky animation.
|
||||
mWindowManager.relayout();
|
||||
}
|
||||
|
||||
void setSizeCompatHintVisibility(boolean show) {
|
||||
final LinearLayout sizeCompatHint = findViewById(R.id.size_compat_hint);
|
||||
int visibility = show ? View.VISIBLE : View.GONE;
|
||||
if (sizeCompatHint.getVisibility() == visibility) {
|
||||
return;
|
||||
}
|
||||
sizeCompatHint.setVisibility(visibility);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
|
||||
final ImageButton restartButton = findViewById(R.id.size_compat_restart_button);
|
||||
restartButton.setOnClickListener(view -> mWindowManager.onRestartButtonClicked());
|
||||
restartButton.setOnLongClickListener(view -> {
|
||||
mWindowManager.onRestartButtonLongClicked();
|
||||
return true;
|
||||
});
|
||||
|
||||
final LinearLayout sizeCompatHint = findViewById(R.id.size_compat_hint);
|
||||
((TextView) sizeCompatHint.findViewById(R.id.compat_mode_hint_text))
|
||||
.setText(R.string.restart_button_description);
|
||||
sizeCompatHint.setOnClickListener(view -> setSizeCompatHintVisibility(/* show= */ false));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* 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.compatui;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Binder;
|
||||
import android.util.Log;
|
||||
import android.view.IWindow;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceControlViewHost;
|
||||
import android.view.SurfaceSession;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowlessWindowManager;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.wm.shell.R;
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.common.DisplayLayout;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
|
||||
/**
|
||||
* Holds view hierarchy of a root surface and helps to inflate and manage layout for compat
|
||||
* controls.
|
||||
*/
|
||||
class CompatUIWindowManager extends WindowlessWindowManager {
|
||||
|
||||
private static final String TAG = "CompatUIWindowManager";
|
||||
|
||||
private final SyncTransactionQueue mSyncQueue;
|
||||
private final CompatUIController.CompatUICallback mCallback;
|
||||
private final int mDisplayId;
|
||||
private final int mTaskId;
|
||||
private final Rect mStableBounds;
|
||||
|
||||
private Context mContext;
|
||||
private Configuration mTaskConfig;
|
||||
private ShellTaskOrganizer.TaskListener mTaskListener;
|
||||
private DisplayLayout mDisplayLayout;
|
||||
|
||||
@VisibleForTesting
|
||||
boolean mShouldShowHint;
|
||||
|
||||
@Nullable
|
||||
@VisibleForTesting
|
||||
CompatUILayout mCompatUILayout;
|
||||
|
||||
@Nullable
|
||||
private SurfaceControlViewHost mViewHost;
|
||||
@Nullable
|
||||
private SurfaceControl mLeash;
|
||||
|
||||
CompatUIWindowManager(Context context, Configuration taskConfig,
|
||||
SyncTransactionQueue syncQueue, CompatUIController.CompatUICallback callback,
|
||||
int taskId, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
|
||||
boolean hasShownHint) {
|
||||
super(taskConfig, null /* rootSurface */, null /* hostInputToken */);
|
||||
mContext = context;
|
||||
mSyncQueue = syncQueue;
|
||||
mCallback = callback;
|
||||
mTaskConfig = taskConfig;
|
||||
mDisplayId = mContext.getDisplayId();
|
||||
mTaskId = taskId;
|
||||
mTaskListener = taskListener;
|
||||
mDisplayLayout = displayLayout;
|
||||
mShouldShowHint = !hasShownHint;
|
||||
mStableBounds = new Rect();
|
||||
mDisplayLayout.getStableBounds(mStableBounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfiguration(Configuration configuration) {
|
||||
super.setConfiguration(configuration);
|
||||
mContext = mContext.createConfigurationContext(configuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
|
||||
// Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
|
||||
final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
|
||||
.setContainerLayer()
|
||||
.setName("CompatUILeash")
|
||||
.setHidden(false)
|
||||
.setCallsite("CompatUIWindowManager#attachToParentSurface");
|
||||
attachToParentSurface(builder);
|
||||
mLeash = builder.build();
|
||||
b.setParent(mLeash);
|
||||
}
|
||||
|
||||
/** Creates the layout for compat controls. */
|
||||
void createLayout(boolean show) {
|
||||
if (!show || mCompatUILayout != null) {
|
||||
// Wait until compat controls should be visible.
|
||||
return;
|
||||
}
|
||||
|
||||
initCompatUi();
|
||||
updateSurfacePosition();
|
||||
|
||||
mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
|
||||
}
|
||||
|
||||
/** Called when compat info changed. */
|
||||
void updateCompatInfo(Configuration taskConfig,
|
||||
ShellTaskOrganizer.TaskListener taskListener, boolean show) {
|
||||
final Configuration prevTaskConfig = mTaskConfig;
|
||||
final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
|
||||
mTaskConfig = taskConfig;
|
||||
mTaskListener = taskListener;
|
||||
|
||||
// Update configuration.
|
||||
mContext = mContext.createConfigurationContext(taskConfig);
|
||||
setConfiguration(taskConfig);
|
||||
|
||||
if (mCompatUILayout == null || prevTaskListener != taskListener) {
|
||||
// TaskListener changed, recreate the layout for new surface parent.
|
||||
release();
|
||||
createLayout(show);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!taskConfig.windowConfiguration.getBounds()
|
||||
.equals(prevTaskConfig.windowConfiguration.getBounds())) {
|
||||
// Reposition the UI surfaces.
|
||||
updateSurfacePosition();
|
||||
}
|
||||
|
||||
if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) {
|
||||
// Update layout for RTL.
|
||||
mCompatUILayout.setLayoutDirection(taskConfig.getLayoutDirection());
|
||||
updateSurfacePosition();
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when the visibility of the UI should change. */
|
||||
void updateVisibility(boolean show) {
|
||||
if (mCompatUILayout == null) {
|
||||
// Layout may not have been created because it was hidden previously.
|
||||
createLayout(show);
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide compat UIs when IME is showing.
|
||||
final int newVisibility = show ? View.VISIBLE : View.GONE;
|
||||
if (mCompatUILayout.getVisibility() != newVisibility) {
|
||||
mCompatUILayout.setVisibility(newVisibility);
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when display layout changed. */
|
||||
void updateDisplayLayout(DisplayLayout displayLayout) {
|
||||
final Rect prevStableBounds = mStableBounds;
|
||||
final Rect curStableBounds = new Rect();
|
||||
displayLayout.getStableBounds(curStableBounds);
|
||||
mDisplayLayout = displayLayout;
|
||||
if (!prevStableBounds.equals(curStableBounds)) {
|
||||
// Stable bounds changed, update UI surface positions.
|
||||
updateSurfacePosition();
|
||||
mStableBounds.set(curStableBounds);
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when it is ready to be placed compat UI surface. */
|
||||
void attachToParentSurface(SurfaceControl.Builder b) {
|
||||
mTaskListener.attachChildSurfaceToTask(mTaskId, b);
|
||||
}
|
||||
|
||||
/** Called when the restart button is clicked. */
|
||||
void onRestartButtonClicked() {
|
||||
mCallback.onSizeCompatRestartButtonClicked(mTaskId);
|
||||
}
|
||||
|
||||
/** Called when the restart button is long clicked. */
|
||||
void onRestartButtonLongClicked() {
|
||||
if (mCompatUILayout == null) {
|
||||
return;
|
||||
}
|
||||
mCompatUILayout.setSizeCompatHintVisibility(/* show= */ true);
|
||||
}
|
||||
|
||||
int getDisplayId() {
|
||||
return mDisplayId;
|
||||
}
|
||||
|
||||
int getTaskId() {
|
||||
return mTaskId;
|
||||
}
|
||||
|
||||
/** Releases the surface control and tears down the view hierarchy. */
|
||||
void release() {
|
||||
mCompatUILayout = null;
|
||||
|
||||
if (mViewHost != null) {
|
||||
mViewHost.release();
|
||||
mViewHost = null;
|
||||
}
|
||||
|
||||
if (mLeash != null) {
|
||||
final SurfaceControl leash = mLeash;
|
||||
mSyncQueue.runInSync(t -> t.remove(leash));
|
||||
mLeash = null;
|
||||
}
|
||||
}
|
||||
|
||||
void relayout() {
|
||||
mViewHost.relayout(getWindowLayoutParams());
|
||||
updateSurfacePosition();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void updateSurfacePosition() {
|
||||
if (mCompatUILayout == null || mLeash == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use stable bounds to prevent controls 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 button in the container coordinate.
|
||||
final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
|
||||
? stableBounds.left - taskBounds.left
|
||||
: stableBounds.right - taskBounds.left - mCompatUILayout.getMeasuredWidth();
|
||||
final int positionY = stableBounds.bottom - taskBounds.top
|
||||
- mCompatUILayout.getMeasuredHeight();
|
||||
|
||||
updateSurfacePosition(positionX, positionY);
|
||||
}
|
||||
|
||||
private int getLayoutDirection() {
|
||||
return mContext.getResources().getConfiguration().getLayoutDirection();
|
||||
}
|
||||
|
||||
private void updateSurfacePosition(int positionX, int positionY) {
|
||||
mSyncQueue.runInSync(t -> {
|
||||
if (mLeash == null || !mLeash.isValid()) {
|
||||
Log.w(TAG, "The leash has been released.");
|
||||
return;
|
||||
}
|
||||
t.setPosition(mLeash, positionX, positionY);
|
||||
// The compat UI should be the topmost child of the Task in case there can be more
|
||||
// than one children.
|
||||
t.setLayer(mLeash, Integer.MAX_VALUE);
|
||||
});
|
||||
}
|
||||
|
||||
/** Inflates {@link CompatUILayout} on to the root surface. */
|
||||
private void initCompatUi() {
|
||||
if (mViewHost != null) {
|
||||
throw new IllegalStateException(
|
||||
"A UI has already been created with this window manager.");
|
||||
}
|
||||
|
||||
// Construction extracted into the separate methods to allow injection for tests.
|
||||
mViewHost = createSurfaceViewHost();
|
||||
mCompatUILayout = inflateCompatUILayout();
|
||||
mCompatUILayout.inject(this);
|
||||
|
||||
mCompatUILayout.setSizeCompatHintVisibility(mShouldShowHint);
|
||||
|
||||
mViewHost.setView(mCompatUILayout, getWindowLayoutParams());
|
||||
|
||||
// Only show by default for the first time.
|
||||
mShouldShowHint = false;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
CompatUILayout inflateCompatUILayout() {
|
||||
return (CompatUILayout) LayoutInflater.from(mContext)
|
||||
.inflate(R.layout.compat_ui_layout, null);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
SurfaceControlViewHost createSurfaceViewHost() {
|
||||
return new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
|
||||
}
|
||||
|
||||
/** Gets the layout params. */
|
||||
private WindowManager.LayoutParams getWindowLayoutParams() {
|
||||
// Measure how big the hint is since its size depends on the text size.
|
||||
mCompatUILayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
|
||||
final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
|
||||
// Cannot be wrap_content as this determines the actual window size
|
||||
mCompatUILayout.getMeasuredWidth(), mCompatUILayout.getMeasuredHeight(),
|
||||
TYPE_APPLICATION_OVERLAY,
|
||||
FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
winParams.token = new Binder();
|
||||
winParams.setTitle(CompatUILayout.class.getSimpleName() + mTaskId);
|
||||
winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
|
||||
return winParams;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -54,6 +54,8 @@ import com.android.wm.shell.common.TransactionPool;
|
||||
import com.android.wm.shell.common.annotations.ShellAnimationThread;
|
||||
import com.android.wm.shell.common.annotations.ShellMainThread;
|
||||
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
|
||||
import com.android.wm.shell.compatui.CompatUI;
|
||||
import com.android.wm.shell.compatui.CompatUIController;
|
||||
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
|
||||
import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
|
||||
import com.android.wm.shell.draganddrop.DragAndDrop;
|
||||
@@ -75,8 +77,6 @@ import com.android.wm.shell.pip.phone.PipAppOpsListener;
|
||||
import com.android.wm.shell.pip.phone.PipTouchHandler;
|
||||
import com.android.wm.shell.recents.RecentTasks;
|
||||
import com.android.wm.shell.recents.RecentTasksController;
|
||||
import com.android.wm.shell.sizecompatui.SizeCompatUI;
|
||||
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
|
||||
import com.android.wm.shell.splitscreen.SplitScreen;
|
||||
import com.android.wm.shell.splitscreen.SplitScreenController;
|
||||
import com.android.wm.shell.startingsurface.StartingSurface;
|
||||
@@ -173,25 +173,25 @@ public abstract class WMShellBaseModule {
|
||||
@Provides
|
||||
static ShellTaskOrganizer provideShellTaskOrganizer(@ShellMainThread ShellExecutor mainExecutor,
|
||||
Context context,
|
||||
SizeCompatUIController sizeCompatUI,
|
||||
CompatUIController compatUI,
|
||||
Optional<RecentTasksController> recentTasksOptional
|
||||
) {
|
||||
return new ShellTaskOrganizer(mainExecutor, context, sizeCompatUI, recentTasksOptional);
|
||||
return new ShellTaskOrganizer(mainExecutor, context, compatUI, recentTasksOptional);
|
||||
}
|
||||
|
||||
@WMSingleton
|
||||
@Provides
|
||||
static SizeCompatUI provideSizeCompatUI(SizeCompatUIController sizeCompatUIController) {
|
||||
return sizeCompatUIController.asSizeCompatUI();
|
||||
static CompatUI provideCompatUI(CompatUIController compatUIController) {
|
||||
return compatUIController.asCompatUI();
|
||||
}
|
||||
|
||||
@WMSingleton
|
||||
@Provides
|
||||
static SizeCompatUIController provideSizeCompatUIController(Context context,
|
||||
static CompatUIController provideCompatUIController(Context context,
|
||||
DisplayController displayController, DisplayInsetsController displayInsetsController,
|
||||
DisplayImeController imeController, SyncTransactionQueue syncQueue,
|
||||
@ShellMainThread ShellExecutor mainExecutor) {
|
||||
return new SizeCompatUIController(context, displayController, displayInsetsController,
|
||||
return new CompatUIController(context, displayController, displayInsetsController,
|
||||
imeController, syncQueue, mainExecutor);
|
||||
}
|
||||
|
||||
|
||||
@@ -778,8 +778,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportSizeCompatUI() {
|
||||
// PIP doesn't support size compat.
|
||||
public boolean supportCompatUI() {
|
||||
// PIP doesn't support compat.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* 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.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
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 LinearLayout hintPopup = findViewById(R.id.size_compat_hint_popup);
|
||||
hintPopup.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mLayout.dismissHint();
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* 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.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.wm.shell.R;
|
||||
|
||||
/** Button to restart the size compat activity. */
|
||||
public class SizeCompatRestartButton extends FrameLayout implements View.OnClickListener,
|
||||
View.OnLongClickListener {
|
||||
|
||||
private SizeCompatUILayout mLayout;
|
||||
|
||||
public SizeCompatRestartButton(@NonNull Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs,
|
||||
int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs,
|
||||
int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
void inject(SizeCompatUILayout layout) {
|
||||
mLayout = layout;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
final ImageButton restartButton = findViewById(R.id.size_compat_restart_button);
|
||||
restartButton.setOnClickListener(this);
|
||||
restartButton.setOnLongClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mLayout.onRestartButtonClicked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
mLayout.onRestartButtonLongClicked();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,344 +0,0 @@
|
||||
/*
|
||||
* 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 android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
|
||||
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Binder;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.wm.shell.R;
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.common.DisplayLayout;
|
||||
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 UI position changes.
|
||||
*/
|
||||
class SizeCompatUILayout {
|
||||
private static final String TAG = "SizeCompatUILayout";
|
||||
|
||||
final SyncTransactionQueue mSyncQueue;
|
||||
private final SizeCompatUIController.SizeCompatUICallback mCallback;
|
||||
private Context mContext;
|
||||
private Configuration mTaskConfig;
|
||||
private final int mDisplayId;
|
||||
private final int mTaskId;
|
||||
private ShellTaskOrganizer.TaskListener mTaskListener;
|
||||
private DisplayLayout mDisplayLayout;
|
||||
private final Rect mStableBounds;
|
||||
private final int mButtonWidth;
|
||||
private final int mButtonHeight;
|
||||
private final int mPopupOffsetX;
|
||||
private final int mPopupOffsetY;
|
||||
|
||||
@VisibleForTesting
|
||||
final SizeCompatUIWindowManager mButtonWindowManager;
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
SizeCompatUIWindowManager mHintWindowManager;
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
SizeCompatRestartButton mButton;
|
||||
@VisibleForTesting
|
||||
@Nullable
|
||||
SizeCompatHintPopup mHint;
|
||||
@VisibleForTesting
|
||||
boolean mShouldShowHint;
|
||||
|
||||
SizeCompatUILayout(SyncTransactionQueue syncQueue,
|
||||
SizeCompatUIController.SizeCompatUICallback callback, Context context,
|
||||
Configuration taskConfig, int taskId, ShellTaskOrganizer.TaskListener taskListener,
|
||||
DisplayLayout displayLayout, boolean hasShownHint) {
|
||||
mSyncQueue = syncQueue;
|
||||
mCallback = callback;
|
||||
mContext = context.createConfigurationContext(taskConfig);
|
||||
mTaskConfig = taskConfig;
|
||||
mDisplayId = mContext.getDisplayId();
|
||||
mTaskId = taskId;
|
||||
mTaskListener = taskListener;
|
||||
mDisplayLayout = displayLayout;
|
||||
mShouldShowHint = !hasShownHint;
|
||||
mButtonWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this);
|
||||
|
||||
mStableBounds = new Rect();
|
||||
mDisplayLayout.getStableBounds(mStableBounds);
|
||||
|
||||
final Resources resources = mContext.getResources();
|
||||
mButtonWidth = resources.getDimensionPixelSize(R.dimen.size_compat_button_width);
|
||||
mButtonHeight = resources.getDimensionPixelSize(R.dimen.size_compat_button_height);
|
||||
mPopupOffsetX = (mButtonWidth / 2) - resources.getDimensionPixelSize(
|
||||
R.dimen.size_compat_hint_corner_radius) - (resources.getDimensionPixelSize(
|
||||
R.dimen.size_compat_hint_point_width) / 2);
|
||||
mPopupOffsetY = mButtonHeight;
|
||||
}
|
||||
|
||||
/** Creates the activity restart button window. */
|
||||
void createSizeCompatButton(boolean show) {
|
||||
if (!show || mButton != null) {
|
||||
// Wait until button should be visible.
|
||||
return;
|
||||
}
|
||||
mButton = mButtonWindowManager.createSizeCompatButton();
|
||||
updateButtonSurfacePosition();
|
||||
|
||||
if (mShouldShowHint) {
|
||||
// Only show by default for the first time.
|
||||
mShouldShowHint = false;
|
||||
createSizeCompatHint();
|
||||
}
|
||||
|
||||
mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
|
||||
}
|
||||
|
||||
/** 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() {
|
||||
dismissHint();
|
||||
mButton = null;
|
||||
mButtonWindowManager.release();
|
||||
}
|
||||
|
||||
/** Called when size compat info changed. */
|
||||
void updateSizeCompatInfo(Configuration taskConfig,
|
||||
ShellTaskOrganizer.TaskListener taskListener, boolean show) {
|
||||
final Configuration prevTaskConfig = mTaskConfig;
|
||||
final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
|
||||
mTaskConfig = taskConfig;
|
||||
mTaskListener = taskListener;
|
||||
|
||||
// Update configuration.
|
||||
mContext = mContext.createConfigurationContext(taskConfig);
|
||||
mButtonWindowManager.setConfiguration(taskConfig);
|
||||
if (mHintWindowManager != null) {
|
||||
mHintWindowManager.setConfiguration(taskConfig);
|
||||
}
|
||||
|
||||
if (mButton == null || prevTaskListener != taskListener) {
|
||||
// TaskListener changed, recreate the button for new surface parent.
|
||||
release();
|
||||
createSizeCompatButton(show);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!taskConfig.windowConfiguration.getBounds()
|
||||
.equals(prevTaskConfig.windowConfiguration.getBounds())) {
|
||||
// Reposition the UI surfaces.
|
||||
updateAllSurfacePositions();
|
||||
}
|
||||
|
||||
if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) {
|
||||
// Update layout for RTL.
|
||||
mButton.setLayoutDirection(taskConfig.getLayoutDirection());
|
||||
updateButtonSurfacePosition();
|
||||
if (mHint != null) {
|
||||
mHint.setLayoutDirection(taskConfig.getLayoutDirection());
|
||||
updateHintSurfacePosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when display layout changed. */
|
||||
void updateDisplayLayout(DisplayLayout displayLayout) {
|
||||
final Rect prevStableBounds = mStableBounds;
|
||||
final Rect curStableBounds = new Rect();
|
||||
displayLayout.getStableBounds(curStableBounds);
|
||||
mDisplayLayout = displayLayout;
|
||||
if (!prevStableBounds.equals(curStableBounds)) {
|
||||
// Stable bounds changed, update UI surface positions.
|
||||
updateAllSurfacePositions();
|
||||
mStableBounds.set(curStableBounds);
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when the visibility of the UI should change. */
|
||||
void updateVisibility(boolean show) {
|
||||
if (mButton == null) {
|
||||
// Button may not have been created because it was hidden previously.
|
||||
createSizeCompatButton(show);
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide size compat UIs when IME is showing.
|
||||
final int newVisibility = show ? View.VISIBLE : View.GONE;
|
||||
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 getButtonWindowLayoutParams() {
|
||||
final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
|
||||
// Cannot be wrap_content as this determines the actual window size
|
||||
mButtonWidth, mButtonHeight,
|
||||
TYPE_APPLICATION_OVERLAY,
|
||||
FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
winParams.token = new Binder();
|
||||
winParams.setTitle(SizeCompatRestartButton.class.getSimpleName() + getTaskId());
|
||||
winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
|
||||
return winParams;
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/** Called when the restart button is clicked. */
|
||||
void onRestartButtonClicked() {
|
||||
mCallback.onSizeCompatRestartButtonClicked(mTaskId);
|
||||
}
|
||||
|
||||
/** Called when the restart button is long clicked. */
|
||||
void onRestartButtonLongClicked() {
|
||||
createSizeCompatHint();
|
||||
}
|
||||
|
||||
private void updateAllSurfacePositions() {
|
||||
updateButtonSurfacePosition();
|
||||
updateHintSurfacePosition();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void updateButtonSurfacePosition() {
|
||||
if (mButton == null || mButtonWindowManager.getSurfaceControl() == null) {
|
||||
return;
|
||||
}
|
||||
final SurfaceControl leash = mButtonWindowManager.getSurfaceControl();
|
||||
|
||||
// Use stable bounds to prevent the button 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 button in the container coordinate.
|
||||
final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
|
||||
? stableBounds.left - taskBounds.left
|
||||
: stableBounds.right - taskBounds.left - mButtonWidth;
|
||||
final int positionY = stableBounds.bottom - taskBounds.top - mButtonHeight;
|
||||
|
||||
updateSurfacePosition(leash, positionX, positionY);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
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();
|
||||
|
||||
updateSurfacePosition(leash, positionX, positionY);
|
||||
}
|
||||
|
||||
private void updateSurfacePosition(SurfaceControl leash, int positionX, int positionY) {
|
||||
mSyncQueue.runInSync(t -> {
|
||||
if (!leash.isValid()) {
|
||||
Log.w(TAG, "The leash has been released.");
|
||||
return;
|
||||
}
|
||||
t.setPosition(leash, positionX, positionY);
|
||||
// The size compat UI should be the topmost child of the Task in case there can be more
|
||||
// than one children.
|
||||
t.setLayer(leash, Integer.MAX_VALUE);
|
||||
});
|
||||
}
|
||||
|
||||
int getDisplayId() {
|
||||
return mDisplayId;
|
||||
}
|
||||
|
||||
int getTaskId() {
|
||||
return mTaskId;
|
||||
}
|
||||
|
||||
private int getLayoutDirection() {
|
||||
return mContext.getResources().getConfiguration().getLayoutDirection();
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
/*
|
||||
* 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.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.view.IWindow;
|
||||
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} or
|
||||
* {@link SizeCompatHintPopup}.
|
||||
*/
|
||||
class SizeCompatUIWindowManager extends WindowlessWindowManager {
|
||||
|
||||
private Context mContext;
|
||||
private final SizeCompatUILayout mLayout;
|
||||
|
||||
@Nullable
|
||||
private SurfaceControlViewHost mViewHost;
|
||||
@Nullable
|
||||
private SurfaceControl mLeash;
|
||||
|
||||
SizeCompatUIWindowManager(Context context, Configuration config, SizeCompatUILayout layout) {
|
||||
super(config, null /* rootSurface */, null /* hostInputToken */);
|
||||
mContext = context;
|
||||
mLayout = layout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfiguration(Configuration configuration) {
|
||||
super.setConfiguration(configuration);
|
||||
mContext = mContext.createConfigurationContext(configuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
|
||||
// Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
|
||||
final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
|
||||
.setContainerLayer()
|
||||
.setName("SizeCompatUILeash")
|
||||
.setHidden(false)
|
||||
.setCallsite("SizeCompatUIWindowManager#attachToParentSurface");
|
||||
mLayout.attachToParentSurface(builder);
|
||||
mLeash = builder.build();
|
||||
b.setParent(mLeash);
|
||||
}
|
||||
|
||||
/** Inflates {@link SizeCompatRestartButton} on to the root surface. */
|
||||
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.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) {
|
||||
mViewHost.release();
|
||||
mViewHost = null;
|
||||
}
|
||||
|
||||
if (mLeash != null) {
|
||||
final SurfaceControl leash = mLeash;
|
||||
mLayout.mSyncQueue.runInSync(t -> t.remove(leash));
|
||||
mLeash = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets {@link SurfaceControl} of the surface holding size compat UI view. @return {@code null}
|
||||
* if not feasible.
|
||||
*/
|
||||
@Nullable
|
||||
SurfaceControl getSurfaceControl() {
|
||||
return mLeash;
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ import androidx.test.filters.SmallTest;
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
import com.android.wm.shell.common.TransactionPool;
|
||||
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
|
||||
import com.android.wm.shell.compatui.CompatUIController;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -82,7 +82,7 @@ public class ShellTaskOrganizerTests {
|
||||
@Mock
|
||||
private Context mContext;
|
||||
@Mock
|
||||
private SizeCompatUIController mSizeCompatUI;
|
||||
private CompatUIController mCompatUI;
|
||||
|
||||
ShellTaskOrganizer mOrganizer;
|
||||
private final SyncTransactionQueue mSyncTransactionQueue = mock(SyncTransactionQueue.class);
|
||||
@@ -132,7 +132,7 @@ public class ShellTaskOrganizerTests {
|
||||
.when(mTaskOrganizerController).registerTaskOrganizer(any());
|
||||
} catch (RemoteException e) {}
|
||||
mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mTestExecutor, mContext,
|
||||
mSizeCompatUI, Optional.empty()));
|
||||
mCompatUI, Optional.empty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -334,34 +334,34 @@ public class ShellTaskOrganizerTests {
|
||||
mOrganizer.onTaskAppeared(taskInfo1, null);
|
||||
|
||||
// sizeCompatActivity is null if top activity is not in size compat.
|
||||
verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
|
||||
verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
|
||||
null /* taskConfig */, null /* taskListener */);
|
||||
|
||||
// sizeCompatActivity is non-null if top activity is in size compat.
|
||||
clearInvocations(mSizeCompatUI);
|
||||
clearInvocations(mCompatUI);
|
||||
final RunningTaskInfo taskInfo2 =
|
||||
createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
|
||||
taskInfo2.displayId = taskInfo1.displayId;
|
||||
taskInfo2.topActivityInSizeCompat = true;
|
||||
taskInfo2.isVisible = true;
|
||||
mOrganizer.onTaskInfoChanged(taskInfo2);
|
||||
verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
|
||||
verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
|
||||
taskInfo1.configuration, taskListener);
|
||||
|
||||
// Not show size compat UI if task is not visible.
|
||||
clearInvocations(mSizeCompatUI);
|
||||
clearInvocations(mCompatUI);
|
||||
final RunningTaskInfo taskInfo3 =
|
||||
createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode());
|
||||
taskInfo3.displayId = taskInfo1.displayId;
|
||||
taskInfo3.topActivityInSizeCompat = true;
|
||||
taskInfo3.isVisible = false;
|
||||
mOrganizer.onTaskInfoChanged(taskInfo3);
|
||||
verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
|
||||
verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
|
||||
null /* taskConfig */, null /* taskListener */);
|
||||
|
||||
clearInvocations(mSizeCompatUI);
|
||||
clearInvocations(mCompatUI);
|
||||
mOrganizer.onTaskVanished(taskInfo1);
|
||||
verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
|
||||
verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId,
|
||||
null /* taskConfig */, null /* taskListener */);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.wm.shell.sizecompatui;
|
||||
package com.android.wm.shell.compatui;
|
||||
|
||||
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
|
||||
|
||||
@@ -56,18 +56,18 @@ import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
/**
|
||||
* Tests for {@link SizeCompatUIController}.
|
||||
* Tests for {@link CompatUIController}.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* atest WMShellUnitTests:SizeCompatUIControllerTest
|
||||
* atest WMShellUnitTests:CompatUIControllerTest
|
||||
*/
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@SmallTest
|
||||
public class SizeCompatUIControllerTest extends ShellTestCase {
|
||||
public class CompatUIControllerTest extends ShellTestCase {
|
||||
private static final int DISPLAY_ID = 0;
|
||||
private static final int TASK_ID = 12;
|
||||
|
||||
private SizeCompatUIController mController;
|
||||
private CompatUIController mController;
|
||||
private @Mock DisplayController mMockDisplayController;
|
||||
private @Mock DisplayInsetsController mMockDisplayInsetsController;
|
||||
private @Mock DisplayLayout mMockDisplayLayout;
|
||||
@@ -75,7 +75,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
|
||||
private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener;
|
||||
private @Mock SyncTransactionQueue mMockSyncQueue;
|
||||
private @Mock ShellExecutor mMockExecutor;
|
||||
private @Mock SizeCompatUILayout mMockLayout;
|
||||
private @Mock CompatUIWindowManager mMockLayout;
|
||||
|
||||
@Captor
|
||||
ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
|
||||
@@ -87,10 +87,10 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
|
||||
doReturn(mMockDisplayLayout).when(mMockDisplayController).getDisplayLayout(anyInt());
|
||||
doReturn(DISPLAY_ID).when(mMockLayout).getDisplayId();
|
||||
doReturn(TASK_ID).when(mMockLayout).getTaskId();
|
||||
mController = new SizeCompatUIController(mContext, mMockDisplayController,
|
||||
mController = new CompatUIController(mContext, mMockDisplayController,
|
||||
mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor) {
|
||||
@Override
|
||||
SizeCompatUILayout createLayout(Context context, int displayId, int taskId,
|
||||
CompatUIWindowManager createLayout(Context context, int displayId, int taskId,
|
||||
Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
|
||||
return mMockLayout;
|
||||
}
|
||||
@@ -105,24 +105,24 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnSizeCompatInfoChanged() {
|
||||
public void testOnCompatInfoChanged() {
|
||||
final Configuration taskConfig = new Configuration();
|
||||
|
||||
// Verify that the restart button is added with non-null size compat info.
|
||||
mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
|
||||
mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
|
||||
|
||||
verify(mController).createLayout(any(), eq(DISPLAY_ID), eq(TASK_ID), eq(taskConfig),
|
||||
eq(mMockTaskListener));
|
||||
|
||||
// Verify that the restart button is updated with non-null new size compat info.
|
||||
final Configuration newTaskConfig = new Configuration();
|
||||
mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, newTaskConfig, mMockTaskListener);
|
||||
mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, newTaskConfig, mMockTaskListener);
|
||||
|
||||
verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener,
|
||||
verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
|
||||
true /* show */);
|
||||
|
||||
// Verify that the restart button is removed with null size compat info.
|
||||
mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, null, mMockTaskListener);
|
||||
mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, null, mMockTaskListener);
|
||||
|
||||
verify(mMockLayout).release();
|
||||
}
|
||||
@@ -140,7 +140,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
|
||||
public void testOnDisplayRemoved() {
|
||||
mController.onDisplayAdded(DISPLAY_ID);
|
||||
final Configuration taskConfig = new Configuration();
|
||||
mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
|
||||
mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
|
||||
mMockTaskListener);
|
||||
|
||||
mController.onDisplayRemoved(DISPLAY_ID + 1);
|
||||
@@ -158,7 +158,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
|
||||
@Test
|
||||
public void testOnDisplayConfigurationChanged() {
|
||||
final Configuration taskConfig = new Configuration();
|
||||
mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
|
||||
mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
|
||||
mMockTaskListener);
|
||||
|
||||
final Configuration newTaskConfig = new Configuration();
|
||||
@@ -175,7 +175,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
|
||||
public void testInsetsChanged() {
|
||||
mController.onDisplayAdded(DISPLAY_ID);
|
||||
final Configuration taskConfig = new Configuration();
|
||||
mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
|
||||
mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
|
||||
mMockTaskListener);
|
||||
InsetsState insetsState = new InsetsState();
|
||||
InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
|
||||
@@ -197,7 +197,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
|
||||
@Test
|
||||
public void testChangeButtonVisibilityOnImeShowHide() {
|
||||
final Configuration taskConfig = new Configuration();
|
||||
mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
|
||||
mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
|
||||
|
||||
// Verify that the restart button is hidden after IME is showing.
|
||||
mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
|
||||
@@ -205,9 +205,9 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
|
||||
verify(mMockLayout).updateVisibility(false);
|
||||
|
||||
// Verify button remains hidden while IME is showing.
|
||||
mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
|
||||
mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
|
||||
|
||||
verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener,
|
||||
verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
|
||||
false /* show */);
|
||||
|
||||
// Verify button is shown after IME is hidden.
|
||||
@@ -219,7 +219,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
|
||||
@Test
|
||||
public void testChangeButtonVisibilityOnKeyguardOccludedChanged() {
|
||||
final Configuration taskConfig = new Configuration();
|
||||
mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
|
||||
mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
|
||||
|
||||
// Verify that the restart button is hidden after keyguard becomes occluded.
|
||||
mController.onKeyguardOccludedChanged(true);
|
||||
@@ -227,9 +227,9 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
|
||||
verify(mMockLayout).updateVisibility(false);
|
||||
|
||||
// Verify button remains hidden while keyguard is occluded.
|
||||
mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
|
||||
mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
|
||||
|
||||
verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener,
|
||||
verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener,
|
||||
false /* show */);
|
||||
|
||||
// Verify button is shown after keyguard becomes not occluded.
|
||||
@@ -241,7 +241,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
|
||||
@Test
|
||||
public void testButtonRemainsHiddenOnKeyguardOccludedFalseWhenImeIsShowing() {
|
||||
final Configuration taskConfig = new Configuration();
|
||||
mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
|
||||
mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
|
||||
|
||||
mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
|
||||
mController.onKeyguardOccludedChanged(true);
|
||||
@@ -264,7 +264,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
|
||||
@Test
|
||||
public void testButtonRemainsHiddenOnImeHideWhenKeyguardIsOccluded() {
|
||||
final Configuration taskConfig = new Configuration();
|
||||
mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
|
||||
mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);
|
||||
|
||||
mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
|
||||
mController.onKeyguardOccludedChanged(true);
|
||||
@@ -14,17 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.wm.shell.sizecompatui;
|
||||
package com.android.wm.shell.compatui;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
||||
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.SurfaceControlViewHost;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
@@ -41,55 +44,68 @@ import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
/**
|
||||
* Tests for {@link SizeCompatRestartButton}.
|
||||
* Tests for {@link CompatUILayout}.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* atest WMShellUnitTests:SizeCompatRestartButtonTest
|
||||
* atest WMShellUnitTests:CompatUILayoutTest
|
||||
*/
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@SmallTest
|
||||
public class SizeCompatRestartButtonTest extends ShellTestCase {
|
||||
public class CompatUILayoutTest extends ShellTestCase {
|
||||
|
||||
private static final int TASK_ID = 1;
|
||||
|
||||
@Mock private SyncTransactionQueue mSyncTransactionQueue;
|
||||
@Mock private SizeCompatUIController.SizeCompatUICallback mCallback;
|
||||
@Mock private CompatUIController.CompatUICallback mCallback;
|
||||
@Mock private ShellTaskOrganizer.TaskListener mTaskListener;
|
||||
@Mock private DisplayLayout mDisplayLayout;
|
||||
@Mock private SurfaceControlViewHost mViewHost;
|
||||
|
||||
private SizeCompatUILayout mLayout;
|
||||
private SizeCompatRestartButton mButton;
|
||||
private CompatUIWindowManager mWindowManager;
|
||||
private CompatUILayout mCompatUILayout;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext,
|
||||
new Configuration(), TASK_ID, mTaskListener, mDisplayLayout,
|
||||
mWindowManager = new CompatUIWindowManager(mContext, new Configuration(),
|
||||
mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(),
|
||||
false /* hasShownHint */);
|
||||
mButton = (SizeCompatRestartButton)
|
||||
LayoutInflater.from(mContext).inflate(R.layout.size_compat_ui, null);
|
||||
mButton.inject(mLayout);
|
||||
|
||||
spyOn(mLayout);
|
||||
mCompatUILayout = (CompatUILayout)
|
||||
LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout, null);
|
||||
mCompatUILayout.inject(mWindowManager);
|
||||
|
||||
spyOn(mWindowManager);
|
||||
spyOn(mCompatUILayout);
|
||||
doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnClick() {
|
||||
final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button);
|
||||
public void testOnClickForRestartButton() {
|
||||
final ImageButton button = mCompatUILayout.findViewById(R.id.size_compat_restart_button);
|
||||
button.performClick();
|
||||
|
||||
verify(mLayout).onRestartButtonClicked();
|
||||
verify(mWindowManager).onRestartButtonClicked();
|
||||
doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
|
||||
verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnLongClick() {
|
||||
doNothing().when(mLayout).onRestartButtonLongClicked();
|
||||
public void testOnLongClickForRestartButton() {
|
||||
doNothing().when(mWindowManager).onRestartButtonLongClicked();
|
||||
|
||||
final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button);
|
||||
final ImageButton button = mCompatUILayout.findViewById(R.id.size_compat_restart_button);
|
||||
button.performLongClick();
|
||||
|
||||
verify(mLayout).onRestartButtonLongClicked();
|
||||
verify(mWindowManager).onRestartButtonLongClicked();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnClickForSizeCompatHint() {
|
||||
mWindowManager.createLayout(true /* show */);
|
||||
final LinearLayout sizeCompatHint = mCompatUILayout.findViewById(R.id.size_compat_hint);
|
||||
sizeCompatHint.performClick();
|
||||
|
||||
verify(mCompatUILayout).setSizeCompatHintVisibility(/* show= */ false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* 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.compatui;
|
||||
|
||||
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
|
||||
|
||||
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.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
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.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.InsetsSource;
|
||||
import android.view.InsetsState;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceControlViewHost;
|
||||
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.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 CompatUIWindowManager}.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* atest WMShellUnitTests:CompatUIWindowManagerTest
|
||||
*/
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@SmallTest
|
||||
public class CompatUIWindowManagerTest extends ShellTestCase {
|
||||
|
||||
private static final int TASK_ID = 1;
|
||||
|
||||
@Mock private SyncTransactionQueue mSyncTransactionQueue;
|
||||
@Mock private CompatUIController.CompatUICallback mCallback;
|
||||
@Mock private ShellTaskOrganizer.TaskListener mTaskListener;
|
||||
@Mock private CompatUILayout mCompatUILayout;
|
||||
@Mock private SurfaceControlViewHost mViewHost;
|
||||
private Configuration mTaskConfig;
|
||||
|
||||
private CompatUIWindowManager mWindowManager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mTaskConfig = new Configuration();
|
||||
|
||||
mWindowManager = new CompatUIWindowManager(mContext, new Configuration(),
|
||||
mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(),
|
||||
false /* hasShownHint */);
|
||||
|
||||
spyOn(mWindowManager);
|
||||
doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
|
||||
doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSizeCompatButton() {
|
||||
// Not create layout if show is false.
|
||||
mWindowManager.createLayout(false /* show */);
|
||||
|
||||
verify(mWindowManager, never()).inflateCompatUILayout();
|
||||
|
||||
// Not create hint popup.
|
||||
mWindowManager.mShouldShowHint = false;
|
||||
mWindowManager.createLayout(true /* show */);
|
||||
|
||||
verify(mWindowManager).inflateCompatUILayout();
|
||||
verify(mCompatUILayout).setSizeCompatHintVisibility(false /* show */);
|
||||
|
||||
// Create hint popup.
|
||||
mWindowManager.release();
|
||||
mWindowManager.mShouldShowHint = true;
|
||||
mWindowManager.createLayout(true /* show */);
|
||||
|
||||
verify(mWindowManager, times(2)).inflateCompatUILayout();
|
||||
assertNotNull(mCompatUILayout);
|
||||
verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */);
|
||||
assertFalse(mWindowManager.mShouldShowHint);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRelease() {
|
||||
mWindowManager.createLayout(true /* show */);
|
||||
|
||||
verify(mWindowManager).inflateCompatUILayout();
|
||||
|
||||
mWindowManager.release();
|
||||
|
||||
verify(mViewHost).release();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateCompatInfo() {
|
||||
mWindowManager.createLayout(true /* show */);
|
||||
|
||||
// No diff
|
||||
clearInvocations(mWindowManager);
|
||||
mWindowManager.updateCompatInfo(mTaskConfig, mTaskListener, true /* show */);
|
||||
|
||||
verify(mWindowManager, never()).updateSurfacePosition();
|
||||
verify(mWindowManager, never()).release();
|
||||
verify(mWindowManager, never()).createLayout(anyBoolean());
|
||||
|
||||
// Change task listener, recreate button.
|
||||
clearInvocations(mWindowManager);
|
||||
final ShellTaskOrganizer.TaskListener newTaskListener = mock(
|
||||
ShellTaskOrganizer.TaskListener.class);
|
||||
mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener,
|
||||
true /* show */);
|
||||
|
||||
verify(mWindowManager).release();
|
||||
verify(mWindowManager).createLayout(anyBoolean());
|
||||
|
||||
// Change task bounds, update position.
|
||||
clearInvocations(mWindowManager);
|
||||
final Configuration newTaskConfiguration = new Configuration();
|
||||
newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
|
||||
mWindowManager.updateCompatInfo(newTaskConfiguration, newTaskListener,
|
||||
true /* show */);
|
||||
|
||||
verify(mWindowManager).updateSurfacePosition();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateDisplayLayout() {
|
||||
final DisplayInfo displayInfo = new DisplayInfo();
|
||||
displayInfo.logicalWidth = 1000;
|
||||
displayInfo.logicalHeight = 2000;
|
||||
final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo,
|
||||
mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
|
||||
|
||||
mWindowManager.updateDisplayLayout(displayLayout1);
|
||||
verify(mWindowManager).updateSurfacePosition();
|
||||
|
||||
// No update if the display bounds is the same.
|
||||
clearInvocations(mWindowManager);
|
||||
final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo,
|
||||
mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
|
||||
mWindowManager.updateDisplayLayout(displayLayout2);
|
||||
verify(mWindowManager, never()).updateSurfacePosition();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateDisplayLayoutInsets() {
|
||||
final DisplayInfo displayInfo = new DisplayInfo();
|
||||
displayInfo.logicalWidth = 1000;
|
||||
displayInfo.logicalHeight = 2000;
|
||||
final DisplayLayout displayLayout = new DisplayLayout(displayInfo,
|
||||
mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false);
|
||||
|
||||
mWindowManager.updateDisplayLayout(displayLayout);
|
||||
verify(mWindowManager).updateSurfacePosition();
|
||||
|
||||
// Update if the insets change on the existing display layout
|
||||
clearInvocations(mWindowManager);
|
||||
InsetsState insetsState = new InsetsState();
|
||||
InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
|
||||
insetsSource.setFrame(0, 0, 1000, 1000);
|
||||
insetsState.addSource(insetsSource);
|
||||
displayLayout.setInsets(mContext.getResources(), insetsState);
|
||||
mWindowManager.updateDisplayLayout(displayLayout);
|
||||
verify(mWindowManager).updateSurfacePosition();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateVisibility() {
|
||||
// Create button if it is not created.
|
||||
mWindowManager.mCompatUILayout = null;
|
||||
mWindowManager.updateVisibility(true /* show */);
|
||||
|
||||
verify(mWindowManager).createLayout(true /* show */);
|
||||
|
||||
// Hide button.
|
||||
clearInvocations(mWindowManager);
|
||||
doReturn(View.VISIBLE).when(mCompatUILayout).getVisibility();
|
||||
mWindowManager.updateVisibility(false /* show */);
|
||||
|
||||
verify(mWindowManager, never()).createLayout(anyBoolean());
|
||||
verify(mCompatUILayout).setVisibility(View.GONE);
|
||||
|
||||
// Show button.
|
||||
doReturn(View.GONE).when(mCompatUILayout).getVisibility();
|
||||
mWindowManager.updateVisibility(true /* show */);
|
||||
|
||||
verify(mWindowManager, never()).createLayout(anyBoolean());
|
||||
verify(mCompatUILayout).setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAttachToParentSurface() {
|
||||
final SurfaceControl.Builder b = new SurfaceControl.Builder();
|
||||
mWindowManager.attachToParentSurface(b);
|
||||
|
||||
verify(mTaskListener).attachChildSurfaceToTask(TASK_ID, b);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnRestartButtonClicked() {
|
||||
mWindowManager.onRestartButtonClicked();
|
||||
|
||||
verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnRestartButtonLongClicked_showHint() {
|
||||
// Not create hint popup.
|
||||
mWindowManager.mShouldShowHint = false;
|
||||
mWindowManager.createLayout(true /* show */);
|
||||
|
||||
verify(mWindowManager).inflateCompatUILayout();
|
||||
verify(mCompatUILayout).setSizeCompatHintVisibility(false /* show */);
|
||||
|
||||
mWindowManager.onRestartButtonLongClicked();
|
||||
|
||||
verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* 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.testing.AndroidTestingRunner;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
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 SizeCompatUIController.SizeCompatUICallback mCallback;
|
||||
@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, mCallback, mContext,
|
||||
new Configuration(), taskId, 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 LinearLayout hintPopup = mHint.findViewById(R.id.size_compat_hint_popup);
|
||||
hintPopup.performClick();
|
||||
|
||||
verify(mLayout).dismissHint();
|
||||
}
|
||||
}
|
||||
@@ -1,284 +0,0 @@
|
||||
/*
|
||||
* 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 android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
|
||||
|
||||
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.anyBoolean;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
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.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.InsetsSource;
|
||||
import android.view.InsetsState;
|
||||
import android.view.SurfaceControl;
|
||||
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.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 SizeCompatUILayout}.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* atest WMShellUnitTests:SizeCompatUILayoutTest
|
||||
*/
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@SmallTest
|
||||
public class SizeCompatUILayoutTest extends ShellTestCase {
|
||||
|
||||
private static final int TASK_ID = 1;
|
||||
|
||||
@Mock private SyncTransactionQueue mSyncTransactionQueue;
|
||||
@Mock private SizeCompatUIController.SizeCompatUICallback mCallback;
|
||||
@Mock private ShellTaskOrganizer.TaskListener mTaskListener;
|
||||
@Mock private DisplayLayout mDisplayLayout;
|
||||
@Mock private SizeCompatRestartButton mButton;
|
||||
@Mock private SizeCompatHintPopup mHint;
|
||||
private Configuration mTaskConfig;
|
||||
|
||||
private SizeCompatUILayout mLayout;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mTaskConfig = new Configuration();
|
||||
|
||||
mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext,
|
||||
new Configuration(), TASK_ID, mTaskListener, new DisplayLayout(),
|
||||
false /* hasShownHint */);
|
||||
|
||||
spyOn(mLayout);
|
||||
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
|
||||
public void testCreateSizeCompatButton() {
|
||||
// Not create button if show is false.
|
||||
mLayout.createSizeCompatButton(false /* show */);
|
||||
|
||||
verify(mLayout.mButtonWindowManager, never()).createSizeCompatButton();
|
||||
assertNull(mLayout.mButton);
|
||||
assertNull(mLayout.mHintWindowManager);
|
||||
assertNull(mLayout.mHint);
|
||||
|
||||
// Not create hint popup.
|
||||
mLayout.mShouldShowHint = false;
|
||||
mLayout.createSizeCompatButton(true /* show */);
|
||||
|
||||
verify(mLayout.mButtonWindowManager).createSizeCompatButton();
|
||||
assertNotNull(mLayout.mButton);
|
||||
assertNull(mLayout.mHintWindowManager);
|
||||
assertNull(mLayout.mHint);
|
||||
|
||||
// Create hint popup.
|
||||
mLayout.release();
|
||||
mLayout.mShouldShowHint = true;
|
||||
mLayout.createSizeCompatButton(true /* show */);
|
||||
|
||||
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(true /* show */);
|
||||
final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager;
|
||||
|
||||
mLayout.release();
|
||||
|
||||
assertNull(mLayout.mButton);
|
||||
assertNull(mLayout.mHint);
|
||||
verify(hintWindowManager).release();
|
||||
assertNull(mLayout.mHintWindowManager);
|
||||
verify(mLayout.mButtonWindowManager).release();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateSizeCompatInfo() {
|
||||
mLayout.createSizeCompatButton(true /* show */);
|
||||
|
||||
// No diff
|
||||
clearInvocations(mLayout);
|
||||
mLayout.updateSizeCompatInfo(mTaskConfig, mTaskListener, true /* show */);
|
||||
|
||||
verify(mLayout, never()).updateButtonSurfacePosition();
|
||||
verify(mLayout, never()).release();
|
||||
verify(mLayout, never()).createSizeCompatButton(anyBoolean());
|
||||
|
||||
// Change task listener, recreate button.
|
||||
clearInvocations(mLayout);
|
||||
final ShellTaskOrganizer.TaskListener newTaskListener = mock(
|
||||
ShellTaskOrganizer.TaskListener.class);
|
||||
mLayout.updateSizeCompatInfo(mTaskConfig, newTaskListener,
|
||||
true /* show */);
|
||||
|
||||
verify(mLayout).release();
|
||||
verify(mLayout).createSizeCompatButton(anyBoolean());
|
||||
|
||||
// Change task bounds, update position.
|
||||
clearInvocations(mLayout);
|
||||
final Configuration newTaskConfiguration = new Configuration();
|
||||
newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
|
||||
mLayout.updateSizeCompatInfo(newTaskConfiguration, newTaskListener,
|
||||
true /* show */);
|
||||
|
||||
verify(mLayout).updateButtonSurfacePosition();
|
||||
verify(mLayout).updateHintSurfacePosition();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateDisplayLayout() {
|
||||
final DisplayInfo displayInfo = new DisplayInfo();
|
||||
displayInfo.logicalWidth = 1000;
|
||||
displayInfo.logicalHeight = 2000;
|
||||
final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo,
|
||||
mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
|
||||
|
||||
mLayout.updateDisplayLayout(displayLayout1);
|
||||
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(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false);
|
||||
mLayout.updateDisplayLayout(displayLayout2);
|
||||
verify(mLayout, never()).updateButtonSurfacePosition();
|
||||
verify(mLayout, never()).updateHintSurfacePosition();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateDisplayLayoutInsets() {
|
||||
final DisplayInfo displayInfo = new DisplayInfo();
|
||||
displayInfo.logicalWidth = 1000;
|
||||
displayInfo.logicalHeight = 2000;
|
||||
final DisplayLayout displayLayout = new DisplayLayout(displayInfo,
|
||||
mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false);
|
||||
|
||||
mLayout.updateDisplayLayout(displayLayout);
|
||||
verify(mLayout).updateButtonSurfacePosition();
|
||||
verify(mLayout).updateHintSurfacePosition();
|
||||
|
||||
// Update if the insets change on the existing display layout
|
||||
clearInvocations(mLayout);
|
||||
InsetsState insetsState = new InsetsState();
|
||||
InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR);
|
||||
insetsSource.setFrame(0, 0, 1000, 1000);
|
||||
insetsState.addSource(insetsSource);
|
||||
displayLayout.setInsets(mContext.getResources(), insetsState);
|
||||
mLayout.updateDisplayLayout(displayLayout);
|
||||
verify(mLayout).updateButtonSurfacePosition();
|
||||
verify(mLayout).updateHintSurfacePosition();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateVisibility() {
|
||||
// Create button if it is not created.
|
||||
mLayout.mButton = null;
|
||||
mLayout.updateVisibility(true /* show */);
|
||||
|
||||
verify(mLayout).createSizeCompatButton(true /* show */);
|
||||
|
||||
// Hide button.
|
||||
clearInvocations(mLayout);
|
||||
doReturn(View.VISIBLE).when(mButton).getVisibility();
|
||||
mLayout.updateVisibility(false /* show */);
|
||||
|
||||
verify(mLayout, never()).createSizeCompatButton(anyBoolean());
|
||||
verify(mButton).setVisibility(View.GONE);
|
||||
|
||||
// Show button.
|
||||
doReturn(View.GONE).when(mButton).getVisibility();
|
||||
mLayout.updateVisibility(true /* show */);
|
||||
|
||||
verify(mLayout, never()).createSizeCompatButton(anyBoolean());
|
||||
verify(mButton).setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAttachToParentSurface() {
|
||||
final SurfaceControl.Builder b = new SurfaceControl.Builder();
|
||||
mLayout.attachToParentSurface(b);
|
||||
|
||||
verify(mTaskListener).attachChildSurfaceToTask(TASK_ID, b);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnRestartButtonClicked() {
|
||||
mLayout.onRestartButtonClicked();
|
||||
|
||||
verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID);
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
@@ -121,7 +121,7 @@ public class SystemUIFactory {
|
||||
.setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper())
|
||||
.setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper())
|
||||
.setRecentTasks(mWMComponent.getRecentTasks())
|
||||
.setSizeCompatUI(Optional.of(mWMComponent.getSizeCompatUI()))
|
||||
.setCompatUI(Optional.of(mWMComponent.getCompatUI()))
|
||||
.setDragAndDrop(Optional.of(mWMComponent.getDragAndDrop()));
|
||||
} else {
|
||||
// TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
|
||||
@@ -141,7 +141,7 @@ public class SystemUIFactory {
|
||||
.setStartingSurface(Optional.ofNullable(null))
|
||||
.setTaskSurfaceHelper(Optional.ofNullable(null))
|
||||
.setRecentTasks(Optional.ofNullable(null))
|
||||
.setSizeCompatUI(Optional.ofNullable(null))
|
||||
.setCompatUI(Optional.ofNullable(null))
|
||||
.setDragAndDrop(Optional.ofNullable(null));
|
||||
}
|
||||
mSysUIComponent = builder.build();
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.android.wm.shell.ShellCommandHandler;
|
||||
import com.android.wm.shell.TaskViewFactory;
|
||||
import com.android.wm.shell.apppairs.AppPairs;
|
||||
import com.android.wm.shell.bubbles.Bubbles;
|
||||
import com.android.wm.shell.compatui.CompatUI;
|
||||
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
|
||||
import com.android.wm.shell.draganddrop.DragAndDrop;
|
||||
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
|
||||
@@ -38,7 +39,6 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
|
||||
import com.android.wm.shell.onehanded.OneHanded;
|
||||
import com.android.wm.shell.pip.Pip;
|
||||
import com.android.wm.shell.recents.RecentTasks;
|
||||
import com.android.wm.shell.sizecompatui.SizeCompatUI;
|
||||
import com.android.wm.shell.splitscreen.SplitScreen;
|
||||
import com.android.wm.shell.startingsurface.StartingSurface;
|
||||
import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper;
|
||||
@@ -110,7 +110,7 @@ public interface SysUIComponent {
|
||||
Builder setRecentTasks(Optional<RecentTasks> r);
|
||||
|
||||
@BindsInstance
|
||||
Builder setSizeCompatUI(Optional<SizeCompatUI> s);
|
||||
Builder setCompatUI(Optional<CompatUI> s);
|
||||
|
||||
@BindsInstance
|
||||
Builder setDragAndDrop(Optional<DragAndDrop> d);
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.android.wm.shell.ShellInit;
|
||||
import com.android.wm.shell.TaskViewFactory;
|
||||
import com.android.wm.shell.apppairs.AppPairs;
|
||||
import com.android.wm.shell.bubbles.Bubbles;
|
||||
import com.android.wm.shell.compatui.CompatUI;
|
||||
import com.android.wm.shell.dagger.TvWMShellModule;
|
||||
import com.android.wm.shell.dagger.WMShellModule;
|
||||
import com.android.wm.shell.dagger.WMSingleton;
|
||||
@@ -35,7 +36,6 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
|
||||
import com.android.wm.shell.onehanded.OneHanded;
|
||||
import com.android.wm.shell.pip.Pip;
|
||||
import com.android.wm.shell.recents.RecentTasks;
|
||||
import com.android.wm.shell.sizecompatui.SizeCompatUI;
|
||||
import com.android.wm.shell.splitscreen.SplitScreen;
|
||||
import com.android.wm.shell.startingsurface.StartingSurface;
|
||||
import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper;
|
||||
@@ -119,7 +119,7 @@ public interface WMComponent {
|
||||
Optional<RecentTasks> getRecentTasks();
|
||||
|
||||
@WMSingleton
|
||||
SizeCompatUI getSizeCompatUI();
|
||||
CompatUI getCompatUI();
|
||||
|
||||
@WMSingleton
|
||||
DragAndDrop getDragAndDrop();
|
||||
|
||||
@@ -56,6 +56,7 @@ import com.android.systemui.statusbar.policy.UserInfoController;
|
||||
import com.android.systemui.tracing.ProtoTracer;
|
||||
import com.android.systemui.tracing.nano.SystemUiTraceProto;
|
||||
import com.android.wm.shell.ShellCommandHandler;
|
||||
import com.android.wm.shell.compatui.CompatUI;
|
||||
import com.android.wm.shell.draganddrop.DragAndDrop;
|
||||
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
|
||||
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
|
||||
@@ -66,7 +67,6 @@ import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
|
||||
import com.android.wm.shell.onehanded.OneHandedUiEventLogger;
|
||||
import com.android.wm.shell.pip.Pip;
|
||||
import com.android.wm.shell.protolog.ShellProtoLogImpl;
|
||||
import com.android.wm.shell.sizecompatui.SizeCompatUI;
|
||||
import com.android.wm.shell.splitscreen.SplitScreen;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
@@ -114,7 +114,7 @@ public final class WMShell extends SystemUI
|
||||
private final Optional<OneHanded> mOneHandedOptional;
|
||||
private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional;
|
||||
private final Optional<ShellCommandHandler> mShellCommandHandler;
|
||||
private final Optional<SizeCompatUI> mSizeCompatUIOptional;
|
||||
private final Optional<CompatUI> mCompatUIOptional;
|
||||
private final Optional<DragAndDrop> mDragAndDropOptional;
|
||||
|
||||
private final CommandQueue mCommandQueue;
|
||||
@@ -132,7 +132,7 @@ public final class WMShell extends SystemUI
|
||||
private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
|
||||
private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
|
||||
private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
|
||||
private KeyguardUpdateMonitorCallback mSizeCompatUIKeyguardCallback;
|
||||
private KeyguardUpdateMonitorCallback mCompatUIKeyguardCallback;
|
||||
private WakefulnessLifecycle.Observer mWakefulnessObserver;
|
||||
|
||||
@Inject
|
||||
@@ -143,7 +143,7 @@ public final class WMShell extends SystemUI
|
||||
Optional<OneHanded> oneHandedOptional,
|
||||
Optional<HideDisplayCutout> hideDisplayCutoutOptional,
|
||||
Optional<ShellCommandHandler> shellCommandHandler,
|
||||
Optional<SizeCompatUI> sizeCompatUIOptional,
|
||||
Optional<CompatUI> sizeCompatUIOptional,
|
||||
Optional<DragAndDrop> dragAndDropOptional,
|
||||
CommandQueue commandQueue,
|
||||
ConfigurationController configurationController,
|
||||
@@ -169,7 +169,7 @@ public final class WMShell extends SystemUI
|
||||
mWakefulnessLifecycle = wakefulnessLifecycle;
|
||||
mProtoTracer = protoTracer;
|
||||
mShellCommandHandler = shellCommandHandler;
|
||||
mSizeCompatUIOptional = sizeCompatUIOptional;
|
||||
mCompatUIOptional = sizeCompatUIOptional;
|
||||
mDragAndDropOptional = dragAndDropOptional;
|
||||
mSysUiMainExecutor = sysUiMainExecutor;
|
||||
}
|
||||
@@ -185,7 +185,7 @@ public final class WMShell extends SystemUI
|
||||
mSplitScreenOptional.ifPresent(this::initSplitScreen);
|
||||
mOneHandedOptional.ifPresent(this::initOneHanded);
|
||||
mHideDisplayCutoutOptional.ifPresent(this::initHideDisplayCutout);
|
||||
mSizeCompatUIOptional.ifPresent(this::initSizeCompatUi);
|
||||
mCompatUIOptional.ifPresent(this::initCompatUi);
|
||||
mDragAndDropOptional.ifPresent(this::initDragAndDrop);
|
||||
}
|
||||
|
||||
@@ -391,14 +391,14 @@ public final class WMShell extends SystemUI
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void initSizeCompatUi(SizeCompatUI sizeCompatUI) {
|
||||
mSizeCompatUIKeyguardCallback = new KeyguardUpdateMonitorCallback() {
|
||||
void initCompatUi(CompatUI sizeCompatUI) {
|
||||
mCompatUIKeyguardCallback = new KeyguardUpdateMonitorCallback() {
|
||||
@Override
|
||||
public void onKeyguardOccludedChanged(boolean occluded) {
|
||||
sizeCompatUI.onKeyguardOccludedChanged(occluded);
|
||||
}
|
||||
};
|
||||
mKeyguardUpdateMonitor.registerCallback(mSizeCompatUIKeyguardCallback);
|
||||
mKeyguardUpdateMonitor.registerCallback(mCompatUIKeyguardCallback);
|
||||
}
|
||||
|
||||
void initDragAndDrop(DragAndDrop dragAndDrop) {
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
|
||||
import com.android.systemui.tracing.ProtoTracer;
|
||||
import com.android.wm.shell.ShellCommandHandler;
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
import com.android.wm.shell.compatui.CompatUI;
|
||||
import com.android.wm.shell.draganddrop.DragAndDrop;
|
||||
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
|
||||
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
|
||||
@@ -42,7 +43,6 @@ import com.android.wm.shell.onehanded.OneHanded;
|
||||
import com.android.wm.shell.onehanded.OneHandedEventCallback;
|
||||
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
|
||||
import com.android.wm.shell.pip.Pip;
|
||||
import com.android.wm.shell.sizecompatui.SizeCompatUI;
|
||||
import com.android.wm.shell.splitscreen.SplitScreen;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -78,7 +78,7 @@ public class WMShellTest extends SysuiTestCase {
|
||||
@Mock WakefulnessLifecycle mWakefulnessLifecycle;
|
||||
@Mock ProtoTracer mProtoTracer;
|
||||
@Mock ShellCommandHandler mShellCommandHandler;
|
||||
@Mock SizeCompatUI mSizeCompatUI;
|
||||
@Mock CompatUI mCompatUI;
|
||||
@Mock ShellExecutor mSysUiMainExecutor;
|
||||
@Mock DragAndDrop mDragAndDrop;
|
||||
|
||||
@@ -88,7 +88,7 @@ public class WMShellTest extends SysuiTestCase {
|
||||
|
||||
mWMShell = new WMShell(mContext, Optional.of(mPip), Optional.of(mLegacySplitScreen),
|
||||
Optional.of(mSplitScreen), Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
|
||||
Optional.of(mShellCommandHandler), Optional.of(mSizeCompatUI),
|
||||
Optional.of(mShellCommandHandler), Optional.of(mCompatUI),
|
||||
Optional.of(mDragAndDrop),
|
||||
mCommandQueue, mConfigurationController, mKeyguardUpdateMonitor,
|
||||
mNavigationModeController, mScreenLifecycle, mSysUiState, mProtoTracer,
|
||||
@@ -136,8 +136,8 @@ public class WMShellTest extends SysuiTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initSizeCompatUI_registersCallbacks() {
|
||||
mWMShell.initSizeCompatUi(mSizeCompatUI);
|
||||
public void initCompatUI_registersCallbacks() {
|
||||
mWMShell.initCompatUi(mCompatUI);
|
||||
|
||||
verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user