[4/n] Add User Aspect Ratio Button
Adds the compat ui button which allows users to reach the app info
page in settings and change the aspect ratio for a selected app.
When the feature is enabled, the button appears every time the app is
launched and when TaskInfo.topActivityEligibleForUserAspectRatioButton
and TaskInfo.topActivityBoundsLetterboxed are both true.
The icons appears after a short interval and disappears after 4 seconds.
Fix: 287460588
Test: `atest WMShellUnitTests:UserAspectRatioSettingsWindowManagerTest`
`atest WMShellUnitTests:UserAspectRatioSettingsLayoutTest`
`atest WMShellUnitTests:CompatUIControllerTest`
`atest WMShellUnitTests:ReachabilityEduWindowManagerTest`
`atest WmTests:LetterboxUiControllerTest`
Change-Id: If1ed5442359b0bc3f352d3b2f9678a5921c73114
This commit is contained in:
@@ -247,6 +247,13 @@ public class TaskInfo {
|
||||
*/
|
||||
public boolean topActivityEligibleForUserAspectRatioButton;
|
||||
|
||||
/**
|
||||
* Whether the user has forced the activity to be fullscreen through the user aspect ratio
|
||||
* settings.
|
||||
* @hide
|
||||
*/
|
||||
public boolean isUserFullscreenOverrideEnabled;
|
||||
|
||||
/**
|
||||
* Hint about the letterbox state of the top activity.
|
||||
* @hide
|
||||
@@ -543,7 +550,8 @@ public class TaskInfo {
|
||||
&& isSleeping == that.isSleeping
|
||||
&& Objects.equals(mTopActivityLocusId, that.mTopActivityLocusId)
|
||||
&& parentTaskId == that.parentTaskId
|
||||
&& Objects.equals(topActivity, that.topActivity);
|
||||
&& Objects.equals(topActivity, that.topActivity)
|
||||
&& isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -574,7 +582,8 @@ public class TaskInfo {
|
||||
&& (!hasCompatUI() || configuration.getLayoutDirection()
|
||||
== that.configuration.getLayoutDirection())
|
||||
&& (!hasCompatUI() || configuration.uiMode == that.configuration.uiMode)
|
||||
&& (!hasCompatUI() || isVisible == that.isVisible);
|
||||
&& (!hasCompatUI() || isVisible == that.isVisible)
|
||||
&& isUserFullscreenOverrideEnabled == that.isUserFullscreenOverrideEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -630,6 +639,7 @@ public class TaskInfo {
|
||||
topActivityLetterboxHorizontalPosition = source.readInt();
|
||||
topActivityLetterboxWidth = source.readInt();
|
||||
topActivityLetterboxHeight = source.readInt();
|
||||
isUserFullscreenOverrideEnabled = source.readBoolean();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -686,6 +696,7 @@ public class TaskInfo {
|
||||
dest.writeInt(topActivityLetterboxHorizontalPosition);
|
||||
dest.writeInt(topActivityLetterboxWidth);
|
||||
dest.writeInt(topActivityLetterboxHeight);
|
||||
dest.writeBoolean(isUserFullscreenOverrideEnabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -732,6 +743,7 @@ public class TaskInfo {
|
||||
+ topActivityLetterboxHorizontalPosition
|
||||
+ " topActivityLetterboxWidth=" + topActivityLetterboxWidth
|
||||
+ " topActivityLetterboxHeight=" + topActivityLetterboxHeight
|
||||
+ " isUserFullscreenOverrideEnabled=" + isUserFullscreenOverrideEnabled
|
||||
+ " locusId=" + mTopActivityLocusId
|
||||
+ " displayAreaFeatureId=" + displayAreaFeatureId
|
||||
+ " cameraCompatControlState="
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2023 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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:fillColor="@color/compat_controls_background"
|
||||
android:strokeAlpha="0.8"
|
||||
android:fillAlpha="0.8"
|
||||
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/compat_controls_text"
|
||||
android:pathData="M19,12h-2v3h-3v2h5v-5zM7,9h3L10,7L5,7v5h2L7,9zM21,3L3,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,5c0,-1.1 -0.9,-2 -2,-2zM21,19.01L3,19.01L3,4.99h18v14.02z"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2023 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.
|
||||
-->
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/compat_background_ripple">
|
||||
<item android:drawable="@drawable/user_aspect_ratio_settings_button"/>
|
||||
</ripple>
|
||||
@@ -55,7 +55,7 @@
|
||||
|
||||
<include android:id="@+id/size_compat_hint"
|
||||
android:visibility="gone"
|
||||
android:layout_width="@dimen/size_compat_hint_width"
|
||||
android:layout_width="@dimen/compat_hint_width"
|
||||
android:layout_height="wrap_content"
|
||||
layout="@layout/compat_mode_hint"/>
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2023 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.compatui.UserAspectRatioSettingsLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="bottom|end">
|
||||
|
||||
<include android:id="@+id/user_aspect_ratio_settings_hint"
|
||||
android:visibility="gone"
|
||||
android:layout_width="@dimen/compat_hint_width"
|
||||
android:layout_height="wrap_content"
|
||||
layout="@layout/compat_mode_hint"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/user_aspect_ratio_settings_button"
|
||||
android:visibility="gone"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/compat_button_margin"
|
||||
android:layout_marginBottom="@dimen/compat_button_margin"
|
||||
android:src="@drawable/user_aspect_ratio_settings_button_ripple"
|
||||
android:background="@android:color/transparent"
|
||||
android:contentDescription="@string/user_aspect_ratio_settings_button_description"/>
|
||||
|
||||
</com.android.wm.shell.compatui.UserAspectRatioSettingsLayout>
|
||||
@@ -260,8 +260,8 @@
|
||||
+ compat_button_margin - compat_hint_corner_radius - compat_hint_point_width / 2). -->
|
||||
<dimen name="compat_hint_padding_end">7dp</dimen>
|
||||
|
||||
<!-- The width of the size compat hint. -->
|
||||
<dimen name="size_compat_hint_width">188dp</dimen>
|
||||
<!-- The width of the compat hint. -->
|
||||
<dimen name="compat_hint_width">188dp</dimen>
|
||||
|
||||
<!-- The width of the camera compat hint. -->
|
||||
<dimen name="camera_compat_hint_width">143dp</dimen>
|
||||
|
||||
@@ -173,7 +173,13 @@
|
||||
<string name="accessibility_bubble_dismissed">Bubble dismissed.</string>
|
||||
|
||||
<!-- Description of the restart button in the hint of size compatibility mode. [CHAR LIMIT=NONE] -->
|
||||
<string name="restart_button_description">Tap to restart this app for a better view.</string>
|
||||
<string name="restart_button_description">Tap to restart this app for a better view</string>
|
||||
|
||||
<!-- Tooltip text of the button for the user aspect ratio settings. [CHAR LIMIT=NONE] -->
|
||||
<string name="user_aspect_ratio_settings_button_hint">Change this app\'s aspect ratio in Settings</string>
|
||||
|
||||
<!-- Content description of the button for the user aspect ratio settings. [CHAR LIMIT=NONE] -->
|
||||
<string name="user_aspect_ratio_settings_button_description">Change aspect ratio</string>
|
||||
|
||||
<!-- Description of the camera compat button for applying stretched issues treatment in the hint for
|
||||
compatibility control. [CHAR LIMIT=NONE] -->
|
||||
|
||||
@@ -16,12 +16,17 @@
|
||||
|
||||
package com.android.wm.shell.compatui;
|
||||
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.TaskInfo;
|
||||
import android.app.TaskInfo.CameraCompatControlState;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.provider.Settings;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
@@ -41,7 +46,6 @@ import com.android.wm.shell.common.DisplayLayout;
|
||||
import com.android.wm.shell.common.DockStateReader;
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState;
|
||||
import com.android.wm.shell.sysui.KeyguardChangeListener;
|
||||
import com.android.wm.shell.sysui.ShellController;
|
||||
import com.android.wm.shell.sysui.ShellInit;
|
||||
@@ -103,6 +107,13 @@ public class CompatUIController implements OnDisplaysChangedListener,
|
||||
*/
|
||||
private Set<Integer> mSetOfTaskIdsShowingRestartDialog = new HashSet<>();
|
||||
|
||||
/**
|
||||
* The active user aspect ratio settings button layout if there is one (there can be at most
|
||||
* one active).
|
||||
*/
|
||||
@Nullable
|
||||
private UserAspectRatioSettingsWindowManager mUserAspectRatioSettingsLayout;
|
||||
|
||||
/**
|
||||
* The active Letterbox Education layout if there is one (there can be at most one active).
|
||||
*
|
||||
@@ -121,38 +132,51 @@ public class CompatUIController implements OnDisplaysChangedListener,
|
||||
/** Avoid creating display context frequently for non-default display. */
|
||||
private final SparseArray<WeakReference<Context>> mDisplayContextCache = new SparseArray<>(0);
|
||||
|
||||
@NonNull
|
||||
private final Context mContext;
|
||||
@NonNull
|
||||
private final ShellController mShellController;
|
||||
@NonNull
|
||||
private final DisplayController mDisplayController;
|
||||
@NonNull
|
||||
private final DisplayInsetsController mDisplayInsetsController;
|
||||
@NonNull
|
||||
private final DisplayImeController mImeController;
|
||||
@NonNull
|
||||
private final SyncTransactionQueue mSyncQueue;
|
||||
@NonNull
|
||||
private final ShellExecutor mMainExecutor;
|
||||
@NonNull
|
||||
private final Lazy<Transitions> mTransitionsLazy;
|
||||
@NonNull
|
||||
private final DockStateReader mDockStateReader;
|
||||
@NonNull
|
||||
private final CompatUIConfiguration mCompatUIConfiguration;
|
||||
// Only show each hint once automatically in the process life.
|
||||
@NonNull
|
||||
private final CompatUIHintsState mCompatUIHintsState;
|
||||
@NonNull
|
||||
private final CompatUIShellCommandHandler mCompatUIShellCommandHandler;
|
||||
|
||||
private CompatUICallback mCallback;
|
||||
@Nullable
|
||||
private CompatUICallback mCompatUICallback;
|
||||
|
||||
// Indicates if the keyguard is currently showing, in which case compat UIs shouldn't
|
||||
// be shown.
|
||||
private boolean mKeyguardShowing;
|
||||
|
||||
public CompatUIController(Context context,
|
||||
ShellInit shellInit,
|
||||
ShellController shellController,
|
||||
DisplayController displayController,
|
||||
DisplayInsetsController displayInsetsController,
|
||||
DisplayImeController imeController,
|
||||
SyncTransactionQueue syncQueue,
|
||||
ShellExecutor mainExecutor,
|
||||
Lazy<Transitions> transitionsLazy,
|
||||
DockStateReader dockStateReader,
|
||||
CompatUIConfiguration compatUIConfiguration,
|
||||
CompatUIShellCommandHandler compatUIShellCommandHandler) {
|
||||
public CompatUIController(@NonNull Context context,
|
||||
@NonNull ShellInit shellInit,
|
||||
@NonNull ShellController shellController,
|
||||
@NonNull DisplayController displayController,
|
||||
@NonNull DisplayInsetsController displayInsetsController,
|
||||
@NonNull DisplayImeController imeController,
|
||||
@NonNull SyncTransactionQueue syncQueue,
|
||||
@NonNull ShellExecutor mainExecutor,
|
||||
@NonNull Lazy<Transitions> transitionsLazy,
|
||||
@NonNull DockStateReader dockStateReader,
|
||||
@NonNull CompatUIConfiguration compatUIConfiguration,
|
||||
@NonNull CompatUIShellCommandHandler compatUIShellCommandHandler) {
|
||||
mContext = context;
|
||||
mShellController = shellController;
|
||||
mDisplayController = displayController;
|
||||
@@ -175,9 +199,9 @@ public class CompatUIController implements OnDisplaysChangedListener,
|
||||
mCompatUIShellCommandHandler.onInit();
|
||||
}
|
||||
|
||||
/** Sets the callback for UI interactions. */
|
||||
public void setCompatUICallback(CompatUICallback callback) {
|
||||
mCallback = callback;
|
||||
/** Sets the callback for Compat UI interactions. */
|
||||
public void setCompatUICallback(@NonNull CompatUICallback compatUiCallback) {
|
||||
mCompatUICallback = compatUiCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,7 +211,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
|
||||
* @param taskInfo {@link TaskInfo} task the activity is in.
|
||||
* @param taskListener listener to handle the Task Surface placement.
|
||||
*/
|
||||
public void onCompatInfoChanged(TaskInfo taskInfo,
|
||||
public void onCompatInfoChanged(@NonNull TaskInfo taskInfo,
|
||||
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
|
||||
if (taskInfo != null && !taskInfo.topActivityInSizeCompat) {
|
||||
mSetOfTaskIdsShowingRestartDialog.remove(taskInfo.taskId);
|
||||
@@ -203,6 +227,16 @@ public class CompatUIController implements OnDisplaysChangedListener,
|
||||
createOrUpdateRestartDialogLayout(taskInfo, taskListener);
|
||||
if (mCompatUIConfiguration.getHasSeenLetterboxEducation(taskInfo.userId)) {
|
||||
createOrUpdateReachabilityEduLayout(taskInfo, taskListener);
|
||||
// The user aspect ratio button should not be handled when a new TaskInfo is
|
||||
// sent because of a double tap or when in multi-window mode.
|
||||
if (taskInfo.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
|
||||
mUserAspectRatioSettingsLayout.release();
|
||||
mUserAspectRatioSettingsLayout = null;
|
||||
return;
|
||||
}
|
||||
if (!taskInfo.isFromLetterboxDoubleTap) {
|
||||
createOrUpdateUserAspectRatioSettingsLayout(taskInfo, taskListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,8 +314,8 @@ public class CompatUIController implements OnDisplaysChangedListener,
|
||||
return mDisplaysWithIme.contains(displayId);
|
||||
}
|
||||
|
||||
private void createOrUpdateCompatLayout(TaskInfo taskInfo,
|
||||
ShellTaskOrganizer.TaskListener taskListener) {
|
||||
private void createOrUpdateCompatLayout(@NonNull TaskInfo taskInfo,
|
||||
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
|
||||
CompatUIWindowManager layout = mActiveCompatLayouts.get(taskInfo.taskId);
|
||||
if (layout != null) {
|
||||
if (layout.needsToBeRecreated(taskInfo, taskListener)) {
|
||||
@@ -314,7 +348,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
|
||||
CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
|
||||
ShellTaskOrganizer.TaskListener taskListener) {
|
||||
return new CompatUIWindowManager(context,
|
||||
taskInfo, mSyncQueue, mCallback, taskListener,
|
||||
taskInfo, mSyncQueue, mCompatUICallback, taskListener,
|
||||
mDisplayController.getDisplayLayout(taskInfo.displayId), mCompatUIHintsState,
|
||||
mCompatUIConfiguration, this::onRestartButtonClicked);
|
||||
}
|
||||
@@ -328,12 +362,12 @@ public class CompatUIController implements OnDisplaysChangedListener,
|
||||
mSetOfTaskIdsShowingRestartDialog.add(taskInfoState.first.taskId);
|
||||
onCompatInfoChanged(taskInfoState.first, taskInfoState.second);
|
||||
} else {
|
||||
mCallback.onSizeCompatRestartButtonClicked(taskInfoState.first.taskId);
|
||||
mCompatUICallback.onSizeCompatRestartButtonClicked(taskInfoState.first.taskId);
|
||||
}
|
||||
}
|
||||
|
||||
private void createOrUpdateLetterboxEduLayout(TaskInfo taskInfo,
|
||||
ShellTaskOrganizer.TaskListener taskListener) {
|
||||
private void createOrUpdateLetterboxEduLayout(@NonNull TaskInfo taskInfo,
|
||||
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
|
||||
if (mActiveLetterboxEduLayout != null) {
|
||||
if (mActiveLetterboxEduLayout.needsToBeRecreated(taskInfo, taskListener)) {
|
||||
mActiveLetterboxEduLayout.release();
|
||||
@@ -377,8 +411,8 @@ public class CompatUIController implements OnDisplaysChangedListener,
|
||||
mDockStateReader, mCompatUIConfiguration);
|
||||
}
|
||||
|
||||
private void createOrUpdateRestartDialogLayout(TaskInfo taskInfo,
|
||||
ShellTaskOrganizer.TaskListener taskListener) {
|
||||
private void createOrUpdateRestartDialogLayout(@NonNull TaskInfo taskInfo,
|
||||
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
|
||||
RestartDialogWindowManager layout =
|
||||
mTaskIdToRestartDialogWindowManagerMap.get(taskInfo.taskId);
|
||||
if (layout != null) {
|
||||
@@ -423,7 +457,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
|
||||
private void onRestartDialogCallback(
|
||||
Pair<TaskInfo, ShellTaskOrganizer.TaskListener> stateInfo) {
|
||||
mTaskIdToRestartDialogWindowManagerMap.remove(stateInfo.first.taskId);
|
||||
mCallback.onSizeCompatRestartButtonClicked(stateInfo.first.taskId);
|
||||
mCompatUICallback.onSizeCompatRestartButtonClicked(stateInfo.first.taskId);
|
||||
}
|
||||
|
||||
private void onRestartDialogDismissCallback(
|
||||
@@ -432,8 +466,8 @@ public class CompatUIController implements OnDisplaysChangedListener,
|
||||
onCompatInfoChanged(stateInfo.first, stateInfo.second);
|
||||
}
|
||||
|
||||
private void createOrUpdateReachabilityEduLayout(TaskInfo taskInfo,
|
||||
ShellTaskOrganizer.TaskListener taskListener) {
|
||||
private void createOrUpdateReachabilityEduLayout(@NonNull TaskInfo taskInfo,
|
||||
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
|
||||
if (mActiveReachabilityEduLayout != null) {
|
||||
if (mActiveReachabilityEduLayout.needsToBeRecreated(taskInfo, taskListener)) {
|
||||
mActiveReachabilityEduLayout.release();
|
||||
@@ -474,14 +508,67 @@ public class CompatUIController implements OnDisplaysChangedListener,
|
||||
ShellTaskOrganizer.TaskListener taskListener) {
|
||||
return new ReachabilityEduWindowManager(context, taskInfo, mSyncQueue,
|
||||
taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId),
|
||||
mCompatUIConfiguration, mMainExecutor);
|
||||
mCompatUIConfiguration, mMainExecutor, this::onInitialReachabilityEduDismissed);
|
||||
}
|
||||
|
||||
private void onInitialReachabilityEduDismissed(@NonNull TaskInfo taskInfo,
|
||||
@NonNull ShellTaskOrganizer.TaskListener taskListener) {
|
||||
// We need to update the UI otherwise it will not be shown until the user relaunches the app
|
||||
createOrUpdateUserAspectRatioSettingsLayout(taskInfo, taskListener);
|
||||
}
|
||||
|
||||
private void createOrUpdateUserAspectRatioSettingsLayout(@NonNull TaskInfo taskInfo,
|
||||
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
|
||||
if (mUserAspectRatioSettingsLayout != null) {
|
||||
if (mUserAspectRatioSettingsLayout.needsToBeRecreated(taskInfo, taskListener)) {
|
||||
mUserAspectRatioSettingsLayout.release();
|
||||
mUserAspectRatioSettingsLayout = null;
|
||||
} else {
|
||||
// UI already exists, update the UI layout.
|
||||
if (!mUserAspectRatioSettingsLayout.updateCompatInfo(taskInfo, taskListener,
|
||||
showOnDisplay(mUserAspectRatioSettingsLayout.getDisplayId()))) {
|
||||
mUserAspectRatioSettingsLayout.release();
|
||||
mUserAspectRatioSettingsLayout = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new UI layout.
|
||||
final Context context = getOrCreateDisplayContext(taskInfo.displayId);
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
final UserAspectRatioSettingsWindowManager newLayout =
|
||||
createUserAspectRatioSettingsWindowManager(context, taskInfo, taskListener);
|
||||
if (newLayout.createLayout(showOnDisplay(taskInfo.displayId))) {
|
||||
// The new layout is eligible to be shown, add it the active layouts.
|
||||
mUserAspectRatioSettingsLayout = newLayout;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@NonNull
|
||||
UserAspectRatioSettingsWindowManager createUserAspectRatioSettingsWindowManager(
|
||||
@NonNull Context context, @NonNull TaskInfo taskInfo,
|
||||
@Nullable ShellTaskOrganizer.TaskListener taskListener) {
|
||||
return new UserAspectRatioSettingsWindowManager(context, taskInfo, mSyncQueue,
|
||||
taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId),
|
||||
mCompatUIHintsState, this::launchUserAspectRatioSettings, mMainExecutor);
|
||||
}
|
||||
|
||||
private void launchUserAspectRatioSettings(
|
||||
@NonNull TaskInfo taskInfo, @NonNull ShellTaskOrganizer.TaskListener taskListener) {
|
||||
final Intent intent = new Intent(Settings.ACTION_MANAGE_USER_ASPECT_RATIO_SETTINGS);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
mContext.startActivity(intent);
|
||||
}
|
||||
|
||||
private void removeLayouts(int taskId) {
|
||||
final CompatUIWindowManager layout = mActiveCompatLayouts.get(taskId);
|
||||
if (layout != null) {
|
||||
layout.release();
|
||||
final CompatUIWindowManager compatLayout = mActiveCompatLayouts.get(taskId);
|
||||
if (compatLayout != null) {
|
||||
compatLayout.release();
|
||||
mActiveCompatLayouts.remove(taskId);
|
||||
}
|
||||
|
||||
@@ -502,6 +589,12 @@ public class CompatUIController implements OnDisplaysChangedListener,
|
||||
mActiveReachabilityEduLayout.release();
|
||||
mActiveReachabilityEduLayout = null;
|
||||
}
|
||||
|
||||
if (mUserAspectRatioSettingsLayout != null
|
||||
&& mUserAspectRatioSettingsLayout.getTaskId() == taskId) {
|
||||
mUserAspectRatioSettingsLayout.release();
|
||||
mUserAspectRatioSettingsLayout = null;
|
||||
}
|
||||
}
|
||||
|
||||
private Context getOrCreateDisplayContext(int displayId) {
|
||||
@@ -557,6 +650,10 @@ public class CompatUIController implements OnDisplaysChangedListener,
|
||||
if (mActiveReachabilityEduLayout != null && condition.test(mActiveReachabilityEduLayout)) {
|
||||
callback.accept(mActiveReachabilityEduLayout);
|
||||
}
|
||||
if (mUserAspectRatioSettingsLayout != null && condition.test(
|
||||
mUserAspectRatioSettingsLayout)) {
|
||||
callback.accept(mUserAspectRatioSettingsLayout);
|
||||
}
|
||||
}
|
||||
|
||||
/** An implementation of {@link OnInsetsChangedListener} for a given display id. */
|
||||
@@ -591,4 +688,14 @@ public class CompatUIController implements OnDisplaysChangedListener,
|
||||
insetsChanged(insetsState);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A class holding the state of the compat UI hints, which is shared between all compat UI
|
||||
* window managers.
|
||||
*/
|
||||
static class CompatUIHintsState {
|
||||
boolean mHasShownSizeCompatHint;
|
||||
boolean mHasShownCameraCompatHint;
|
||||
boolean mHasShownUserAspectRatioSettingsButtonHint;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.common.DisplayLayout;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
import com.android.wm.shell.compatui.CompatUIController.CompatUICallback;
|
||||
import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -235,15 +236,4 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
|
||||
return mCameraCompatControlState != CAMERA_COMPAT_CONTROL_HIDDEN
|
||||
&& mCameraCompatControlState != CAMERA_COMPAT_CONTROL_DISMISSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* A class holding the state of the compat UI hints, which is shared between all compat UI
|
||||
* window managers.
|
||||
*/
|
||||
static class CompatUIHintsState {
|
||||
@VisibleForTesting
|
||||
boolean mHasShownSizeCompatHint;
|
||||
@VisibleForTesting
|
||||
boolean mHasShownCameraCompatHint;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@ import com.android.wm.shell.common.DisplayLayout;
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* Window manager for the reachability education
|
||||
*/
|
||||
@@ -73,6 +75,8 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract {
|
||||
// we need to animate them.
|
||||
private boolean mHasLetterboxSizeChanged;
|
||||
|
||||
private final BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> mOnDismissCallback;
|
||||
|
||||
@Nullable
|
||||
@VisibleForTesting
|
||||
ReachabilityEduLayout mLayout;
|
||||
@@ -80,7 +84,8 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract {
|
||||
ReachabilityEduWindowManager(Context context, TaskInfo taskInfo,
|
||||
SyncTransactionQueue syncQueue,
|
||||
ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
|
||||
CompatUIConfiguration compatUIConfiguration, ShellExecutor mainExecutor) {
|
||||
CompatUIConfiguration compatUIConfiguration, ShellExecutor mainExecutor,
|
||||
BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> onDismissCallback) {
|
||||
super(context, taskInfo, syncQueue, taskListener, displayLayout);
|
||||
mIsActivityLetterboxed = taskInfo.isLetterboxDoubleTapEnabled;
|
||||
mLetterboxVerticalPosition = taskInfo.topActivityLetterboxVerticalPosition;
|
||||
@@ -89,6 +94,7 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract {
|
||||
mTopActivityLetterboxHeight = taskInfo.topActivityLetterboxHeight;
|
||||
mCompatUIConfiguration = compatUIConfiguration;
|
||||
mMainExecutor = mainExecutor;
|
||||
mOnDismissCallback = onDismissCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -217,13 +223,17 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract {
|
||||
return;
|
||||
}
|
||||
final TaskInfo lastTaskInfo = getLastTaskInfo();
|
||||
final boolean hasSeenHorizontalReachabilityEdu =
|
||||
mCompatUIConfiguration.hasSeenHorizontalReachabilityEducation(lastTaskInfo);
|
||||
final boolean hasSeenVerticalReachabilityEdu =
|
||||
mCompatUIConfiguration.hasSeenVerticalReachabilityEducation(lastTaskInfo);
|
||||
final boolean eligibleForDisplayHorizontalEducation = mForceUpdate
|
||||
|| !mCompatUIConfiguration.hasSeenHorizontalReachabilityEducation(lastTaskInfo)
|
||||
|| !hasSeenHorizontalReachabilityEdu
|
||||
|| (mHasUserDoubleTapped
|
||||
&& (mLetterboxHorizontalPosition == REACHABILITY_LEFT_OR_UP_POSITION
|
||||
|| mLetterboxHorizontalPosition == REACHABILITY_RIGHT_OR_BOTTOM_POSITION));
|
||||
final boolean eligibleForDisplayVerticalEducation = mForceUpdate
|
||||
|| !mCompatUIConfiguration.hasSeenVerticalReachabilityEducation(lastTaskInfo)
|
||||
|| !hasSeenVerticalReachabilityEdu
|
||||
|| (mHasUserDoubleTapped
|
||||
&& (mLetterboxVerticalPosition == REACHABILITY_LEFT_OR_UP_POSITION
|
||||
|| mLetterboxVerticalPosition == REACHABILITY_RIGHT_OR_BOTTOM_POSITION));
|
||||
@@ -239,6 +249,14 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract {
|
||||
if (!mHasLetterboxSizeChanged) {
|
||||
updateHideTime();
|
||||
mMainExecutor.executeDelayed(this::hideReachability, DISAPPEAR_DELAY_MS);
|
||||
// If reachability education has been seen for the first time, trigger callback to
|
||||
// display aspect ratio settings button once reachability education disappears
|
||||
if (hasShownHorizontalReachabilityEduFirstTime(hasSeenHorizontalReachabilityEdu)
|
||||
|| hasShownVerticalReachabilityEduFirstTime(
|
||||
hasSeenVerticalReachabilityEdu)) {
|
||||
mMainExecutor.executeDelayed(this::triggerOnDismissCallback,
|
||||
DISAPPEAR_DELAY_MS);
|
||||
}
|
||||
}
|
||||
mHasUserDoubleTapped = false;
|
||||
} else {
|
||||
@@ -246,6 +264,38 @@ class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the value of
|
||||
* {@link CompatUIConfiguration#hasSeenHorizontalReachabilityEducation} before and after the
|
||||
* layout is shown. Horizontal reachability education is considered seen for the first time if
|
||||
* prior to viewing the layout,
|
||||
* {@link CompatUIConfiguration#hasSeenHorizontalReachabilityEducation} is {@code false}
|
||||
* but becomes {@code true} once the current layout is shown.
|
||||
*/
|
||||
private boolean hasShownHorizontalReachabilityEduFirstTime(
|
||||
boolean previouslyShownHorizontalReachabilityEducation) {
|
||||
return !previouslyShownHorizontalReachabilityEducation
|
||||
&& mCompatUIConfiguration.hasSeenHorizontalReachabilityEducation(getLastTaskInfo());
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the value of
|
||||
* {@link CompatUIConfiguration#hasSeenVerticalReachabilityEducation} before and after the
|
||||
* layout is shown. Horizontal reachability education is considered seen for the first time if
|
||||
* prior to viewing the layout,
|
||||
* {@link CompatUIConfiguration#hasSeenVerticalReachabilityEducation} is {@code false}
|
||||
* but becomes {@code true} once the current layout is shown.
|
||||
*/
|
||||
private boolean hasShownVerticalReachabilityEduFirstTime(
|
||||
boolean previouslyShownVerticalReachabilityEducation) {
|
||||
return !previouslyShownVerticalReachabilityEducation
|
||||
&& mCompatUIConfiguration.hasSeenVerticalReachabilityEducation(getLastTaskInfo());
|
||||
}
|
||||
|
||||
private void triggerOnDismissCallback() {
|
||||
mOnDismissCallback.accept(getLastTaskInfo(), getTaskListener());
|
||||
}
|
||||
|
||||
private void hideReachability() {
|
||||
if (mLayout == null || !shouldHideEducation()) {
|
||||
return;
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.IdRes;
|
||||
import android.annotation.NonNull;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Layout for the user aspect ratio button which opens the app list page in settings
|
||||
* and allows users to change apps aspect ratio.
|
||||
*/
|
||||
public class UserAspectRatioSettingsLayout extends LinearLayout {
|
||||
|
||||
private static final float ALPHA_FULL_TRANSPARENT = 0f;
|
||||
|
||||
private static final float ALPHA_FULL_OPAQUE = 1f;
|
||||
|
||||
private static final long VISIBILITY_ANIMATION_DURATION_MS = 50;
|
||||
|
||||
private static final String ALPHA_PROPERTY_NAME = "alpha";
|
||||
|
||||
private UserAspectRatioSettingsWindowManager mWindowManager;
|
||||
|
||||
public UserAspectRatioSettingsLayout(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public UserAspectRatioSettingsLayout(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public UserAspectRatioSettingsLayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
this(context, attrs, defStyleAttr, 0);
|
||||
}
|
||||
|
||||
public UserAspectRatioSettingsLayout(Context context, AttributeSet attrs, int defStyleAttr,
|
||||
int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
void inject(@NonNull UserAspectRatioSettingsWindowManager windowManager) {
|
||||
mWindowManager = windowManager;
|
||||
}
|
||||
|
||||
void setUserAspectRatioSettingsHintVisibility(boolean show) {
|
||||
setViewVisibility(R.id.user_aspect_ratio_settings_hint, show);
|
||||
}
|
||||
|
||||
void setUserAspectRatioButtonVisibility(boolean show) {
|
||||
setViewVisibility(R.id.user_aspect_ratio_settings_button, show);
|
||||
// Hint should never be visible without button.
|
||||
if (!show) {
|
||||
setUserAspectRatioSettingsHintVisibility(/* show= */ false);
|
||||
}
|
||||
}
|
||||
|
||||
private void setViewVisibility(@IdRes int resId, boolean show) {
|
||||
final View view = findViewById(resId);
|
||||
int visibility = show ? View.VISIBLE : View.GONE;
|
||||
if (view.getVisibility() == visibility) {
|
||||
return;
|
||||
}
|
||||
if (show) {
|
||||
showItem(view);
|
||||
} else {
|
||||
view.setVisibility(visibility);
|
||||
}
|
||||
}
|
||||
|
||||
@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 setUserAspectRatioButtonVisibility can result in flaky animation.
|
||||
mWindowManager.relayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
|
||||
final ImageButton userAspectRatioButton =
|
||||
findViewById(R.id.user_aspect_ratio_settings_button);
|
||||
userAspectRatioButton.setOnClickListener(
|
||||
view -> mWindowManager.onUserAspectRatioSettingsButtonClicked());
|
||||
userAspectRatioButton.setOnLongClickListener(view -> {
|
||||
mWindowManager.onUserAspectRatioSettingsButtonLongClicked();
|
||||
return true;
|
||||
});
|
||||
|
||||
final LinearLayout sizeCompatHint = findViewById(R.id.user_aspect_ratio_settings_hint);
|
||||
((TextView) sizeCompatHint.findViewById(R.id.compat_mode_hint_text))
|
||||
.setText(R.string.user_aspect_ratio_settings_button_hint);
|
||||
sizeCompatHint.setOnClickListener(
|
||||
view -> setUserAspectRatioSettingsHintVisibility(/* show= */ false));
|
||||
}
|
||||
|
||||
private void showItem(@NonNull View view) {
|
||||
view.setVisibility(View.VISIBLE);
|
||||
final ObjectAnimator fadeIn = ObjectAnimator.ofFloat(view, ALPHA_PROPERTY_NAME,
|
||||
ALPHA_FULL_TRANSPARENT, ALPHA_FULL_OPAQUE);
|
||||
fadeIn.setDuration(VISIBILITY_ANIMATION_DURATION_MS);
|
||||
fadeIn.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
view.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
fadeIn.start();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.TaskInfo;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.os.SystemClock;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
||||
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.ShellExecutor;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* Window manager for the user aspect ratio settings button which allows users to go to
|
||||
* app settings and change apps aspect ratio.
|
||||
*/
|
||||
class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract {
|
||||
|
||||
private static final long SHOW_USER_ASPECT_RATIO_BUTTON_DELAY_MS = 500L;
|
||||
|
||||
private static final long HIDE_USER_ASPECT_RATIO_BUTTON_DELAY_MS = 4000L;
|
||||
|
||||
private long mNextButtonHideTimeMs = -1L;
|
||||
|
||||
private final BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> mOnButtonClicked;
|
||||
|
||||
private final ShellExecutor mShellExecutor;
|
||||
|
||||
@VisibleForTesting
|
||||
@NonNull
|
||||
final CompatUIHintsState mCompatUIHintsState;
|
||||
|
||||
@Nullable
|
||||
private UserAspectRatioSettingsLayout mLayout;
|
||||
|
||||
// Remember the last reported states in case visibility changes due to keyguard or IME updates.
|
||||
@VisibleForTesting
|
||||
boolean mHasUserAspectRatioSettingsButton;
|
||||
|
||||
UserAspectRatioSettingsWindowManager(@NonNull Context context, @NonNull TaskInfo taskInfo,
|
||||
@NonNull SyncTransactionQueue syncQueue,
|
||||
@Nullable ShellTaskOrganizer.TaskListener taskListener,
|
||||
@NonNull DisplayLayout displayLayout, @NonNull CompatUIHintsState compatUIHintsState,
|
||||
@NonNull BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> onButtonClicked,
|
||||
@NonNull ShellExecutor shellExecutor) {
|
||||
super(context, taskInfo, syncQueue, taskListener, displayLayout);
|
||||
mShellExecutor = shellExecutor;
|
||||
mHasUserAspectRatioSettingsButton = getHasUserAspectRatioSettingsButton(taskInfo);
|
||||
mCompatUIHintsState = compatUIHintsState;
|
||||
mOnButtonClicked = onButtonClicked;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getZOrder() {
|
||||
return TASK_CHILD_LAYER_COMPAT_UI + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable View getLayout() {
|
||||
return mLayout;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeLayout() {
|
||||
mLayout = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean eligibleToShowLayout() {
|
||||
return mHasUserAspectRatioSettingsButton;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View createLayout() {
|
||||
mLayout = inflateLayout();
|
||||
mLayout.inject(this);
|
||||
|
||||
updateVisibilityOfViews();
|
||||
|
||||
return mLayout;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
UserAspectRatioSettingsLayout inflateLayout() {
|
||||
return (UserAspectRatioSettingsLayout) LayoutInflater.from(mContext).inflate(
|
||||
R.layout.user_aspect_ratio_settings_layout, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateCompatInfo(@NonNull TaskInfo taskInfo,
|
||||
@NonNull ShellTaskOrganizer.TaskListener taskListener, boolean canShow) {
|
||||
final boolean prevHasUserAspectRatioSettingsButton = mHasUserAspectRatioSettingsButton;
|
||||
mHasUserAspectRatioSettingsButton = getHasUserAspectRatioSettingsButton(taskInfo);
|
||||
|
||||
if (!super.updateCompatInfo(taskInfo, taskListener, canShow)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prevHasUserAspectRatioSettingsButton != mHasUserAspectRatioSettingsButton) {
|
||||
updateVisibilityOfViews();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Called when the user aspect ratio settings button is clicked. */
|
||||
void onUserAspectRatioSettingsButtonClicked() {
|
||||
mOnButtonClicked.accept(getLastTaskInfo(), getTaskListener());
|
||||
}
|
||||
|
||||
/** Called when the user aspect ratio settings button is long clicked. */
|
||||
void onUserAspectRatioSettingsButtonLongClicked() {
|
||||
if (mLayout == null) {
|
||||
return;
|
||||
}
|
||||
mLayout.setUserAspectRatioSettingsHintVisibility(/* show= */ true);
|
||||
mNextButtonHideTimeMs = updateHideTime(HIDE_USER_ASPECT_RATIO_BUTTON_DELAY_MS);
|
||||
mShellExecutor.executeDelayed(this::hideUserAspectRatioButton,
|
||||
HIDE_USER_ASPECT_RATIO_BUTTON_DELAY_MS);
|
||||
}
|
||||
|
||||
@Override
|
||||
@VisibleForTesting
|
||||
public void updateSurfacePosition() {
|
||||
if (mLayout == null) {
|
||||
return;
|
||||
}
|
||||
// Position of the button in the container coordinate.
|
||||
final Rect taskBounds = getTaskBounds();
|
||||
final Rect taskStableBounds = getTaskStableBounds();
|
||||
final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
|
||||
? taskStableBounds.left - taskBounds.left
|
||||
: taskStableBounds.right - taskBounds.left - mLayout.getMeasuredWidth();
|
||||
final int positionY = taskStableBounds.bottom - taskBounds.top
|
||||
- mLayout.getMeasuredHeight();
|
||||
updateSurfacePosition(positionX, positionY);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void updateVisibilityOfViews() {
|
||||
if (mHasUserAspectRatioSettingsButton) {
|
||||
mShellExecutor.executeDelayed(this::showUserAspectRatioButton,
|
||||
SHOW_USER_ASPECT_RATIO_BUTTON_DELAY_MS);
|
||||
mNextButtonHideTimeMs = updateHideTime(HIDE_USER_ASPECT_RATIO_BUTTON_DELAY_MS);
|
||||
mShellExecutor.executeDelayed(this::hideUserAspectRatioButton,
|
||||
HIDE_USER_ASPECT_RATIO_BUTTON_DELAY_MS);
|
||||
} else {
|
||||
mShellExecutor.removeCallbacks(this::showUserAspectRatioButton);
|
||||
mShellExecutor.execute(this::hideUserAspectRatioButton);
|
||||
}
|
||||
}
|
||||
|
||||
private void showUserAspectRatioButton() {
|
||||
if (mLayout == null) {
|
||||
return;
|
||||
}
|
||||
mLayout.setUserAspectRatioButtonVisibility(true);
|
||||
// Only show by default for the first time.
|
||||
if (!mCompatUIHintsState.mHasShownUserAspectRatioSettingsButtonHint) {
|
||||
mLayout.setUserAspectRatioSettingsHintVisibility(/* show= */ true);
|
||||
mCompatUIHintsState.mHasShownUserAspectRatioSettingsButtonHint = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void hideUserAspectRatioButton() {
|
||||
if (mLayout == null || !isHideDelayReached(mNextButtonHideTimeMs)) {
|
||||
return;
|
||||
}
|
||||
mLayout.setUserAspectRatioButtonVisibility(false);
|
||||
}
|
||||
|
||||
private boolean isHideDelayReached(long nextHideTime) {
|
||||
return SystemClock.uptimeMillis() >= nextHideTime;
|
||||
}
|
||||
|
||||
private long updateHideTime(long hideDelay) {
|
||||
return SystemClock.uptimeMillis() + hideDelay;
|
||||
}
|
||||
|
||||
private boolean getHasUserAspectRatioSettingsButton(@NonNull TaskInfo taskInfo) {
|
||||
return taskInfo.topActivityEligibleForUserAspectRatioButton
|
||||
&& (taskInfo.topActivityBoundsLetterboxed
|
||||
|| taskInfo.isUserFullscreenOverrideEnabled);
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,8 @@ import com.android.wm.shell.sysui.ShellController;
|
||||
import com.android.wm.shell.sysui.ShellInit;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
|
||||
import dagger.Lazy;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -66,8 +68,6 @@ import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import dagger.Lazy;
|
||||
|
||||
/**
|
||||
* Tests for {@link CompatUIController}.
|
||||
*
|
||||
@@ -82,21 +82,36 @@ public class CompatUIControllerTest extends ShellTestCase {
|
||||
|
||||
private CompatUIController mController;
|
||||
private ShellInit mShellInit;
|
||||
private @Mock ShellController mMockShellController;
|
||||
private @Mock DisplayController mMockDisplayController;
|
||||
private @Mock DisplayInsetsController mMockDisplayInsetsController;
|
||||
private @Mock DisplayLayout mMockDisplayLayout;
|
||||
private @Mock DisplayImeController mMockImeController;
|
||||
private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener;
|
||||
private @Mock SyncTransactionQueue mMockSyncQueue;
|
||||
private @Mock ShellExecutor mMockExecutor;
|
||||
private @Mock Lazy<Transitions> mMockTransitionsLazy;
|
||||
private @Mock CompatUIWindowManager mMockCompatLayout;
|
||||
private @Mock LetterboxEduWindowManager mMockLetterboxEduLayout;
|
||||
private @Mock RestartDialogWindowManager mMockRestartDialogLayout;
|
||||
private @Mock DockStateReader mDockStateReader;
|
||||
private @Mock CompatUIConfiguration mCompatUIConfiguration;
|
||||
private @Mock CompatUIShellCommandHandler mCompatUIShellCommandHandler;
|
||||
@Mock
|
||||
private ShellController mMockShellController;
|
||||
@Mock
|
||||
private DisplayController mMockDisplayController;
|
||||
@Mock
|
||||
private DisplayInsetsController mMockDisplayInsetsController;
|
||||
@Mock
|
||||
private DisplayLayout mMockDisplayLayout;
|
||||
@Mock
|
||||
private DisplayImeController mMockImeController;
|
||||
@Mock
|
||||
private ShellTaskOrganizer.TaskListener mMockTaskListener;
|
||||
@Mock
|
||||
private SyncTransactionQueue mMockSyncQueue;
|
||||
@Mock
|
||||
private ShellExecutor mMockExecutor;
|
||||
@Mock
|
||||
private Lazy<Transitions> mMockTransitionsLazy;
|
||||
@Mock
|
||||
private CompatUIWindowManager mMockCompatLayout;
|
||||
@Mock
|
||||
private LetterboxEduWindowManager mMockLetterboxEduLayout;
|
||||
@Mock
|
||||
private RestartDialogWindowManager mMockRestartDialogLayout;
|
||||
@Mock
|
||||
private DockStateReader mDockStateReader;
|
||||
@Mock
|
||||
private CompatUIConfiguration mCompatUIConfiguration;
|
||||
@Mock
|
||||
private CompatUIShellCommandHandler mCompatUIShellCommandHandler;
|
||||
|
||||
@Captor
|
||||
ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
|
||||
|
||||
@@ -44,7 +44,7 @@ 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 com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState;
|
||||
import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ 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 com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState;
|
||||
import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReachabilityEduWindowManager}.
|
||||
*
|
||||
@@ -57,6 +59,8 @@ public class ReachabilityEduWindowManagerTest extends ShellTestCase {
|
||||
private CompatUIConfiguration mCompatUIConfiguration;
|
||||
@Mock
|
||||
private DisplayLayout mDisplayLayout;
|
||||
@Mock
|
||||
private BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> mOnDismissCallback;
|
||||
private TestShellExecutor mExecutor;
|
||||
private TaskInfo mTaskInfo;
|
||||
private ReachabilityEduWindowManager mWindowManager;
|
||||
@@ -104,6 +108,7 @@ public class ReachabilityEduWindowManagerTest extends ShellTestCase {
|
||||
|
||||
private ReachabilityEduWindowManager createReachabilityEduWindowManager(TaskInfo taskInfo) {
|
||||
return new ReachabilityEduWindowManager(mContext, taskInfo, mSyncTransactionQueue,
|
||||
mTaskListener, mDisplayLayout, mCompatUIConfiguration, mExecutor);
|
||||
mTaskListener, mDisplayLayout, mCompatUIConfiguration, mExecutor,
|
||||
mOnDismissCallback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
|
||||
|
||||
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.app.ActivityManager;
|
||||
import android.app.TaskInfo;
|
||||
import android.app.TaskInfo.CameraCompatControlState;
|
||||
import android.content.ComponentName;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.util.Pair;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.SurfaceControlViewHost;
|
||||
import android.widget.ImageButton;
|
||||
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.TestShellExecutor;
|
||||
import com.android.wm.shell.common.DisplayLayout;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* Tests for {@link UserAspectRatioSettingsLayout}.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* atest WMShellUnitTests:UserAspectRatioSettingsLayoutTest
|
||||
*/
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@SmallTest
|
||||
public class UserAspectRatioSettingsLayoutTest extends ShellTestCase {
|
||||
|
||||
private static final int TASK_ID = 1;
|
||||
|
||||
@Mock
|
||||
private SyncTransactionQueue mSyncTransactionQueue;
|
||||
@Mock
|
||||
private BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener>
|
||||
mOnUserAspectRatioSettingsButtonClicked;
|
||||
@Mock
|
||||
private ShellTaskOrganizer.TaskListener mTaskListener;
|
||||
@Mock
|
||||
private SurfaceControlViewHost mViewHost;
|
||||
@Captor
|
||||
private ArgumentCaptor<ShellTaskOrganizer.TaskListener> mUserAspectRatioTaskListenerCaptor;
|
||||
@Captor
|
||||
private ArgumentCaptor<TaskInfo> mUserAspectRationTaskInfoCaptor;
|
||||
|
||||
private UserAspectRatioSettingsWindowManager mWindowManager;
|
||||
private UserAspectRatioSettingsLayout mLayout;
|
||||
private TaskInfo mTaskInfo;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mTaskInfo = createTaskInfo(/* hasSizeCompat= */ false, CAMERA_COMPAT_CONTROL_HIDDEN);
|
||||
mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo,
|
||||
mSyncTransactionQueue, mTaskListener, new DisplayLayout(),
|
||||
new CompatUIController.CompatUIHintsState(),
|
||||
mOnUserAspectRatioSettingsButtonClicked, new TestShellExecutor());
|
||||
|
||||
mLayout = (UserAspectRatioSettingsLayout) LayoutInflater.from(mContext).inflate(
|
||||
R.layout.user_aspect_ratio_settings_layout, null);
|
||||
mLayout.inject(mWindowManager);
|
||||
|
||||
spyOn(mWindowManager);
|
||||
spyOn(mLayout);
|
||||
doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
|
||||
doReturn(mLayout).when(mWindowManager).inflateLayout();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnClickForUserAspectRatioSettingsButton() {
|
||||
final ImageButton button = mLayout.findViewById(R.id.user_aspect_ratio_settings_button);
|
||||
button.performClick();
|
||||
|
||||
verify(mWindowManager).onUserAspectRatioSettingsButtonClicked();
|
||||
verify(mOnUserAspectRatioSettingsButtonClicked).accept(
|
||||
mUserAspectRationTaskInfoCaptor.capture(),
|
||||
mUserAspectRatioTaskListenerCaptor.capture());
|
||||
final Pair<TaskInfo, ShellTaskOrganizer.TaskListener> result =
|
||||
new Pair<>(mUserAspectRationTaskInfoCaptor.getValue(),
|
||||
mUserAspectRatioTaskListenerCaptor.getValue());
|
||||
Assert.assertEquals(mTaskInfo, result.first);
|
||||
Assert.assertEquals(mTaskListener, result.second);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnLongClickForUserAspectRatioButton() {
|
||||
doNothing().when(mWindowManager).onUserAspectRatioSettingsButtonLongClicked();
|
||||
|
||||
final ImageButton button = mLayout.findViewById(R.id.user_aspect_ratio_settings_button);
|
||||
button.performLongClick();
|
||||
|
||||
verify(mWindowManager).onUserAspectRatioSettingsButtonLongClicked();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnClickForUserAspectRatioSettingsHint() {
|
||||
mWindowManager.mHasUserAspectRatioSettingsButton = true;
|
||||
mWindowManager.createLayout(/* canShow= */ true);
|
||||
final LinearLayout sizeCompatHint = mLayout.findViewById(
|
||||
R.id.user_aspect_ratio_settings_hint);
|
||||
sizeCompatHint.performClick();
|
||||
|
||||
verify(mLayout).setUserAspectRatioSettingsHintVisibility(/* show= */ false);
|
||||
}
|
||||
|
||||
private static TaskInfo createTaskInfo(boolean hasSizeCompat,
|
||||
@CameraCompatControlState int cameraCompatControlState) {
|
||||
ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
|
||||
taskInfo.taskId = TASK_ID;
|
||||
taskInfo.topActivityInSizeCompat = hasSizeCompat;
|
||||
taskInfo.cameraCompatControlState = cameraCompatControlState;
|
||||
taskInfo.realActivity = new ComponentName("com.mypackage.test", "TestActivity");
|
||||
return taskInfo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.WindowInsets.Type.navigationBars;
|
||||
|
||||
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.assertTrue;
|
||||
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.verify;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.TaskInfo;
|
||||
import android.content.ComponentName;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.util.Pair;
|
||||
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.TestShellExecutor;
|
||||
import com.android.wm.shell.common.DisplayLayout;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* Tests for {@link UserAspectRatioSettingsWindowManager}.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* atest WMShellUnitTests:UserAspectRatioSettingsWindowManagerTest
|
||||
*/
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@SmallTest
|
||||
public class UserAspectRatioSettingsWindowManagerTest extends ShellTestCase {
|
||||
|
||||
private static final int TASK_ID = 1;
|
||||
|
||||
@Mock private SyncTransactionQueue mSyncTransactionQueue;
|
||||
@Mock
|
||||
private BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener>
|
||||
mOnUserAspectRatioSettingsButtonClicked;
|
||||
@Mock private ShellTaskOrganizer.TaskListener mTaskListener;
|
||||
@Mock private UserAspectRatioSettingsLayout mLayout;
|
||||
@Mock private SurfaceControlViewHost mViewHost;
|
||||
@Captor
|
||||
private ArgumentCaptor<ShellTaskOrganizer.TaskListener> mUserAspectRatioTaskListenerCaptor;
|
||||
@Captor
|
||||
private ArgumentCaptor<TaskInfo> mUserAspectRationTaskInfoCaptor;
|
||||
|
||||
private final Set<String> mPackageNameCache = new HashSet<>();
|
||||
|
||||
private UserAspectRatioSettingsWindowManager mWindowManager;
|
||||
private TaskInfo mTaskInfo;
|
||||
|
||||
private TestShellExecutor mExecutor;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mExecutor = new TestShellExecutor();
|
||||
mTaskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
|
||||
false, /* topActivityBoundsLetterboxed */ true);
|
||||
mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo,
|
||||
mSyncTransactionQueue, mTaskListener, new DisplayLayout(), new CompatUIHintsState(),
|
||||
mOnUserAspectRatioSettingsButtonClicked, mExecutor);
|
||||
spyOn(mWindowManager);
|
||||
doReturn(mLayout).when(mWindowManager).inflateLayout();
|
||||
doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateUserAspectRatioButton() {
|
||||
// Doesn't create layout if show is false.
|
||||
mWindowManager.mHasUserAspectRatioSettingsButton = true;
|
||||
assertTrue(mWindowManager.createLayout(/* canShow= */ false));
|
||||
|
||||
verify(mWindowManager, never()).inflateLayout();
|
||||
|
||||
// Doesn't create hint popup.
|
||||
mWindowManager.mCompatUIHintsState.mHasShownUserAspectRatioSettingsButtonHint = true;
|
||||
assertTrue(mWindowManager.createLayout(/* canShow= */ true));
|
||||
|
||||
verify(mWindowManager).inflateLayout();
|
||||
mExecutor.flushAll();
|
||||
verify(mLayout).setUserAspectRatioButtonVisibility(/* show= */ true);
|
||||
verify(mLayout, never()).setUserAspectRatioSettingsHintVisibility(/* show= */ true);
|
||||
|
||||
// Creates hint popup.
|
||||
clearInvocations(mWindowManager);
|
||||
clearInvocations(mLayout);
|
||||
mWindowManager.release();
|
||||
mWindowManager.mCompatUIHintsState.mHasShownUserAspectRatioSettingsButtonHint = false;
|
||||
assertTrue(mWindowManager.createLayout(/* canShow= */ true));
|
||||
|
||||
verify(mWindowManager).inflateLayout();
|
||||
assertNotNull(mLayout);
|
||||
mExecutor.flushAll();
|
||||
verify(mLayout).setUserAspectRatioButtonVisibility(/* show= */ true);
|
||||
verify(mLayout).setUserAspectRatioSettingsHintVisibility(/* show= */ true);
|
||||
assertTrue(mWindowManager.mCompatUIHintsState.mHasShownUserAspectRatioSettingsButtonHint);
|
||||
|
||||
// Returns false and doesn't create layout if mHasUserAspectRatioSettingsButton is false.
|
||||
clearInvocations(mWindowManager);
|
||||
mWindowManager.release();
|
||||
mWindowManager.mHasUserAspectRatioSettingsButton = false;
|
||||
assertFalse(mWindowManager.createLayout(/* canShow= */ true));
|
||||
|
||||
verify(mWindowManager, never()).inflateLayout();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRelease() {
|
||||
mWindowManager.mHasUserAspectRatioSettingsButton = true;
|
||||
mWindowManager.createLayout(/* canShow= */ true);
|
||||
|
||||
verify(mWindowManager).inflateLayout();
|
||||
|
||||
mWindowManager.release();
|
||||
|
||||
verify(mViewHost).release();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateCompatInfo() {
|
||||
mWindowManager.mHasUserAspectRatioSettingsButton = true;
|
||||
mWindowManager.createLayout(/* canShow= */ true);
|
||||
|
||||
// No diff
|
||||
clearInvocations(mWindowManager);
|
||||
TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
|
||||
true, /* topActivityBoundsLetterboxed */ true);
|
||||
assertTrue(mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true));
|
||||
|
||||
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);
|
||||
assertTrue(mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
|
||||
|
||||
verify(mWindowManager).release();
|
||||
verify(mWindowManager).createLayout(/* canShow= */ true);
|
||||
|
||||
// Change has eligibleForUserAspectRatioButton to false, dispose the component
|
||||
clearInvocations(mWindowManager);
|
||||
clearInvocations(mLayout);
|
||||
taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
|
||||
false, /* topActivityBoundsLetterboxed */ true);
|
||||
assertFalse(
|
||||
mWindowManager.updateCompatInfo(taskInfo, newTaskListener, /* canShow= */ true));
|
||||
verify(mWindowManager).release();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateCompatInfoLayoutNotInflatedYet() {
|
||||
mWindowManager.mHasUserAspectRatioSettingsButton = true;
|
||||
mWindowManager.createLayout(/* canShow= */ false);
|
||||
|
||||
verify(mWindowManager, never()).inflateLayout();
|
||||
|
||||
// Change topActivityInSizeCompat to false and pass canShow true, layout shouldn't be
|
||||
// inflated
|
||||
clearInvocations(mWindowManager);
|
||||
TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
|
||||
false, /* topActivityBoundsLetterboxed */ true);
|
||||
mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
|
||||
|
||||
verify(mWindowManager, never()).inflateLayout();
|
||||
|
||||
// Change topActivityInSizeCompat to true and pass canShow true, layout should be inflated.
|
||||
clearInvocations(mWindowManager);
|
||||
taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
|
||||
true, /* topActivityBoundsLetterboxed */ true);
|
||||
mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
|
||||
|
||||
verify(mWindowManager).inflateLayout();
|
||||
}
|
||||
|
||||
@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();
|
||||
insetsState.setDisplayFrame(new Rect(0, 0, 1000, 2000));
|
||||
InsetsSource insetsSource = new InsetsSource(
|
||||
InsetsSource.createId(null, 0, navigationBars()), navigationBars());
|
||||
insetsSource.setFrame(0, 1800, 1000, 2000);
|
||||
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.removeLayout();
|
||||
mWindowManager.mHasUserAspectRatioSettingsButton = true;
|
||||
mWindowManager.updateVisibility(/* canShow= */ true);
|
||||
|
||||
verify(mWindowManager).createLayout(/* canShow= */ true);
|
||||
|
||||
// Hide button.
|
||||
clearInvocations(mWindowManager);
|
||||
doReturn(View.VISIBLE).when(mLayout).getVisibility();
|
||||
mWindowManager.updateVisibility(/* canShow= */ false);
|
||||
|
||||
verify(mWindowManager, never()).createLayout(anyBoolean());
|
||||
verify(mLayout).setVisibility(View.GONE);
|
||||
|
||||
// Show button.
|
||||
doReturn(View.GONE).when(mLayout).getVisibility();
|
||||
mWindowManager.updateVisibility(/* canShow= */ true);
|
||||
|
||||
verify(mWindowManager, never()).createLayout(anyBoolean());
|
||||
verify(mLayout).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 testOnUserAspectRatioButtonClicked() {
|
||||
mWindowManager.onUserAspectRatioSettingsButtonClicked();
|
||||
|
||||
verify(mOnUserAspectRatioSettingsButtonClicked).accept(
|
||||
mUserAspectRationTaskInfoCaptor.capture(),
|
||||
mUserAspectRatioTaskListenerCaptor.capture());
|
||||
final Pair<TaskInfo, ShellTaskOrganizer.TaskListener> result =
|
||||
new Pair<>(mUserAspectRationTaskInfoCaptor.getValue(),
|
||||
mUserAspectRatioTaskListenerCaptor.getValue());
|
||||
Assert.assertEquals(mTaskInfo, result.first);
|
||||
Assert.assertEquals(mTaskListener, result.second);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnUserAspectRatioButtonLongClicked_showHint() {
|
||||
// Not create hint popup.
|
||||
mWindowManager.mHasUserAspectRatioSettingsButton = true;
|
||||
mWindowManager.mCompatUIHintsState.mHasShownUserAspectRatioSettingsButtonHint = true;
|
||||
mWindowManager.createLayout(/* canShow= */ true);
|
||||
|
||||
verify(mWindowManager).inflateLayout();
|
||||
verify(mLayout, never()).setUserAspectRatioSettingsHintVisibility(/* show= */ true);
|
||||
|
||||
mWindowManager.onUserAspectRatioSettingsButtonLongClicked();
|
||||
|
||||
verify(mLayout).setUserAspectRatioSettingsHintVisibility(/* show= */ true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWhenDockedStateHasChanged_needsToBeRecreated() {
|
||||
ActivityManager.RunningTaskInfo newTaskInfo = new ActivityManager.RunningTaskInfo();
|
||||
newTaskInfo.configuration.uiMode |= Configuration.UI_MODE_TYPE_DESK;
|
||||
|
||||
Assert.assertTrue(mWindowManager.needsToBeRecreated(newTaskInfo, mTaskListener));
|
||||
}
|
||||
|
||||
private static TaskInfo createTaskInfo(boolean eligibleForUserAspectRatioButton,
|
||||
boolean topActivityBoundsLetterboxed) {
|
||||
ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
|
||||
taskInfo.taskId = TASK_ID;
|
||||
taskInfo.topActivityEligibleForUserAspectRatioButton = eligibleForUserAspectRatioButton;
|
||||
taskInfo.topActivityBoundsLetterboxed = topActivityBoundsLetterboxed;
|
||||
taskInfo.configuration.uiMode &= ~Configuration.UI_MODE_TYPE_DESK;
|
||||
taskInfo.realActivity = new ComponentName("com.mypackage.test", "TestActivity");
|
||||
return taskInfo;
|
||||
}
|
||||
}
|
||||
@@ -969,8 +969,10 @@ final class LetterboxUiController {
|
||||
final Rect innerFrame = hasInheritedLetterboxBehavior()
|
||||
? mActivityRecord.getBounds() : w.getFrame();
|
||||
mLetterbox.layout(spaceToFill, innerFrame, mTmpPoint);
|
||||
// We need to notify Shell that letterbox position has changed.
|
||||
mActivityRecord.getTask().dispatchTaskInfoChangedIfNeeded(true /* force */);
|
||||
if (mDoubleTapEvent) {
|
||||
// We need to notify Shell that letterbox position has changed.
|
||||
mActivityRecord.getTask().dispatchTaskInfoChangedIfNeeded(true /* force */);
|
||||
}
|
||||
} else if (mLetterbox != null) {
|
||||
mLetterbox.hide();
|
||||
}
|
||||
@@ -1242,6 +1244,7 @@ final class LetterboxUiController {
|
||||
? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_LEFT
|
||||
: LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__RIGHT_TO_CENTER;
|
||||
logLetterboxPositionChange(changeToLog);
|
||||
mDoubleTapEvent = true;
|
||||
} else if (mLetterbox.getInnerFrame().right < x) {
|
||||
// Moving to the next stop on the right side of the app window: left > center > right.
|
||||
mLetterboxConfiguration.movePositionForHorizontalReachabilityToNextRightStop(
|
||||
@@ -1252,8 +1255,8 @@ final class LetterboxUiController {
|
||||
? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_RIGHT
|
||||
: LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__LEFT_TO_CENTER;
|
||||
logLetterboxPositionChange(changeToLog);
|
||||
mDoubleTapEvent = true;
|
||||
}
|
||||
mDoubleTapEvent = true;
|
||||
// TODO(197549949): Add animation for transition.
|
||||
mActivityRecord.recomputeConfiguration();
|
||||
}
|
||||
@@ -1281,6 +1284,7 @@ final class LetterboxUiController {
|
||||
? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_TOP
|
||||
: LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__BOTTOM_TO_CENTER;
|
||||
logLetterboxPositionChange(changeToLog);
|
||||
mDoubleTapEvent = true;
|
||||
} else if (mLetterbox.getInnerFrame().bottom < y) {
|
||||
// Moving to the next stop on the bottom side of the app window: top > center > bottom.
|
||||
mLetterboxConfiguration.movePositionForVerticalReachabilityToNextBottomStop(
|
||||
@@ -1291,8 +1295,8 @@ final class LetterboxUiController {
|
||||
? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_BOTTOM
|
||||
: LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__TOP_TO_CENTER;
|
||||
logLetterboxPositionChange(changeToLog);
|
||||
mDoubleTapEvent = true;
|
||||
}
|
||||
mDoubleTapEvent = true;
|
||||
// TODO(197549949): Add animation for transition.
|
||||
mActivityRecord.recomputeConfiguration();
|
||||
}
|
||||
|
||||
@@ -3459,6 +3459,8 @@ class Task extends TaskFragment {
|
||||
info.topActivityLetterboxHorizontalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
|
||||
info.topActivityLetterboxWidth = TaskInfo.PROPERTY_VALUE_UNSET;
|
||||
info.topActivityLetterboxHeight = TaskInfo.PROPERTY_VALUE_UNSET;
|
||||
info.isUserFullscreenOverrideEnabled = top != null
|
||||
&& top.mLetterboxUiController.shouldApplyUserFullscreenOverride();
|
||||
info.isFromLetterboxDoubleTap = top != null && top.mLetterboxUiController.isFromDoubleTap();
|
||||
if (info.isLetterboxDoubleTapEnabled) {
|
||||
info.topActivityLetterboxWidth = top.getBounds().width();
|
||||
|
||||
Reference in New Issue
Block a user