Merge "Add support to show apps on desktop in sysui proxy" into tm-qpr-dev
This commit is contained in:
@@ -57,6 +57,8 @@ import com.android.wm.shell.common.annotations.ShellMainThread;
|
|||||||
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
|
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
|
||||||
import com.android.wm.shell.compatui.CompatUIController;
|
import com.android.wm.shell.compatui.CompatUIController;
|
||||||
import com.android.wm.shell.desktopmode.DesktopMode;
|
import com.android.wm.shell.desktopmode.DesktopMode;
|
||||||
|
import com.android.wm.shell.desktopmode.DesktopModeController;
|
||||||
|
import com.android.wm.shell.desktopmode.DesktopModeStatus;
|
||||||
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
|
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
|
||||||
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
|
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
|
||||||
import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
|
import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
|
||||||
@@ -716,15 +718,36 @@ public abstract class WMShellBaseModule {
|
|||||||
// Desktop mode (optional feature)
|
// Desktop mode (optional feature)
|
||||||
//
|
//
|
||||||
|
|
||||||
|
@WMSingleton
|
||||||
|
@Provides
|
||||||
|
static Optional<DesktopMode> provideDesktopMode(
|
||||||
|
Optional<DesktopModeController> desktopModeController) {
|
||||||
|
return desktopModeController.map(DesktopModeController::asDesktopMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@BindsOptionalOf
|
||||||
|
@DynamicOverride
|
||||||
|
abstract DesktopModeController optionalDesktopModeController();
|
||||||
|
|
||||||
|
@WMSingleton
|
||||||
|
@Provides
|
||||||
|
static Optional<DesktopModeController> providesDesktopModeController(
|
||||||
|
@DynamicOverride Optional<DesktopModeController> desktopModeController) {
|
||||||
|
if (DesktopModeStatus.IS_SUPPORTED) {
|
||||||
|
return desktopModeController;
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
@BindsOptionalOf
|
@BindsOptionalOf
|
||||||
@DynamicOverride
|
@DynamicOverride
|
||||||
abstract DesktopModeTaskRepository optionalDesktopModeTaskRepository();
|
abstract DesktopModeTaskRepository optionalDesktopModeTaskRepository();
|
||||||
|
|
||||||
@WMSingleton
|
@WMSingleton
|
||||||
@Provides
|
@Provides
|
||||||
static Optional<DesktopModeTaskRepository> providesDesktopModeTaskRepository(
|
static Optional<DesktopModeTaskRepository> providesDesktopTaskRepository(
|
||||||
@DynamicOverride Optional<DesktopModeTaskRepository> desktopModeTaskRepository) {
|
@DynamicOverride Optional<DesktopModeTaskRepository> desktopModeTaskRepository) {
|
||||||
if (DesktopMode.IS_SUPPORTED) {
|
if (DesktopModeStatus.IS_SUPPORTED) {
|
||||||
return desktopModeTaskRepository;
|
return desktopModeTaskRepository;
|
||||||
}
|
}
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ 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.DesktopMode;
|
|
||||||
import com.android.wm.shell.desktopmode.DesktopModeController;
|
import com.android.wm.shell.desktopmode.DesktopModeController;
|
||||||
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
|
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
|
||||||
import com.android.wm.shell.draganddrop.DragAndDropController;
|
import com.android.wm.shell.draganddrop.DragAndDropController;
|
||||||
@@ -595,19 +594,18 @@ public abstract class WMShellModule {
|
|||||||
|
|
||||||
@WMSingleton
|
@WMSingleton
|
||||||
@Provides
|
@Provides
|
||||||
static Optional<DesktopModeController> provideDesktopModeController(
|
@DynamicOverride
|
||||||
Context context, ShellInit shellInit,
|
static DesktopModeController provideDesktopModeController(Context context, ShellInit shellInit,
|
||||||
ShellTaskOrganizer shellTaskOrganizer,
|
ShellTaskOrganizer shellTaskOrganizer,
|
||||||
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
|
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
|
||||||
|
Transitions transitions,
|
||||||
|
@DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
|
||||||
@ShellMainThread Handler mainHandler,
|
@ShellMainThread Handler mainHandler,
|
||||||
Transitions transitions
|
@ShellMainThread ShellExecutor mainExecutor
|
||||||
) {
|
) {
|
||||||
if (DesktopMode.IS_SUPPORTED) {
|
return new DesktopModeController(context, shellInit, shellTaskOrganizer,
|
||||||
return Optional.of(new DesktopModeController(context, shellInit, shellTaskOrganizer,
|
rootTaskDisplayAreaOrganizer, transitions, desktopModeTaskRepository, mainHandler,
|
||||||
rootTaskDisplayAreaOrganizer, mainHandler, transitions));
|
mainExecutor);
|
||||||
} else {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@WMSingleton
|
@WMSingleton
|
||||||
|
|||||||
@@ -16,43 +16,16 @@
|
|||||||
|
|
||||||
package com.android.wm.shell.desktopmode;
|
package com.android.wm.shell.desktopmode;
|
||||||
|
|
||||||
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
|
import com.android.wm.shell.common.annotations.ExternalThread;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.SystemProperties;
|
|
||||||
import android.os.UserHandle;
|
|
||||||
import android.provider.Settings;
|
|
||||||
|
|
||||||
import com.android.internal.protolog.common.ProtoLog;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constants for desktop mode feature
|
* Interface to interact with desktop mode feature in shell.
|
||||||
*/
|
*/
|
||||||
public class DesktopMode {
|
@ExternalThread
|
||||||
|
public interface DesktopMode {
|
||||||
|
|
||||||
/**
|
/** Returns a binder that can be passed to an external process to manipulate DesktopMode. */
|
||||||
* Flag to indicate whether desktop mode is available on the device
|
default IDesktopMode createExternalInterface() {
|
||||||
*/
|
return null;
|
||||||
public static final boolean IS_SUPPORTED = SystemProperties.getBoolean(
|
|
||||||
"persist.wm.debug.desktop_mode", false);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if desktop mode is active
|
|
||||||
*
|
|
||||||
* @return {@code true} if active
|
|
||||||
*/
|
|
||||||
public static boolean isActive(Context context) {
|
|
||||||
if (!IS_SUPPORTED) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
int result = Settings.System.getIntForUser(context.getContentResolver(),
|
|
||||||
Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT);
|
|
||||||
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "isDesktopModeEnabled=%s", result);
|
|
||||||
return result != 0;
|
|
||||||
} catch (Exception e) {
|
|
||||||
ProtoLog.e(WM_SHELL_DESKTOP_MODE, "Failed to read DESKTOP_MODE setting %s", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,10 @@ 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.view.WindowManager.TRANSIT_CHANGE;
|
import static android.view.WindowManager.TRANSIT_CHANGE;
|
||||||
|
|
||||||
|
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
|
||||||
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
|
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
|
||||||
|
|
||||||
|
import android.app.ActivityManager.RunningTaskInfo;
|
||||||
import android.app.WindowConfiguration;
|
import android.app.WindowConfiguration;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.ContentObserver;
|
import android.database.ContentObserver;
|
||||||
@@ -29,51 +31,83 @@ import android.net.Uri;
|
|||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
import android.util.ArraySet;
|
||||||
import android.window.DisplayAreaInfo;
|
import android.window.DisplayAreaInfo;
|
||||||
import android.window.WindowContainerTransaction;
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
|
import androidx.annotation.BinderThread;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
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;
|
||||||
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.common.RemoteCallable;
|
||||||
|
import com.android.wm.shell.common.ShellExecutor;
|
||||||
|
import com.android.wm.shell.common.annotations.ExternalThread;
|
||||||
import com.android.wm.shell.common.annotations.ShellMainThread;
|
import com.android.wm.shell.common.annotations.ShellMainThread;
|
||||||
import com.android.wm.shell.sysui.ShellInit;
|
import com.android.wm.shell.sysui.ShellInit;
|
||||||
import com.android.wm.shell.transition.Transitions;
|
import com.android.wm.shell.transition.Transitions;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles windowing changes when desktop mode system setting changes
|
* Handles windowing changes when desktop mode system setting changes
|
||||||
*/
|
*/
|
||||||
public class DesktopModeController {
|
public class DesktopModeController implements RemoteCallable<DesktopModeController> {
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final ShellTaskOrganizer mShellTaskOrganizer;
|
private final ShellTaskOrganizer mShellTaskOrganizer;
|
||||||
private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
|
private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
|
||||||
private final SettingsObserver mSettingsObserver;
|
|
||||||
private final Transitions mTransitions;
|
private final Transitions mTransitions;
|
||||||
|
private final DesktopModeTaskRepository mDesktopModeTaskRepository;
|
||||||
|
private final ShellExecutor mMainExecutor;
|
||||||
|
private final DesktopMode mDesktopModeImpl = new DesktopModeImpl();
|
||||||
|
private final SettingsObserver mSettingsObserver;
|
||||||
|
|
||||||
public DesktopModeController(Context context, ShellInit shellInit,
|
public DesktopModeController(Context context, ShellInit shellInit,
|
||||||
ShellTaskOrganizer shellTaskOrganizer,
|
ShellTaskOrganizer shellTaskOrganizer,
|
||||||
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
|
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
|
||||||
|
Transitions transitions,
|
||||||
|
DesktopModeTaskRepository desktopModeTaskRepository,
|
||||||
@ShellMainThread Handler mainHandler,
|
@ShellMainThread Handler mainHandler,
|
||||||
Transitions transitions) {
|
@ShellMainThread ShellExecutor mainExecutor) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mShellTaskOrganizer = shellTaskOrganizer;
|
mShellTaskOrganizer = shellTaskOrganizer;
|
||||||
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
|
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
|
||||||
mSettingsObserver = new SettingsObserver(mContext, mainHandler);
|
|
||||||
mTransitions = transitions;
|
mTransitions = transitions;
|
||||||
|
mDesktopModeTaskRepository = desktopModeTaskRepository;
|
||||||
|
mMainExecutor = mainExecutor;
|
||||||
|
mSettingsObserver = new SettingsObserver(mContext, mainHandler);
|
||||||
shellInit.addInitCallback(this::onInit, this);
|
shellInit.addInitCallback(this::onInit, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onInit() {
|
private void onInit() {
|
||||||
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopModeController");
|
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopModeController");
|
||||||
mSettingsObserver.observe();
|
mSettingsObserver.observe();
|
||||||
if (DesktopMode.isActive(mContext)) {
|
if (DesktopModeStatus.isActive(mContext)) {
|
||||||
updateDesktopModeActive(true);
|
updateDesktopModeActive(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Context getContext() {
|
||||||
|
return mContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ShellExecutor getRemoteCallExecutor() {
|
||||||
|
return mMainExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get connection interface between sysui and shell
|
||||||
|
*/
|
||||||
|
public DesktopMode asDesktopMode() {
|
||||||
|
return mDesktopModeImpl;
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void updateDesktopModeActive(boolean active) {
|
void updateDesktopModeActive(boolean active) {
|
||||||
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "updateDesktopModeActive: active=%s", active);
|
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "updateDesktopModeActive: active=%s", active);
|
||||||
@@ -120,6 +154,28 @@ public class DesktopModeController {
|
|||||||
wct.setWindowingMode(displayAreaInfo.token, windowingMode);
|
wct.setWindowingMode(displayAreaInfo.token, windowingMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show apps on desktop
|
||||||
|
*/
|
||||||
|
public void showDesktopApps() {
|
||||||
|
ArraySet<Integer> activeTasks = mDesktopModeTaskRepository.getActiveTasks();
|
||||||
|
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront: tasks=%s", activeTasks.size());
|
||||||
|
ArrayList<RunningTaskInfo> taskInfos = new ArrayList<>();
|
||||||
|
for (Integer taskId : activeTasks) {
|
||||||
|
RunningTaskInfo taskInfo = mShellTaskOrganizer.getRunningTaskInfo(taskId);
|
||||||
|
if (taskInfo != null) {
|
||||||
|
taskInfos.add(taskInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Order by lastActiveTime, descending
|
||||||
|
taskInfos.sort(Comparator.comparingLong(task -> -task.lastActiveTime));
|
||||||
|
WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
|
for (RunningTaskInfo task : taskInfos) {
|
||||||
|
wct.reorder(task.token, true);
|
||||||
|
}
|
||||||
|
mShellTaskOrganizer.applyTransaction(wct);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link ContentObserver} for listening to changes to {@link Settings.System#DESKTOP_MODE}
|
* A {@link ContentObserver} for listening to changes to {@link Settings.System#DESKTOP_MODE}
|
||||||
*/
|
*/
|
||||||
@@ -150,8 +206,51 @@ public class DesktopModeController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void desktopModeSettingChanged() {
|
private void desktopModeSettingChanged() {
|
||||||
boolean enabled = DesktopMode.isActive(mContext);
|
boolean enabled = DesktopModeStatus.isActive(mContext);
|
||||||
updateDesktopModeActive(enabled);
|
updateDesktopModeActive(enabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interface for calls from outside the shell, within the host process.
|
||||||
|
*/
|
||||||
|
@ExternalThread
|
||||||
|
private final class DesktopModeImpl implements DesktopMode {
|
||||||
|
|
||||||
|
private IDesktopModeImpl mIDesktopMode;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IDesktopMode createExternalInterface() {
|
||||||
|
if (mIDesktopMode != null) {
|
||||||
|
mIDesktopMode.invalidate();
|
||||||
|
}
|
||||||
|
mIDesktopMode = new IDesktopModeImpl(DesktopModeController.this);
|
||||||
|
return mIDesktopMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interface for calls from outside the host process.
|
||||||
|
*/
|
||||||
|
@BinderThread
|
||||||
|
private static class IDesktopModeImpl extends IDesktopMode.Stub {
|
||||||
|
|
||||||
|
private DesktopModeController mController;
|
||||||
|
|
||||||
|
IDesktopModeImpl(DesktopModeController controller) {
|
||||||
|
mController = controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates this instance, preventing future calls from updating the controller.
|
||||||
|
*/
|
||||||
|
void invalidate() {
|
||||||
|
mController = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDesktopApps() {
|
||||||
|
executeRemoteCallWithTaskPermission(mController, "showDesktopApps",
|
||||||
|
DesktopModeController::showDesktopApps);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.SystemProperties;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
|
import com.android.internal.protolog.common.ProtoLog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constants for desktop mode feature
|
||||||
|
*/
|
||||||
|
public class DesktopModeStatus {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag to indicate whether desktop mode is available on the device
|
||||||
|
*/
|
||||||
|
public static final boolean IS_SUPPORTED = SystemProperties.getBoolean(
|
||||||
|
"persist.wm.debug.desktop_mode", false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if desktop mode is active
|
||||||
|
*
|
||||||
|
* @return {@code true} if active
|
||||||
|
*/
|
||||||
|
public static boolean isActive(Context context) {
|
||||||
|
if (!IS_SUPPORTED) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
int result = Settings.System.getIntForUser(context.getContentResolver(),
|
||||||
|
Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT);
|
||||||
|
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "isDesktopModeEnabled=%s", result);
|
||||||
|
return result != 0;
|
||||||
|
} catch (Exception e) {
|
||||||
|
ProtoLog.e(WM_SHELL_DESKTOP_MODE, "Failed to read DESKTOP_MODE setting %s", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface that is exposed to remote callers to manipulate desktop mode features.
|
||||||
|
*/
|
||||||
|
interface IDesktopMode {
|
||||||
|
|
||||||
|
/** Show apps on the desktop */
|
||||||
|
void showDesktopApps();
|
||||||
|
}
|
||||||
@@ -28,7 +28,7 @@ import androidx.annotation.Nullable;
|
|||||||
|
|
||||||
import com.android.internal.protolog.common.ProtoLog;
|
import com.android.internal.protolog.common.ProtoLog;
|
||||||
import com.android.wm.shell.ShellTaskOrganizer;
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
import com.android.wm.shell.desktopmode.DesktopMode;
|
import com.android.wm.shell.desktopmode.DesktopModeStatus;
|
||||||
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
|
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
|
||||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||||
import com.android.wm.shell.sysui.ShellInit;
|
import com.android.wm.shell.sysui.ShellInit;
|
||||||
@@ -90,7 +90,7 @@ public class FreeformTaskListener<T extends AutoCloseable>
|
|||||||
t.apply();
|
t.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DesktopMode.IS_SUPPORTED && taskInfo.isVisible) {
|
if (DesktopModeStatus.IS_SUPPORTED && taskInfo.isVisible) {
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
|
||||||
"Adding active freeform task: #%d", taskInfo.taskId);
|
"Adding active freeform task: #%d", taskInfo.taskId);
|
||||||
mDesktopModeTaskRepository.ifPresent(it -> it.addActiveTask(taskInfo.taskId));
|
mDesktopModeTaskRepository.ifPresent(it -> it.addActiveTask(taskInfo.taskId));
|
||||||
@@ -123,7 +123,7 @@ public class FreeformTaskListener<T extends AutoCloseable>
|
|||||||
taskInfo.taskId);
|
taskInfo.taskId);
|
||||||
mTasks.remove(taskInfo.taskId);
|
mTasks.remove(taskInfo.taskId);
|
||||||
|
|
||||||
if (DesktopMode.IS_SUPPORTED) {
|
if (DesktopModeStatus.IS_SUPPORTED) {
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
|
||||||
"Removing active freeform task: #%d", taskInfo.taskId);
|
"Removing active freeform task: #%d", taskInfo.taskId);
|
||||||
mDesktopModeTaskRepository.ifPresent(it -> it.removeActiveTask(taskInfo.taskId));
|
mDesktopModeTaskRepository.ifPresent(it -> it.removeActiveTask(taskInfo.taskId));
|
||||||
@@ -150,7 +150,7 @@ public class FreeformTaskListener<T extends AutoCloseable>
|
|||||||
mWindowDecorationViewModel.onTaskInfoChanged(state.mTaskInfo, state.mWindowDecoration);
|
mWindowDecorationViewModel.onTaskInfoChanged(state.mTaskInfo, state.mWindowDecoration);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DesktopMode.IS_SUPPORTED) {
|
if (DesktopModeStatus.IS_SUPPORTED) {
|
||||||
if (taskInfo.isVisible) {
|
if (taskInfo.isVisible) {
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
|
||||||
"Adding active freeform task: #%d", taskInfo.taskId);
|
"Adding active freeform task: #%d", taskInfo.taskId);
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ import com.android.wm.shell.common.TaskStackListenerCallback;
|
|||||||
import com.android.wm.shell.common.TaskStackListenerImpl;
|
import com.android.wm.shell.common.TaskStackListenerImpl;
|
||||||
import com.android.wm.shell.common.annotations.ExternalThread;
|
import com.android.wm.shell.common.annotations.ExternalThread;
|
||||||
import com.android.wm.shell.common.annotations.ShellMainThread;
|
import com.android.wm.shell.common.annotations.ShellMainThread;
|
||||||
import com.android.wm.shell.desktopmode.DesktopMode;
|
import com.android.wm.shell.desktopmode.DesktopModeStatus;
|
||||||
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
|
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
|
||||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||||
import com.android.wm.shell.sysui.ShellCommandHandler;
|
import com.android.wm.shell.sysui.ShellCommandHandler;
|
||||||
@@ -287,7 +287,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
|
|||||||
rawMapping.put(taskInfo.taskId, taskInfo);
|
rawMapping.put(taskInfo.taskId, taskInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean desktopModeActive = DesktopMode.isActive(mContext);
|
boolean desktopModeActive = DesktopModeStatus.isActive(mContext);
|
||||||
ArrayList<ActivityManager.RecentTaskInfo> freeformTasks = new ArrayList<>();
|
ArrayList<ActivityManager.RecentTaskInfo> freeformTasks = new ArrayList<>();
|
||||||
|
|
||||||
// Pull out the pairs as we iterate back in the list
|
// Pull out the pairs as we iterate back in the list
|
||||||
@@ -320,7 +320,6 @@ public class RecentTasksController implements TaskStackListenerCallback,
|
|||||||
|
|
||||||
// Add a special entry for freeform tasks
|
// Add a special entry for freeform tasks
|
||||||
if (!freeformTasks.isEmpty()) {
|
if (!freeformTasks.isEmpty()) {
|
||||||
// First task is added separately
|
|
||||||
recentTasks.add(0, GroupedRecentTaskInfo.forFreeformTasks(
|
recentTasks.add(0, GroupedRecentTaskInfo.forFreeformTasks(
|
||||||
freeformTasks.toArray(new ActivityManager.RecentTaskInfo[0])));
|
freeformTasks.toArray(new ActivityManager.RecentTaskInfo[0])));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,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.DesktopMode;
|
import com.android.wm.shell.desktopmode.DesktopModeStatus;
|
||||||
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
|
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
|
||||||
import com.android.wm.shell.transition.Transitions;
|
import com.android.wm.shell.transition.Transitions;
|
||||||
|
|
||||||
@@ -239,7 +239,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption
|
|||||||
|
|
||||||
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
|
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
|
||||||
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
|
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
|
||||||
return DesktopMode.IS_SUPPORTED
|
return DesktopModeStatus.IS_SUPPORTED
|
||||||
&& mDisplayController.getDisplayContext(taskInfo.displayId)
|
&& mDisplayController.getDisplayContext(taskInfo.displayId)
|
||||||
.getResources().getConfiguration().smallestScreenWidthDp >= 600;
|
.getResources().getConfiguration().smallestScreenWidthDp >= 600;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +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.DesktopMode;
|
import com.android.wm.shell.desktopmode.DesktopModeStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
@@ -164,7 +164,7 @@ 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 (DesktopMode.IS_SUPPORTED) {
|
if (DesktopModeStatus.IS_SUPPORTED) {
|
||||||
// Hide maximize button when desktop mode is available
|
// Hide maximize button when desktop mode is available
|
||||||
maximize.setVisibility(View.GONE);
|
maximize.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ public final class TestRunningTaskInfoBuilder {
|
|||||||
private ActivityManager.TaskDescription.Builder mTaskDescriptionBuilder = null;
|
private ActivityManager.TaskDescription.Builder mTaskDescriptionBuilder = null;
|
||||||
private final Point mPositionInParent = new Point();
|
private final Point mPositionInParent = new Point();
|
||||||
private boolean mIsVisible = false;
|
private boolean mIsVisible = false;
|
||||||
|
private long mLastActiveTime;
|
||||||
|
|
||||||
public static WindowContainerToken createMockWCToken() {
|
public static WindowContainerToken createMockWCToken() {
|
||||||
final IWindowContainerToken itoken = mock(IWindowContainerToken.class);
|
final IWindowContainerToken itoken = mock(IWindowContainerToken.class);
|
||||||
@@ -52,6 +53,11 @@ public final class TestRunningTaskInfoBuilder {
|
|||||||
return new WindowContainerToken(itoken);
|
return new WindowContainerToken(itoken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TestRunningTaskInfoBuilder setToken(WindowContainerToken token) {
|
||||||
|
mToken = token;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public TestRunningTaskInfoBuilder setBounds(Rect bounds) {
|
public TestRunningTaskInfoBuilder setBounds(Rect bounds) {
|
||||||
mBounds.set(bounds);
|
mBounds.set(bounds);
|
||||||
return this;
|
return this;
|
||||||
@@ -95,6 +101,11 @@ public final class TestRunningTaskInfoBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TestRunningTaskInfoBuilder setLastActiveTime(long lastActiveTime) {
|
||||||
|
mLastActiveTime = lastActiveTime;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public ActivityManager.RunningTaskInfo build() {
|
public ActivityManager.RunningTaskInfo build() {
|
||||||
final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
|
final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
|
||||||
info.taskId = sNextTaskId++;
|
info.taskId = sNextTaskId++;
|
||||||
@@ -110,6 +121,7 @@ public final class TestRunningTaskInfoBuilder {
|
|||||||
mTaskDescriptionBuilder != null ? mTaskDescriptionBuilder.build() : null;
|
mTaskDescriptionBuilder != null ? mTaskDescriptionBuilder.build() : null;
|
||||||
info.positionInParent = mPositionInParent;
|
info.positionInParent = mPositionInParent;
|
||||||
info.isVisible = mIsVisible;
|
info.isVisible = mIsVisible;
|
||||||
|
info.lastActiveTime = mLastActiveTime;
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,16 +19,22 @@ package com.android.wm.shell.desktopmode;
|
|||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
|
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_UNDEFINED;
|
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||||
|
import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS;
|
||||||
|
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
|
||||||
|
|
||||||
|
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
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 static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.app.WindowConfiguration;
|
import android.app.ActivityManager;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.testing.AndroidTestingRunner;
|
import android.testing.AndroidTestingRunner;
|
||||||
@@ -39,13 +45,17 @@ import android.window.WindowContainerTransaction.Change;
|
|||||||
|
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
|
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
|
||||||
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.ShellTestCase;
|
import com.android.wm.shell.ShellTestCase;
|
||||||
|
import com.android.wm.shell.TestRunningTaskInfoBuilder;
|
||||||
|
import com.android.wm.shell.TestShellExecutor;
|
||||||
import com.android.wm.shell.common.ShellExecutor;
|
import com.android.wm.shell.common.ShellExecutor;
|
||||||
import com.android.wm.shell.sysui.ShellInit;
|
import com.android.wm.shell.sysui.ShellInit;
|
||||||
import com.android.wm.shell.transition.Transitions;
|
import com.android.wm.shell.transition.Transitions;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@@ -67,18 +77,38 @@ public class DesktopModeControllerTest extends ShellTestCase {
|
|||||||
private Handler mMockHandler;
|
private Handler mMockHandler;
|
||||||
@Mock
|
@Mock
|
||||||
private Transitions mMockTransitions;
|
private Transitions mMockTransitions;
|
||||||
|
private TestShellExecutor mExecutor;
|
||||||
|
|
||||||
private DesktopModeController mController;
|
private DesktopModeController mController;
|
||||||
|
private DesktopModeTaskRepository mDesktopModeTaskRepository;
|
||||||
private ShellInit mShellInit;
|
private ShellInit mShellInit;
|
||||||
|
private StaticMockitoSession mMockitoSession;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
|
mMockitoSession = mockitoSession().mockStatic(DesktopModeStatus.class).startMocking();
|
||||||
|
when(DesktopModeStatus.isActive(any())).thenReturn(true);
|
||||||
|
|
||||||
mShellInit = Mockito.spy(new ShellInit(mTestExecutor));
|
mShellInit = Mockito.spy(new ShellInit(mTestExecutor));
|
||||||
|
mExecutor = new TestShellExecutor();
|
||||||
|
|
||||||
|
mDesktopModeTaskRepository = new DesktopModeTaskRepository();
|
||||||
|
|
||||||
mController = new DesktopModeController(mContext, mShellInit, mShellTaskOrganizer,
|
mController = new DesktopModeController(mContext, mShellInit, mShellTaskOrganizer,
|
||||||
mRootTaskDisplayAreaOrganizer, mMockHandler, mMockTransitions);
|
mRootTaskDisplayAreaOrganizer, mMockTransitions,
|
||||||
|
mDesktopModeTaskRepository, mMockHandler, mExecutor);
|
||||||
|
|
||||||
|
when(mShellTaskOrganizer.prepareClearFreeformForStandardTasks(anyInt())).thenReturn(
|
||||||
|
new WindowContainerTransaction());
|
||||||
|
|
||||||
mShellInit.init();
|
mShellInit.init();
|
||||||
|
clearInvocations(mShellTaskOrganizer);
|
||||||
|
clearInvocations(mRootTaskDisplayAreaOrganizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
mMockitoSession.finishMocking();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -159,17 +189,15 @@ public class DesktopModeControllerTest extends ShellTestCase {
|
|||||||
assertThat(wct.getChanges()).hasSize(3);
|
assertThat(wct.getChanges()).hasSize(3);
|
||||||
|
|
||||||
// Verify executed WCT has a change for setting task windowing mode to undefined
|
// Verify executed WCT has a change for setting task windowing mode to undefined
|
||||||
Change taskWmModeChange = wct.getChanges().get(taskWmMockToken.binder());
|
Change taskWmMode = wct.getChanges().get(taskWmMockToken.binder());
|
||||||
assertThat(taskWmModeChange).isNotNull();
|
assertThat(taskWmMode).isNotNull();
|
||||||
assertThat(taskWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED);
|
assertThat(taskWmMode.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED);
|
||||||
|
|
||||||
// Verify executed WCT has a change for clearing task bounds
|
// Verify executed WCT has a change for clearing task bounds
|
||||||
Change taskBoundsChange = wct.getChanges().get(taskBoundsMockToken.binder());
|
Change bounds = wct.getChanges().get(taskBoundsMockToken.binder());
|
||||||
assertThat(taskBoundsChange).isNotNull();
|
assertThat(bounds).isNotNull();
|
||||||
assertThat(taskBoundsChange.getWindowSetMask()
|
assertThat(bounds.getWindowSetMask() & WINDOW_CONFIG_BOUNDS).isNotEqualTo(0);
|
||||||
& WindowConfiguration.WINDOW_CONFIG_BOUNDS).isNotEqualTo(0);
|
assertThat(bounds.getConfiguration().windowConfiguration.getBounds().isEmpty()).isTrue();
|
||||||
assertThat(taskBoundsChange.getConfiguration().windowConfiguration.getBounds().isEmpty())
|
|
||||||
.isTrue();
|
|
||||||
|
|
||||||
// Verify executed WCT has a change for setting display windowing mode to fullscreen
|
// Verify executed WCT has a change for setting display windowing mode to fullscreen
|
||||||
Change displayWmModeChange = wct.getChanges().get(displayAreaInfo.token.asBinder());
|
Change displayWmModeChange = wct.getChanges().get(displayAreaInfo.token.asBinder());
|
||||||
@@ -177,6 +205,41 @@ public class DesktopModeControllerTest extends ShellTestCase {
|
|||||||
assertThat(displayWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_FULLSCREEN);
|
assertThat(displayWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_FULLSCREEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShowDesktopApps() {
|
||||||
|
// Set up two active tasks on desktop
|
||||||
|
mDesktopModeTaskRepository.addActiveTask(1);
|
||||||
|
mDesktopModeTaskRepository.addActiveTask(2);
|
||||||
|
MockToken token1 = new MockToken();
|
||||||
|
MockToken token2 = new MockToken();
|
||||||
|
ActivityManager.RunningTaskInfo taskInfo1 = new TestRunningTaskInfoBuilder().setToken(
|
||||||
|
token1.token()).setLastActiveTime(100).build();
|
||||||
|
ActivityManager.RunningTaskInfo taskInfo2 = new TestRunningTaskInfoBuilder().setToken(
|
||||||
|
token2.token()).setLastActiveTime(200).build();
|
||||||
|
when(mShellTaskOrganizer.getRunningTaskInfo(1)).thenReturn(taskInfo1);
|
||||||
|
when(mShellTaskOrganizer.getRunningTaskInfo(2)).thenReturn(taskInfo2);
|
||||||
|
|
||||||
|
// Run show desktop apps logic
|
||||||
|
mController.showDesktopApps();
|
||||||
|
ArgumentCaptor<WindowContainerTransaction> wctCaptor = ArgumentCaptor.forClass(
|
||||||
|
WindowContainerTransaction.class);
|
||||||
|
verify(mShellTaskOrganizer).applyTransaction(wctCaptor.capture());
|
||||||
|
WindowContainerTransaction wct = wctCaptor.getValue();
|
||||||
|
|
||||||
|
// Check wct has reorder calls
|
||||||
|
assertThat(wct.getHierarchyOps()).hasSize(2);
|
||||||
|
|
||||||
|
// Task 2 has activity later, must be first
|
||||||
|
WindowContainerTransaction.HierarchyOp op1 = wct.getHierarchyOps().get(0);
|
||||||
|
assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
|
||||||
|
assertThat(op1.getContainer()).isEqualTo(token2.binder());
|
||||||
|
|
||||||
|
// Task 1 should be second
|
||||||
|
WindowContainerTransaction.HierarchyOp op2 = wct.getHierarchyOps().get(0);
|
||||||
|
assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
|
||||||
|
assertThat(op2.getContainer()).isEqualTo(token2.binder());
|
||||||
|
}
|
||||||
|
|
||||||
private static class MockToken {
|
private static class MockToken {
|
||||||
private final WindowContainerToken mToken;
|
private final WindowContainerToken mToken;
|
||||||
private final IBinder mBinder;
|
private final IBinder mBinder;
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ import com.android.wm.shell.ShellTestCase;
|
|||||||
import com.android.wm.shell.TestShellExecutor;
|
import com.android.wm.shell.TestShellExecutor;
|
||||||
import com.android.wm.shell.common.ShellExecutor;
|
import com.android.wm.shell.common.ShellExecutor;
|
||||||
import com.android.wm.shell.common.TaskStackListenerImpl;
|
import com.android.wm.shell.common.TaskStackListenerImpl;
|
||||||
import com.android.wm.shell.desktopmode.DesktopMode;
|
import com.android.wm.shell.desktopmode.DesktopModeStatus;
|
||||||
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
|
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
|
||||||
import com.android.wm.shell.sysui.ShellCommandHandler;
|
import com.android.wm.shell.sysui.ShellCommandHandler;
|
||||||
import com.android.wm.shell.sysui.ShellInit;
|
import com.android.wm.shell.sysui.ShellInit;
|
||||||
@@ -190,8 +190,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testGetRecentTasks_groupActiveFreeformTasks() {
|
public void testGetRecentTasks_groupActiveFreeformTasks() {
|
||||||
StaticMockitoSession mockitoSession = mockitoSession().mockStatic(
|
StaticMockitoSession mockitoSession = mockitoSession().mockStatic(
|
||||||
DesktopMode.class).startMocking();
|
DesktopModeStatus.class).startMocking();
|
||||||
when(DesktopMode.isActive(any())).thenReturn(true);
|
when(DesktopModeStatus.isActive(any())).thenReturn(true);
|
||||||
|
|
||||||
ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
|
ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
|
||||||
ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
|
ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
|
||||||
|
|||||||
@@ -237,6 +237,13 @@ public class Task {
|
|||||||
public ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
|
public ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
|
||||||
new ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData();
|
new ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that this task for the desktop tile in recents.
|
||||||
|
*
|
||||||
|
* Used when desktop mode feature is enabled.
|
||||||
|
*/
|
||||||
|
public boolean desktopTile;
|
||||||
|
|
||||||
public Task() {
|
public Task() {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
@@ -267,6 +274,7 @@ public class Task {
|
|||||||
this(other.key, other.colorPrimary, other.colorBackground, other.isDockable,
|
this(other.key, other.colorPrimary, other.colorBackground, other.isDockable,
|
||||||
other.isLocked, other.taskDescription, other.topActivity);
|
other.isLocked, other.taskDescription, other.topActivity);
|
||||||
lastSnapshotData.set(other.lastSnapshotData);
|
lastSnapshotData.set(other.lastSnapshotData);
|
||||||
|
desktopTile = other.desktopTile;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ public class QuickStepContract {
|
|||||||
// See IRecentTasks.aidl
|
// See IRecentTasks.aidl
|
||||||
public static final String KEY_EXTRA_RECENT_TASKS = "recent_tasks";
|
public static final String KEY_EXTRA_RECENT_TASKS = "recent_tasks";
|
||||||
public static final String KEY_EXTRA_SHELL_BACK_ANIMATION = "extra_shell_back_animation";
|
public static final String KEY_EXTRA_SHELL_BACK_ANIMATION = "extra_shell_back_animation";
|
||||||
|
// See IDesktopMode.aidl
|
||||||
|
public static final String KEY_EXTRA_SHELL_DESKTOP_MODE = "extra_shell_desktop_mode";
|
||||||
|
|
||||||
public static final String NAV_BAR_MODE_3BUTTON_OVERLAY =
|
public static final String NAV_BAR_MODE_3BUTTON_OVERLAY =
|
||||||
WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
|
WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
|
||||||
|
|||||||
@@ -97,7 +97,8 @@ public abstract class SystemUIInitializer {
|
|||||||
.setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper())
|
.setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper())
|
||||||
.setRecentTasks(mWMComponent.getRecentTasks())
|
.setRecentTasks(mWMComponent.getRecentTasks())
|
||||||
.setBackAnimation(mWMComponent.getBackAnimation())
|
.setBackAnimation(mWMComponent.getBackAnimation())
|
||||||
.setFloatingTasks(mWMComponent.getFloatingTasks());
|
.setFloatingTasks(mWMComponent.getFloatingTasks())
|
||||||
|
.setDesktopMode(mWMComponent.getDesktopMode());
|
||||||
|
|
||||||
// Only initialize when not starting from tests since this currently initializes some
|
// Only initialize when not starting from tests since this currently initializes some
|
||||||
// components that shouldn't be run in the test environment
|
// components that shouldn't be run in the test environment
|
||||||
@@ -117,7 +118,8 @@ public abstract class SystemUIInitializer {
|
|||||||
.setStartingSurface(Optional.ofNullable(null))
|
.setStartingSurface(Optional.ofNullable(null))
|
||||||
.setRecentTasks(Optional.ofNullable(null))
|
.setRecentTasks(Optional.ofNullable(null))
|
||||||
.setBackAnimation(Optional.ofNullable(null))
|
.setBackAnimation(Optional.ofNullable(null))
|
||||||
.setFloatingTasks(Optional.ofNullable(null));
|
.setFloatingTasks(Optional.ofNullable(null))
|
||||||
|
.setDesktopMode(Optional.ofNullable(null));
|
||||||
}
|
}
|
||||||
mSysUIComponent = builder.build();
|
mSysUIComponent = builder.build();
|
||||||
if (initializeComponents) {
|
if (initializeComponents) {
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
|
|||||||
import com.android.wm.shell.TaskViewFactory;
|
import com.android.wm.shell.TaskViewFactory;
|
||||||
import com.android.wm.shell.back.BackAnimation;
|
import com.android.wm.shell.back.BackAnimation;
|
||||||
import com.android.wm.shell.bubbles.Bubbles;
|
import com.android.wm.shell.bubbles.Bubbles;
|
||||||
|
import com.android.wm.shell.desktopmode.DesktopMode;
|
||||||
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
|
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
|
||||||
import com.android.wm.shell.floating.FloatingTasks;
|
import com.android.wm.shell.floating.FloatingTasks;
|
||||||
import com.android.wm.shell.onehanded.OneHanded;
|
import com.android.wm.shell.onehanded.OneHanded;
|
||||||
@@ -113,6 +114,9 @@ public interface SysUIComponent {
|
|||||||
@BindsInstance
|
@BindsInstance
|
||||||
Builder setFloatingTasks(Optional<FloatingTasks> f);
|
Builder setFloatingTasks(Optional<FloatingTasks> f);
|
||||||
|
|
||||||
|
@BindsInstance
|
||||||
|
Builder setDesktopMode(Optional<DesktopMode> d);
|
||||||
|
|
||||||
SysUIComponent build();
|
SysUIComponent build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import com.android.wm.shell.common.annotations.ShellMainThread;
|
|||||||
import com.android.wm.shell.dagger.TvWMShellModule;
|
import com.android.wm.shell.dagger.TvWMShellModule;
|
||||||
import com.android.wm.shell.dagger.WMShellModule;
|
import com.android.wm.shell.dagger.WMShellModule;
|
||||||
import com.android.wm.shell.dagger.WMSingleton;
|
import com.android.wm.shell.dagger.WMSingleton;
|
||||||
|
import com.android.wm.shell.desktopmode.DesktopMode;
|
||||||
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
|
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
|
||||||
import com.android.wm.shell.floating.FloatingTasks;
|
import com.android.wm.shell.floating.FloatingTasks;
|
||||||
import com.android.wm.shell.onehanded.OneHanded;
|
import com.android.wm.shell.onehanded.OneHanded;
|
||||||
@@ -112,4 +113,10 @@ public interface WMComponent {
|
|||||||
|
|
||||||
@WMSingleton
|
@WMSingleton
|
||||||
Optional<FloatingTasks> getFloatingTasks();
|
Optional<FloatingTasks> getFloatingTasks();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional {@link DesktopMode} component for interacting with desktop mode.
|
||||||
|
*/
|
||||||
|
@WMSingleton
|
||||||
|
Optional<DesktopMode> getDesktopMode();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
|
|||||||
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
|
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
|
||||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS;
|
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS;
|
||||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_BACK_ANIMATION;
|
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_BACK_ANIMATION;
|
||||||
|
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_DESKTOP_MODE;
|
||||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_FLOATING_TASKS;
|
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_FLOATING_TASKS;
|
||||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
|
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
|
||||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
|
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
|
||||||
@@ -110,6 +111,7 @@ import com.android.systemui.statusbar.phone.CentralSurfaces;
|
|||||||
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
|
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
|
||||||
import com.android.systemui.statusbar.policy.CallbackController;
|
import com.android.systemui.statusbar.policy.CallbackController;
|
||||||
import com.android.wm.shell.back.BackAnimation;
|
import com.android.wm.shell.back.BackAnimation;
|
||||||
|
import com.android.wm.shell.desktopmode.DesktopMode;
|
||||||
import com.android.wm.shell.floating.FloatingTasks;
|
import com.android.wm.shell.floating.FloatingTasks;
|
||||||
import com.android.wm.shell.onehanded.OneHanded;
|
import com.android.wm.shell.onehanded.OneHanded;
|
||||||
import com.android.wm.shell.pip.Pip;
|
import com.android.wm.shell.pip.Pip;
|
||||||
@@ -169,6 +171,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
|
|||||||
private final KeyguardUnlockAnimationController mSysuiUnlockAnimationController;
|
private final KeyguardUnlockAnimationController mSysuiUnlockAnimationController;
|
||||||
private final Optional<RecentTasks> mRecentTasks;
|
private final Optional<RecentTasks> mRecentTasks;
|
||||||
private final Optional<BackAnimation> mBackAnimation;
|
private final Optional<BackAnimation> mBackAnimation;
|
||||||
|
private final Optional<DesktopMode> mDesktopModeOptional;
|
||||||
private final UiEventLogger mUiEventLogger;
|
private final UiEventLogger mUiEventLogger;
|
||||||
|
|
||||||
private Region mActiveNavBarRegion;
|
private Region mActiveNavBarRegion;
|
||||||
@@ -488,6 +491,9 @@ public class OverviewProxyService extends CurrentUserTracker implements
|
|||||||
mBackAnimation.ifPresent((backAnimation) -> params.putBinder(
|
mBackAnimation.ifPresent((backAnimation) -> params.putBinder(
|
||||||
KEY_EXTRA_SHELL_BACK_ANIMATION,
|
KEY_EXTRA_SHELL_BACK_ANIMATION,
|
||||||
backAnimation.createExternalInterface().asBinder()));
|
backAnimation.createExternalInterface().asBinder()));
|
||||||
|
mDesktopModeOptional.ifPresent((desktopMode -> params.putBinder(
|
||||||
|
KEY_EXTRA_SHELL_DESKTOP_MODE,
|
||||||
|
desktopMode.createExternalInterface().asBinder())));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Log.d(TAG_OPS, "OverviewProxyService connected, initializing overview proxy");
|
Log.d(TAG_OPS, "OverviewProxyService connected, initializing overview proxy");
|
||||||
@@ -573,6 +579,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
|
|||||||
Optional<RecentTasks> recentTasks,
|
Optional<RecentTasks> recentTasks,
|
||||||
Optional<BackAnimation> backAnimation,
|
Optional<BackAnimation> backAnimation,
|
||||||
Optional<StartingSurface> startingSurface,
|
Optional<StartingSurface> startingSurface,
|
||||||
|
Optional<DesktopMode> desktopModeOptional,
|
||||||
BroadcastDispatcher broadcastDispatcher,
|
BroadcastDispatcher broadcastDispatcher,
|
||||||
ShellTransitions shellTransitions,
|
ShellTransitions shellTransitions,
|
||||||
ScreenLifecycle screenLifecycle,
|
ScreenLifecycle screenLifecycle,
|
||||||
@@ -607,6 +614,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
|
|||||||
mShellTransitions = shellTransitions;
|
mShellTransitions = shellTransitions;
|
||||||
mRecentTasks = recentTasks;
|
mRecentTasks = recentTasks;
|
||||||
mBackAnimation = backAnimation;
|
mBackAnimation = backAnimation;
|
||||||
|
mDesktopModeOptional = desktopModeOptional;
|
||||||
mUiEventLogger = uiEventLogger;
|
mUiEventLogger = uiEventLogger;
|
||||||
|
|
||||||
dumpManager.registerDumpable(getClass().getSimpleName(), this);
|
dumpManager.registerDumpable(getClass().getSimpleName(), this);
|
||||||
|
|||||||
Reference in New Issue
Block a user