Merge changes from topic "desktop-windowing-hide" into tm-qpr-dev
* changes: Hide some options when desktop mode is available Handle desktop mode setting change
This commit is contained in:
@@ -16,14 +16,20 @@
|
|||||||
|
|
||||||
package com.android.wm.shell;
|
package com.android.wm.shell;
|
||||||
|
|
||||||
|
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
|
||||||
|
|
||||||
|
import android.app.WindowConfiguration;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
import android.window.DisplayAreaAppearedInfo;
|
import android.window.DisplayAreaAppearedInfo;
|
||||||
import android.window.DisplayAreaInfo;
|
import android.window.DisplayAreaInfo;
|
||||||
import android.window.DisplayAreaOrganizer;
|
import android.window.DisplayAreaOrganizer;
|
||||||
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.android.internal.protolog.common.ProtoLog;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
@@ -102,10 +108,44 @@ public class RootDisplayAreaOrganizer extends DisplayAreaOrganizer {
|
|||||||
mDisplayAreasInfo.put(displayId, displayAreaInfo);
|
mDisplayAreasInfo.put(displayId, displayAreaInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@link WindowContainerTransaction} to update display windowing mode.
|
||||||
|
*
|
||||||
|
* @param displayId display id to update windowing mode for
|
||||||
|
* @param windowingMode target {@link WindowConfiguration.WindowingMode}
|
||||||
|
* @return {@link WindowContainerTransaction} with pending operation to set windowing mode
|
||||||
|
*/
|
||||||
|
public WindowContainerTransaction prepareWindowingModeChange(int displayId,
|
||||||
|
@WindowConfiguration.WindowingMode int windowingMode) {
|
||||||
|
WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
|
DisplayAreaInfo displayAreaInfo = mDisplayAreasInfo.get(displayId);
|
||||||
|
if (displayAreaInfo == null) {
|
||||||
|
ProtoLog.e(WM_SHELL_DESKTOP_MODE,
|
||||||
|
"unable to update windowing mode for display %d display not found", displayId);
|
||||||
|
return wct;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtoLog.d(WM_SHELL_DESKTOP_MODE,
|
||||||
|
"setWindowingMode: displayId=%d current wmMode=%d new wmMode=%d", displayId,
|
||||||
|
displayAreaInfo.configuration.windowConfiguration.getWindowingMode(),
|
||||||
|
windowingMode);
|
||||||
|
|
||||||
|
wct.setWindowingMode(displayAreaInfo.token, windowingMode);
|
||||||
|
return wct;
|
||||||
|
}
|
||||||
|
|
||||||
public void dump(@NonNull PrintWriter pw, String prefix) {
|
public void dump(@NonNull PrintWriter pw, String prefix) {
|
||||||
final String innerPrefix = prefix + " ";
|
final String innerPrefix = prefix + " ";
|
||||||
final String childPrefix = innerPrefix + " ";
|
final String childPrefix = innerPrefix + " ";
|
||||||
pw.println(prefix + this);
|
pw.println(prefix + this);
|
||||||
|
|
||||||
|
for (int i = 0; i < mDisplayAreasInfo.size(); i++) {
|
||||||
|
int displayId = mDisplayAreasInfo.keyAt(i);
|
||||||
|
DisplayAreaInfo displayAreaInfo = mDisplayAreasInfo.get(displayId);
|
||||||
|
int windowingMode =
|
||||||
|
displayAreaInfo.configuration.windowConfiguration.getWindowingMode();
|
||||||
|
pw.println(innerPrefix + "# displayId=" + displayId + " wmMode=" + windowingMode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
|||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||||
|
|
||||||
|
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
|
||||||
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
|
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
|
||||||
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
|
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
|
||||||
|
|
||||||
@@ -46,6 +47,7 @@ import android.window.StartingWindowInfo;
|
|||||||
import android.window.StartingWindowRemovalInfo;
|
import android.window.StartingWindowRemovalInfo;
|
||||||
import android.window.TaskAppearedInfo;
|
import android.window.TaskAppearedInfo;
|
||||||
import android.window.TaskOrganizer;
|
import android.window.TaskOrganizer;
|
||||||
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.protolog.common.ProtoLog;
|
import com.android.internal.protolog.common.ProtoLog;
|
||||||
@@ -690,6 +692,49 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
|
|||||||
taskListener.reparentChildSurfaceToTask(taskId, sc, t);
|
taskListener.reparentChildSurfaceToTask(taskId, sc, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@link WindowContainerTransaction} to clear task bounds.
|
||||||
|
*
|
||||||
|
* @param displayId display id for tasks that will have bounds cleared
|
||||||
|
* @return {@link WindowContainerTransaction} with pending operations to clear bounds
|
||||||
|
*/
|
||||||
|
public WindowContainerTransaction prepareClearBoundsForTasks(int displayId) {
|
||||||
|
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "prepareClearBoundsForTasks: displayId=%d", displayId);
|
||||||
|
WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
|
for (int i = 0; i < mTasks.size(); i++) {
|
||||||
|
RunningTaskInfo taskInfo = mTasks.valueAt(i).getTaskInfo();
|
||||||
|
if (taskInfo.displayId == displayId) {
|
||||||
|
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "clearing bounds for token=%s taskInfo=%s",
|
||||||
|
taskInfo.token, taskInfo);
|
||||||
|
wct.setBounds(taskInfo.token, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return wct;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@link WindowContainerTransaction} to clear task level freeform setting.
|
||||||
|
*
|
||||||
|
* @param displayId display id for tasks that will have windowing mode reset to {@link
|
||||||
|
* WindowConfiguration#WINDOWING_MODE_UNDEFINED}
|
||||||
|
* @return {@link WindowContainerTransaction} with pending operations to clear windowing mode
|
||||||
|
*/
|
||||||
|
public WindowContainerTransaction prepareClearFreeformForTasks(int displayId) {
|
||||||
|
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "prepareClearFreeformForTasks: displayId=%d", displayId);
|
||||||
|
WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
|
for (int i = 0; i < mTasks.size(); i++) {
|
||||||
|
RunningTaskInfo taskInfo = mTasks.valueAt(i).getTaskInfo();
|
||||||
|
if (taskInfo.displayId == displayId
|
||||||
|
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
|
||||||
|
ProtoLog.d(WM_SHELL_DESKTOP_MODE,
|
||||||
|
"clearing windowing mode for token=%s taskInfo=%s", taskInfo.token,
|
||||||
|
taskInfo);
|
||||||
|
wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return wct;
|
||||||
|
}
|
||||||
|
|
||||||
private void logSizeCompatRestartButtonEventReported(@NonNull TaskAppearedInfo info,
|
private void logSizeCompatRestartButtonEventReported(@NonNull TaskAppearedInfo info,
|
||||||
int event) {
|
int event) {
|
||||||
ActivityInfo topActivityInfo = info.getTaskInfo().topActivityInfo;
|
ActivityInfo topActivityInfo = info.getTaskInfo().topActivityInfo;
|
||||||
@@ -816,7 +861,14 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
|
|||||||
final int key = mTasks.keyAt(i);
|
final int key = mTasks.keyAt(i);
|
||||||
final TaskAppearedInfo info = mTasks.valueAt(i);
|
final TaskAppearedInfo info = mTasks.valueAt(i);
|
||||||
final TaskListener listener = getTaskListener(info.getTaskInfo());
|
final TaskListener listener = getTaskListener(info.getTaskInfo());
|
||||||
pw.println(innerPrefix + "#" + i + " task=" + key + " listener=" + listener);
|
final int windowingMode = info.getTaskInfo().getWindowingMode();
|
||||||
|
String pkg = "";
|
||||||
|
if (info.getTaskInfo().baseActivity != null) {
|
||||||
|
pkg = info.getTaskInfo().baseActivity.getPackageName();
|
||||||
|
}
|
||||||
|
Rect bounds = info.getTaskInfo().getConfiguration().windowConfiguration.getBounds();
|
||||||
|
pw.println(innerPrefix + "#" + i + " task=" + key + " listener=" + listener
|
||||||
|
+ " wmMode=" + windowingMode + " pkg=" + pkg + " bounds=" + bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
pw.println();
|
pw.println();
|
||||||
@@ -826,6 +878,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
|
|||||||
final TaskListener listener = mLaunchCookieToListener.valueAt(i);
|
final TaskListener listener = mLaunchCookieToListener.valueAt(i);
|
||||||
pw.println(innerPrefix + "#" + i + " cookie=" + key + " listener=" + listener);
|
pw.println(innerPrefix + "#" + i + " cookie=" + key + " listener=" + listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import com.android.internal.jank.InteractionJankMonitor;
|
|||||||
import com.android.internal.logging.UiEventLogger;
|
import com.android.internal.logging.UiEventLogger;
|
||||||
import com.android.internal.statusbar.IStatusBarService;
|
import com.android.internal.statusbar.IStatusBarService;
|
||||||
import com.android.launcher3.icons.IconProvider;
|
import com.android.launcher3.icons.IconProvider;
|
||||||
|
import com.android.wm.shell.RootDisplayAreaOrganizer;
|
||||||
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
|
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
|
||||||
import com.android.wm.shell.ShellTaskOrganizer;
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
import com.android.wm.shell.TaskViewTransitions;
|
import com.android.wm.shell.TaskViewTransitions;
|
||||||
@@ -48,6 +49,8 @@ import com.android.wm.shell.common.TaskStackListenerImpl;
|
|||||||
import com.android.wm.shell.common.TransactionPool;
|
import com.android.wm.shell.common.TransactionPool;
|
||||||
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
|
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
|
||||||
import com.android.wm.shell.common.annotations.ShellMainThread;
|
import com.android.wm.shell.common.annotations.ShellMainThread;
|
||||||
|
import com.android.wm.shell.desktopmode.DesktopModeConstants;
|
||||||
|
import com.android.wm.shell.desktopmode.DesktopModeController;
|
||||||
import com.android.wm.shell.draganddrop.DragAndDropController;
|
import com.android.wm.shell.draganddrop.DragAndDropController;
|
||||||
import com.android.wm.shell.freeform.FreeformComponents;
|
import com.android.wm.shell.freeform.FreeformComponents;
|
||||||
import com.android.wm.shell.freeform.FreeformTaskListener;
|
import com.android.wm.shell.freeform.FreeformTaskListener;
|
||||||
@@ -573,6 +576,27 @@ public abstract class WMShellModule {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Desktop mode (optional feature)
|
||||||
|
//
|
||||||
|
|
||||||
|
@WMSingleton
|
||||||
|
@Provides
|
||||||
|
static Optional<DesktopModeController> provideDesktopModeController(
|
||||||
|
Context context, ShellInit shellInit,
|
||||||
|
ShellTaskOrganizer shellTaskOrganizer,
|
||||||
|
RootDisplayAreaOrganizer rootDisplayAreaOrganizer,
|
||||||
|
@ShellMainThread Handler mainHandler
|
||||||
|
) {
|
||||||
|
if (DesktopModeConstants.IS_FEATURE_ENABLED) {
|
||||||
|
return Optional.of(new DesktopModeController(context, shellInit, shellTaskOrganizer,
|
||||||
|
rootDisplayAreaOrganizer,
|
||||||
|
mainHandler));
|
||||||
|
} else {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Misc
|
// Misc
|
||||||
//
|
//
|
||||||
@@ -583,7 +607,8 @@ public abstract class WMShellModule {
|
|||||||
@ShellCreateTriggerOverride
|
@ShellCreateTriggerOverride
|
||||||
@Provides
|
@Provides
|
||||||
static Object provideIndependentShellComponentsToCreate(
|
static Object provideIndependentShellComponentsToCreate(
|
||||||
SplitscreenPipMixedHandler splitscreenPipMixedHandler) {
|
SplitscreenPipMixedHandler splitscreenPipMixedHandler,
|
||||||
|
Optional<DesktopModeController> desktopModeController) {
|
||||||
return new Object();
|
return new Object();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.desktopmode;
|
||||||
|
|
||||||
|
import android.os.SystemProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constants for desktop mode feature
|
||||||
|
*/
|
||||||
|
public class DesktopModeConstants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag to indicate whether desktop mode is available on the device
|
||||||
|
*/
|
||||||
|
public static final boolean IS_FEATURE_ENABLED = SystemProperties.getBoolean(
|
||||||
|
"persist.wm.debug.desktop_mode", false);
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.desktopmode;
|
||||||
|
|
||||||
|
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
|
||||||
|
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||||
|
|
||||||
|
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.ContentObserver;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.internal.protolog.common.ProtoLog;
|
||||||
|
import com.android.wm.shell.RootDisplayAreaOrganizer;
|
||||||
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
|
import com.android.wm.shell.common.annotations.ShellMainThread;
|
||||||
|
import com.android.wm.shell.sysui.ShellInit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles windowing changes when desktop mode system setting changes
|
||||||
|
*/
|
||||||
|
public class DesktopModeController {
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
|
private final ShellTaskOrganizer mShellTaskOrganizer;
|
||||||
|
private final RootDisplayAreaOrganizer mRootDisplayAreaOrganizer;
|
||||||
|
private final SettingsObserver mSettingsObserver;
|
||||||
|
|
||||||
|
public DesktopModeController(Context context, ShellInit shellInit,
|
||||||
|
ShellTaskOrganizer shellTaskOrganizer,
|
||||||
|
RootDisplayAreaOrganizer rootDisplayAreaOrganizer,
|
||||||
|
@ShellMainThread Handler mainHandler) {
|
||||||
|
mContext = context;
|
||||||
|
mShellTaskOrganizer = shellTaskOrganizer;
|
||||||
|
mRootDisplayAreaOrganizer = rootDisplayAreaOrganizer;
|
||||||
|
mSettingsObserver = new SettingsObserver(mContext, mainHandler);
|
||||||
|
shellInit.addInitCallback(this::onInit, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onInit() {
|
||||||
|
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopModeController");
|
||||||
|
mSettingsObserver.observe();
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void updateDesktopModeEnabled(boolean enabled) {
|
||||||
|
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "updateDesktopModeState: enabled=%s", enabled);
|
||||||
|
|
||||||
|
int displayId = mContext.getDisplayId();
|
||||||
|
|
||||||
|
WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
|
// Reset freeform windowing mode that is set per task level (tasks should inherit
|
||||||
|
// container value)
|
||||||
|
wct.merge(mShellTaskOrganizer.prepareClearFreeformForTasks(displayId), true /* transfer */);
|
||||||
|
int targetWindowingMode;
|
||||||
|
if (enabled) {
|
||||||
|
targetWindowingMode = WINDOWING_MODE_FREEFORM;
|
||||||
|
} else {
|
||||||
|
targetWindowingMode = WINDOWING_MODE_FULLSCREEN;
|
||||||
|
// Clear any resized bounds
|
||||||
|
wct.merge(mShellTaskOrganizer.prepareClearBoundsForTasks(displayId),
|
||||||
|
true /* transfer */);
|
||||||
|
}
|
||||||
|
wct.merge(mRootDisplayAreaOrganizer.prepareWindowingModeChange(displayId,
|
||||||
|
targetWindowingMode), true /* transfer */);
|
||||||
|
mRootDisplayAreaOrganizer.applyTransaction(wct);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link ContentObserver} for listening to changes to {@link Settings.System#DESKTOP_MODE}
|
||||||
|
*/
|
||||||
|
private final class SettingsObserver extends ContentObserver {
|
||||||
|
|
||||||
|
private final Uri mDesktopModeSetting = Settings.System.getUriFor(
|
||||||
|
Settings.System.DESKTOP_MODE);
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
|
|
||||||
|
SettingsObserver(Context context, Handler handler) {
|
||||||
|
super(handler);
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void observe() {
|
||||||
|
// TODO(b/242867463): listen for setting change for all users
|
||||||
|
mContext.getContentResolver().registerContentObserver(mDesktopModeSetting,
|
||||||
|
false /* notifyForDescendants */, this /* observer */, UserHandle.USER_CURRENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChange(boolean selfChange, @Nullable Uri uri) {
|
||||||
|
if (mDesktopModeSetting.equals(uri)) {
|
||||||
|
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Received update for desktop mode setting");
|
||||||
|
desktopModeSettingChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void desktopModeSettingChanged() {
|
||||||
|
boolean enabled = isDesktopModeEnabled();
|
||||||
|
updateDesktopModeEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isDesktopModeEnabled() {
|
||||||
|
try {
|
||||||
|
int result = Settings.System.getIntForUser(mContext.getContentResolver(),
|
||||||
|
Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT);
|
||||||
|
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "isDesktopModeEnabled=%s", result);
|
||||||
|
return result != 0;
|
||||||
|
} catch (Settings.SettingNotFoundException e) {
|
||||||
|
ProtoLog.e(WM_SHELL_DESKTOP_MODE, "Failed to read DESKTOP_MODE setting %s", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,6 +46,8 @@ public enum ShellProtoLogGroup implements IProtoLogGroup {
|
|||||||
Consts.TAG_WM_SHELL),
|
Consts.TAG_WM_SHELL),
|
||||||
WM_SHELL_SYSUI_EVENTS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
|
WM_SHELL_SYSUI_EVENTS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
|
||||||
Consts.TAG_WM_SHELL),
|
Consts.TAG_WM_SHELL),
|
||||||
|
WM_SHELL_DESKTOP_MODE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
|
||||||
|
Consts.TAG_WM_SHELL),
|
||||||
TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest");
|
TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest");
|
||||||
|
|
||||||
private final boolean mEnabled;
|
private final boolean mEnabled;
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import com.android.wm.shell.R;
|
|||||||
import com.android.wm.shell.ShellTaskOrganizer;
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
import com.android.wm.shell.common.DisplayController;
|
import com.android.wm.shell.common.DisplayController;
|
||||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||||
|
import com.android.wm.shell.desktopmode.DesktopModeConstants;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
|
* Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
|
||||||
@@ -163,7 +164,13 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
|
|||||||
View caption = mResult.mRootView.findViewById(R.id.caption);
|
View caption = mResult.mRootView.findViewById(R.id.caption);
|
||||||
caption.setOnTouchListener(mOnCaptionTouchListener);
|
caption.setOnTouchListener(mOnCaptionTouchListener);
|
||||||
View maximize = caption.findViewById(R.id.maximize_window);
|
View maximize = caption.findViewById(R.id.maximize_window);
|
||||||
|
if (DesktopModeConstants.IS_FEATURE_ENABLED) {
|
||||||
|
// Hide maximize button when desktop mode is available
|
||||||
|
maximize.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
maximize.setVisibility(View.VISIBLE);
|
||||||
maximize.setOnClickListener(mOnCaptionButtonClickListener);
|
maximize.setOnClickListener(mOnCaptionButtonClickListener);
|
||||||
|
}
|
||||||
View close = caption.findViewById(R.id.close_window);
|
View close = caption.findViewById(R.id.close_window);
|
||||||
close.setOnClickListener(mOnCaptionButtonClickListener);
|
close.setOnClickListener(mOnCaptionButtonClickListener);
|
||||||
View minimize = caption.findViewById(R.id.minimize_window);
|
View minimize = caption.findViewById(R.id.minimize_window);
|
||||||
|
|||||||
@@ -16,9 +16,11 @@
|
|||||||
|
|
||||||
package com.android.wm.shell;
|
package com.android.wm.shell;
|
||||||
|
|
||||||
|
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
|
||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||||
|
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||||
import static android.view.Display.DEFAULT_DISPLAY;
|
import static android.view.Display.DEFAULT_DISPLAY;
|
||||||
|
|
||||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
||||||
@@ -30,6 +32,8 @@ import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIO
|
|||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
import static org.junit.Assume.assumeFalse;
|
import static org.junit.Assume.assumeFalse;
|
||||||
@@ -38,9 +42,11 @@ import static org.mockito.Mockito.clearInvocations;
|
|||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.app.ActivityManager.RunningTaskInfo;
|
import android.app.ActivityManager.RunningTaskInfo;
|
||||||
import android.app.TaskInfo;
|
import android.app.TaskInfo;
|
||||||
|
import android.app.WindowConfiguration;
|
||||||
import android.content.LocusId;
|
import android.content.LocusId;
|
||||||
import android.content.pm.ParceledListSlice;
|
import android.content.pm.ParceledListSlice;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
@@ -53,6 +59,8 @@ import android.window.ITaskOrganizer;
|
|||||||
import android.window.ITaskOrganizerController;
|
import android.window.ITaskOrganizerController;
|
||||||
import android.window.TaskAppearedInfo;
|
import android.window.TaskAppearedInfo;
|
||||||
import android.window.WindowContainerToken;
|
import android.window.WindowContainerToken;
|
||||||
|
import android.window.WindowContainerTransaction;
|
||||||
|
import android.window.WindowContainerTransaction.Change;
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
@@ -628,6 +636,71 @@ public class ShellTaskOrganizerTests extends ShellTestCase {
|
|||||||
verify(mTaskOrganizerController).restartTaskTopActivityProcessIfVisible(task1.token);
|
verify(mTaskOrganizerController).restartTaskTopActivityProcessIfVisible(task1.token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPrepareClearBoundsForTasks() {
|
||||||
|
RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_UNDEFINED);
|
||||||
|
task1.displayId = 1;
|
||||||
|
MockToken token1 = new MockToken();
|
||||||
|
task1.token = token1.token();
|
||||||
|
mOrganizer.onTaskAppeared(task1, null);
|
||||||
|
|
||||||
|
RunningTaskInfo task2 = createTaskInfo(2, WINDOWING_MODE_UNDEFINED);
|
||||||
|
task2.displayId = 1;
|
||||||
|
MockToken token2 = new MockToken();
|
||||||
|
task2.token = token2.token();
|
||||||
|
mOrganizer.onTaskAppeared(task2, null);
|
||||||
|
|
||||||
|
RunningTaskInfo otherDisplayTask = createTaskInfo(3, WINDOWING_MODE_UNDEFINED);
|
||||||
|
otherDisplayTask.displayId = 2;
|
||||||
|
MockToken otherDisplayToken = new MockToken();
|
||||||
|
otherDisplayTask.token = otherDisplayToken.token();
|
||||||
|
mOrganizer.onTaskAppeared(otherDisplayTask, null);
|
||||||
|
|
||||||
|
WindowContainerTransaction wct = mOrganizer.prepareClearBoundsForTasks(1);
|
||||||
|
|
||||||
|
assertEquals(wct.getChanges().size(), 2);
|
||||||
|
Change boundsChange1 = wct.getChanges().get(token1.binder());
|
||||||
|
assertNotNull(boundsChange1);
|
||||||
|
assertNotEquals(
|
||||||
|
(boundsChange1.getWindowSetMask() & WindowConfiguration.WINDOW_CONFIG_BOUNDS), 0);
|
||||||
|
assertTrue(boundsChange1.getConfiguration().windowConfiguration.getBounds().isEmpty());
|
||||||
|
|
||||||
|
Change boundsChange2 = wct.getChanges().get(token2.binder());
|
||||||
|
assertNotNull(boundsChange2);
|
||||||
|
assertNotEquals(
|
||||||
|
(boundsChange2.getWindowSetMask() & WindowConfiguration.WINDOW_CONFIG_BOUNDS), 0);
|
||||||
|
assertTrue(boundsChange2.getConfiguration().windowConfiguration.getBounds().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPrepareClearFreeformForTasks() {
|
||||||
|
RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_FREEFORM);
|
||||||
|
task1.displayId = 1;
|
||||||
|
MockToken token1 = new MockToken();
|
||||||
|
task1.token = token1.token();
|
||||||
|
mOrganizer.onTaskAppeared(task1, null);
|
||||||
|
|
||||||
|
RunningTaskInfo task2 = createTaskInfo(2, WINDOWING_MODE_MULTI_WINDOW);
|
||||||
|
task2.displayId = 1;
|
||||||
|
MockToken token2 = new MockToken();
|
||||||
|
task2.token = token2.token();
|
||||||
|
mOrganizer.onTaskAppeared(task2, null);
|
||||||
|
|
||||||
|
RunningTaskInfo otherDisplayTask = createTaskInfo(3, WINDOWING_MODE_FREEFORM);
|
||||||
|
otherDisplayTask.displayId = 2;
|
||||||
|
MockToken otherDisplayToken = new MockToken();
|
||||||
|
otherDisplayTask.token = otherDisplayToken.token();
|
||||||
|
mOrganizer.onTaskAppeared(otherDisplayTask, null);
|
||||||
|
|
||||||
|
WindowContainerTransaction wct = mOrganizer.prepareClearFreeformForTasks(1);
|
||||||
|
|
||||||
|
// Only task with freeform windowing mode and the right display should be updated
|
||||||
|
assertEquals(wct.getChanges().size(), 1);
|
||||||
|
Change wmModeChange1 = wct.getChanges().get(token1.binder());
|
||||||
|
assertNotNull(wmModeChange1);
|
||||||
|
assertEquals(wmModeChange1.getWindowingMode(), WINDOWING_MODE_UNDEFINED);
|
||||||
|
}
|
||||||
|
|
||||||
private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
|
private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
|
||||||
RunningTaskInfo taskInfo = new RunningTaskInfo();
|
RunningTaskInfo taskInfo = new RunningTaskInfo();
|
||||||
taskInfo.taskId = taskId;
|
taskInfo.taskId = taskId;
|
||||||
@@ -635,4 +708,22 @@ public class ShellTaskOrganizerTests extends ShellTestCase {
|
|||||||
return taskInfo;
|
return taskInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class MockToken {
|
||||||
|
private final WindowContainerToken mToken;
|
||||||
|
private final IBinder mBinder;
|
||||||
|
|
||||||
|
MockToken() {
|
||||||
|
mToken = mock(WindowContainerToken.class);
|
||||||
|
mBinder = mock(IBinder.class);
|
||||||
|
when(mToken.asBinder()).thenReturn(mBinder);
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowContainerToken token() {
|
||||||
|
return mToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
IBinder binder() {
|
||||||
|
return mBinder;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.desktopmode;
|
||||||
|
|
||||||
|
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
|
||||||
|
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||||
|
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.app.WindowConfiguration;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.testing.AndroidTestingRunner;
|
||||||
|
import android.window.WindowContainerToken;
|
||||||
|
import android.window.WindowContainerTransaction;
|
||||||
|
import android.window.WindowContainerTransaction.Change;
|
||||||
|
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
|
import com.android.wm.shell.RootDisplayAreaOrganizer;
|
||||||
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
|
import com.android.wm.shell.ShellTestCase;
|
||||||
|
import com.android.wm.shell.common.ShellExecutor;
|
||||||
|
import com.android.wm.shell.sysui.ShellInit;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner.class)
|
||||||
|
public class DesktopModeControllerTest extends ShellTestCase {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ShellTaskOrganizer mShellTaskOrganizer;
|
||||||
|
@Mock
|
||||||
|
private RootDisplayAreaOrganizer mRootDisplayAreaOrganizer;
|
||||||
|
@Mock
|
||||||
|
private ShellExecutor mTestExecutor;
|
||||||
|
@Mock
|
||||||
|
private Handler mMockHandler;
|
||||||
|
|
||||||
|
private DesktopModeController mController;
|
||||||
|
private ShellInit mShellInit;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mShellInit = Mockito.spy(new ShellInit(mTestExecutor));
|
||||||
|
|
||||||
|
mController = new DesktopModeController(mContext, mShellInit, mShellTaskOrganizer,
|
||||||
|
mRootDisplayAreaOrganizer, mMockHandler);
|
||||||
|
|
||||||
|
mShellInit.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void instantiate_addInitCallback() {
|
||||||
|
verify(mShellInit, times(1)).addInitCallback(any(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDesktopModeEnabled_taskWmClearedDisplaySetToFreeform() {
|
||||||
|
// Create a fake WCT to simulate setting task windowing mode to undefined
|
||||||
|
WindowContainerTransaction taskWct = new WindowContainerTransaction();
|
||||||
|
MockToken taskMockToken = new MockToken();
|
||||||
|
taskWct.setWindowingMode(taskMockToken.token(), WINDOWING_MODE_UNDEFINED);
|
||||||
|
when(mShellTaskOrganizer.prepareClearFreeformForTasks(mContext.getDisplayId())).thenReturn(
|
||||||
|
taskWct);
|
||||||
|
|
||||||
|
// Create a fake WCT to simulate setting display windowing mode to freeform
|
||||||
|
WindowContainerTransaction displayWct = new WindowContainerTransaction();
|
||||||
|
MockToken displayMockToken = new MockToken();
|
||||||
|
displayWct.setWindowingMode(displayMockToken.token(), WINDOWING_MODE_FREEFORM);
|
||||||
|
when(mRootDisplayAreaOrganizer.prepareWindowingModeChange(mContext.getDisplayId(),
|
||||||
|
WINDOWING_MODE_FREEFORM)).thenReturn(displayWct);
|
||||||
|
|
||||||
|
// The test
|
||||||
|
mController.updateDesktopModeEnabled(true);
|
||||||
|
|
||||||
|
ArgumentCaptor<WindowContainerTransaction> arg = ArgumentCaptor.forClass(
|
||||||
|
WindowContainerTransaction.class);
|
||||||
|
verify(mRootDisplayAreaOrganizer).applyTransaction(arg.capture());
|
||||||
|
|
||||||
|
// WCT should have 2 changes - clear task wm mode and set display wm mode
|
||||||
|
WindowContainerTransaction wct = arg.getValue();
|
||||||
|
assertThat(wct.getChanges()).hasSize(2);
|
||||||
|
|
||||||
|
// Verify executed WCT has a change for setting task windowing mode to undefined
|
||||||
|
Change taskWmModeChange = wct.getChanges().get(taskMockToken.binder());
|
||||||
|
assertThat(taskWmModeChange).isNotNull();
|
||||||
|
assertThat(taskWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED);
|
||||||
|
|
||||||
|
// Verify executed WCT has a change for setting display windowing mode to freeform
|
||||||
|
Change displayWmModeChange = wct.getChanges().get(displayMockToken.binder());
|
||||||
|
assertThat(displayWmModeChange).isNotNull();
|
||||||
|
assertThat(displayWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_FREEFORM);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDesktopModeDisabled_taskWmAndBoundsClearedDisplaySetToFullscreen() {
|
||||||
|
// Create a fake WCT to simulate setting task windowing mode to undefined
|
||||||
|
WindowContainerTransaction taskWmWct = new WindowContainerTransaction();
|
||||||
|
MockToken taskWmMockToken = new MockToken();
|
||||||
|
taskWmWct.setWindowingMode(taskWmMockToken.token(), WINDOWING_MODE_UNDEFINED);
|
||||||
|
when(mShellTaskOrganizer.prepareClearFreeformForTasks(mContext.getDisplayId())).thenReturn(
|
||||||
|
taskWmWct);
|
||||||
|
|
||||||
|
// Create a fake WCT to simulate clearing task bounds
|
||||||
|
WindowContainerTransaction taskBoundsWct = new WindowContainerTransaction();
|
||||||
|
MockToken taskBoundsMockToken = new MockToken();
|
||||||
|
taskBoundsWct.setBounds(taskBoundsMockToken.token(), null);
|
||||||
|
when(mShellTaskOrganizer.prepareClearBoundsForTasks(mContext.getDisplayId())).thenReturn(
|
||||||
|
taskBoundsWct);
|
||||||
|
|
||||||
|
// Create a fake WCT to simulate setting display windowing mode to fullscreen
|
||||||
|
WindowContainerTransaction displayWct = new WindowContainerTransaction();
|
||||||
|
MockToken displayMockToken = new MockToken();
|
||||||
|
displayWct.setWindowingMode(displayMockToken.token(), WINDOWING_MODE_FULLSCREEN);
|
||||||
|
when(mRootDisplayAreaOrganizer.prepareWindowingModeChange(mContext.getDisplayId(),
|
||||||
|
WINDOWING_MODE_FULLSCREEN)).thenReturn(displayWct);
|
||||||
|
|
||||||
|
// The test
|
||||||
|
mController.updateDesktopModeEnabled(false);
|
||||||
|
|
||||||
|
ArgumentCaptor<WindowContainerTransaction> arg = ArgumentCaptor.forClass(
|
||||||
|
WindowContainerTransaction.class);
|
||||||
|
verify(mRootDisplayAreaOrganizer).applyTransaction(arg.capture());
|
||||||
|
|
||||||
|
// WCT should have 3 changes - clear task wm mode and bounds and set display wm mode
|
||||||
|
WindowContainerTransaction wct = arg.getValue();
|
||||||
|
assertThat(wct.getChanges()).hasSize(3);
|
||||||
|
|
||||||
|
// Verify executed WCT has a change for setting task windowing mode to undefined
|
||||||
|
Change taskWmModeChange = wct.getChanges().get(taskWmMockToken.binder());
|
||||||
|
assertThat(taskWmModeChange).isNotNull();
|
||||||
|
assertThat(taskWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED);
|
||||||
|
|
||||||
|
// Verify executed WCT has a change for clearing task bounds
|
||||||
|
Change taskBoundsChange = wct.getChanges().get(taskBoundsMockToken.binder());
|
||||||
|
assertThat(taskBoundsChange).isNotNull();
|
||||||
|
assertThat(taskBoundsChange.getWindowSetMask()
|
||||||
|
& WindowConfiguration.WINDOW_CONFIG_BOUNDS).isNotEqualTo(0);
|
||||||
|
assertThat(taskBoundsChange.getConfiguration().windowConfiguration.getBounds().isEmpty())
|
||||||
|
.isTrue();
|
||||||
|
|
||||||
|
// Verify executed WCT has a change for setting display windowing mode to fullscreen
|
||||||
|
Change displayWmModeChange = wct.getChanges().get(displayMockToken.binder());
|
||||||
|
assertThat(displayWmModeChange).isNotNull();
|
||||||
|
assertThat(displayWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_FULLSCREEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MockToken {
|
||||||
|
private final WindowContainerToken mToken;
|
||||||
|
private final IBinder mBinder;
|
||||||
|
|
||||||
|
MockToken() {
|
||||||
|
mToken = mock(WindowContainerToken.class);
|
||||||
|
mBinder = mock(IBinder.class);
|
||||||
|
when(mToken.asBinder()).thenReturn(mBinder);
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowContainerToken token() {
|
||||||
|
return mToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
IBinder binder() {
|
||||||
|
return mBinder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user