[1/n] Camera Compat UI: Refactor size compat mode UI controller.

First CL in the chain that will add a camera compat UI control which will be used to correct stretched issues in TextureView and SurfaceView cause by apps not handling mismatch between camera buffers and view size correctly.

This change is necessary because right now there are 2 instances of WindowlessWindowManager (SizeCompatUIWindowManager) in SizeCompatUILayout: for a hint and for a restart button. They are also positioned relatively by computing offsets in java code rather than by specifying layout with XML. This code structure will cause issues since with upcoming camera changes there will be a need to coordinate relative position of 4 UI elements (2 buttons and 2 hints).

What is changing in this CL:
- Majority of changes are simple renaming from "size compat UI" to "compat UI" since, starting from the next change, two different compat controls will be supported.
- Merging 2 instances of SizeCompatUIWindowManager into one (CompatUIWindowManager) and for that create a joint layout in compat_ui_layout.xml
- Merge SizeCompatLayout logic into CompatUIWindowManager since in absence of 2 instances of WindowlessWindowManager there is no good reason to keep them separate.
- Merging and renaming tests to follow refactoring of the implementation.

Test: atest WMShellUnitTests (the same set of tests should be sufficient since this refactoring just reorganized the structure of the implementation)
Bug: 206602997
Change-Id: I7141c69b7ba4742c5b7b2018329b91a1b37b45bc
This commit is contained in:
Mariia Sandrikova
2021-12-08 14:13:53 +00:00
parent 535795b36d
commit 6dd79270b0
30 changed files with 908 additions and 1213 deletions

View File

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

View File

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

View File

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

View 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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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.
*/

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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