diff options
3 files changed, 277 insertions, 0 deletions
diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java new file mode 100644 index 000000000000..5e44d6c72bca --- /dev/null +++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java @@ -0,0 +1,125 @@ +/* + * 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.server.wm; + +import static android.util.DisplayMetrics.DENSITY_DEFAULT; + +import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; + +import android.annotation.Nullable; +import android.app.ActivityOptions; +import android.content.pm.ActivityInfo; +import android.graphics.Rect; +import android.os.SystemProperties; +import android.util.Slog; + +import com.android.server.wm.LaunchParamsController.LaunchParamsModifier; + +/** + * The class that defines default launch params for tasks in desktop mode + */ +public class DesktopModeLaunchParamsModifier implements LaunchParamsModifier { + + private static final String TAG = + TAG_WITH_CLASS_NAME ? "DesktopModeLaunchParamsModifier" : TAG_ATM; + private static final boolean DEBUG = false; + + // Desktop mode feature flag. + static final boolean DESKTOP_MODE_SUPPORTED = SystemProperties.getBoolean( + "persist.wm.debug.desktop_mode", false); + // Override default freeform task width when desktop mode is enabled. In dips. + private static final int DESKTOP_MODE_DEFAULT_WIDTH_DP = SystemProperties.getInt( + "persist.wm.debug.desktop_mode.default_width", 840); + // Override default freeform task height when desktop mode is enabled. In dips. + private static final int DESKTOP_MODE_DEFAULT_HEIGHT_DP = SystemProperties.getInt( + "persist.wm.debug.desktop_mode.default_height", 630); + + private StringBuilder mLogBuilder; + + @Override + public int onCalculate(@Nullable Task task, @Nullable ActivityInfo.WindowLayout layout, + @Nullable ActivityRecord activity, @Nullable ActivityRecord source, + @Nullable ActivityOptions options, @Nullable ActivityStarter.Request request, int phase, + LaunchParamsController.LaunchParams currentParams, + LaunchParamsController.LaunchParams outParams) { + + initLogBuilder(task, activity); + int result = calculate(task, layout, activity, source, options, request, phase, + currentParams, outParams); + outputLog(); + return result; + } + + private int calculate(@Nullable Task task, @Nullable ActivityInfo.WindowLayout layout, + @Nullable ActivityRecord activity, @Nullable ActivityRecord source, + @Nullable ActivityOptions options, @Nullable ActivityStarter.Request request, int phase, + LaunchParamsController.LaunchParams currentParams, + LaunchParamsController.LaunchParams outParams) { + + if (task == null) { + appendLog("task null, skipping"); + return RESULT_SKIP; + } + if (phase != PHASE_BOUNDS) { + appendLog("not in bounds phase, skipping"); + return RESULT_SKIP; + } + if (!task.inFreeformWindowingMode()) { + appendLog("not a freeform task, skipping"); + return RESULT_SKIP; + } + if (!currentParams.mBounds.isEmpty()) { + appendLog("currentParams has bounds set, not overriding"); + return RESULT_SKIP; + } + + // Copy over any values + outParams.set(currentParams); + + // Update width and height with default desktop mode values + float density = (float) task.getConfiguration().densityDpi / DENSITY_DEFAULT; + final int width = (int) (DESKTOP_MODE_DEFAULT_WIDTH_DP * density + 0.5f); + final int height = (int) (DESKTOP_MODE_DEFAULT_HEIGHT_DP * density + 0.5f); + outParams.mBounds.right = width; + outParams.mBounds.bottom = height; + + // Center the task in window bounds + Rect windowBounds = task.getWindowConfiguration().getBounds(); + outParams.mBounds.offset(windowBounds.centerX() - outParams.mBounds.centerX(), + windowBounds.centerY() - outParams.mBounds.centerY()); + + appendLog("setting desktop mode task bounds to %s", outParams.mBounds); + + return RESULT_DONE; + } + + private void initLogBuilder(Task task, ActivityRecord activity) { + if (DEBUG) { + mLogBuilder = new StringBuilder( + "DesktopModeLaunchParamsModifier: task=" + task + " activity=" + activity); + } + } + + private void appendLog(String format, Object... args) { + if (DEBUG) mLogBuilder.append(" ").append(String.format(format, args)); + } + + private void outputLog() { + if (DEBUG) Slog.d(TAG, mLogBuilder.toString()); + } +} diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java index 7bd2a4a28e69..e74e5787ef5a 100644 --- a/services/core/java/com/android/server/wm/LaunchParamsController.java +++ b/services/core/java/com/android/server/wm/LaunchParamsController.java @@ -64,6 +64,10 @@ class LaunchParamsController { void registerDefaultModifiers(ActivityTaskSupervisor supervisor) { // {@link TaskLaunchParamsModifier} handles window layout preferences. registerModifier(new TaskLaunchParamsModifier(supervisor)); + if (DesktopModeLaunchParamsModifier.DESKTOP_MODE_SUPPORTED) { + // {@link DesktopModeLaunchParamsModifier} handles default task size changes + registerModifier(new DesktopModeLaunchParamsModifier()); + } } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java new file mode 100644 index 000000000000..7830e9094796 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java @@ -0,0 +1,148 @@ +/* + * 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.server.wm; + +import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.util.DisplayMetrics.DENSITY_DEFAULT; + +import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS; +import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY; +import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_DONE; +import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import com.android.server.wm.LaunchParamsController.LaunchParamsModifier.Result; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for desktop mode task bounds. + * + * Build/Install/Run: + * atest WmTests:DesktopModeLaunchParamsModifierTests + */ +@SmallTest +@Presubmit +@RunWith(WindowTestRunner.class) +public class DesktopModeLaunchParamsModifierTests extends WindowTestsBase { + + private ActivityRecord mActivity; + + private DesktopModeLaunchParamsModifier mTarget; + + private LaunchParamsController.LaunchParams mCurrent; + private LaunchParamsController.LaunchParams mResult; + + @Before + public void setUp() throws Exception { + mActivity = new ActivityBuilder(mAtm).build(); + mTarget = new DesktopModeLaunchParamsModifier(); + mCurrent = new LaunchParamsController.LaunchParams(); + mCurrent.reset(); + mResult = new LaunchParamsController.LaunchParams(); + mResult.reset(); + } + + @Test + public void testReturnsSkipIfTaskIsNull() { + assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(null).calculate()); + } + + @Test + public void testReturnsSkipIfNotBoundsPhase() { + final Task task = new TaskBuilder(mSupervisor).build(); + assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).setPhase( + PHASE_DISPLAY).calculate()); + } + + @Test + public void testReturnsSkipIfTaskNotInFreeform() { + final Task task = new TaskBuilder(mSupervisor).setWindowingMode( + WINDOWING_MODE_FULLSCREEN).build(); + assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).calculate()); + } + + @Test + public void testReturnsSkipIfCurrentParamsHasBounds() { + final Task task = new TaskBuilder(mSupervisor).setWindowingMode( + WINDOWING_MODE_FREEFORM).build(); + mCurrent.mBounds.set(/* left */ 0, /* top */ 0, /* right */ 100, /* bottom */ 100); + assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).calculate()); + } + + @Test + public void testUsesDefaultBounds() { + final Task task = new TaskBuilder(mSupervisor).setWindowingMode( + WINDOWING_MODE_FREEFORM).build(); + assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate()); + assertEquals(dpiToPx(task, 840), mResult.mBounds.width()); + assertEquals(dpiToPx(task, 630), mResult.mBounds.height()); + } + + @Test + public void testUsesDisplayAreaAndWindowingModeFromSource() { + final Task task = new TaskBuilder(mSupervisor).setWindowingMode( + WINDOWING_MODE_FREEFORM).build(); + TaskDisplayArea mockTaskDisplayArea = mock(TaskDisplayArea.class); + mCurrent.mPreferredTaskDisplayArea = mockTaskDisplayArea; + mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM; + + assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate()); + assertEquals(mockTaskDisplayArea, mResult.mPreferredTaskDisplayArea); + assertEquals(WINDOWING_MODE_FREEFORM, mResult.mWindowingMode); + } + + private int dpiToPx(Task task, int dpi) { + float density = (float) task.getConfiguration().densityDpi / DENSITY_DEFAULT; + return (int) (dpi * density + 0.5f); + } + + private class CalculateRequestBuilder { + private Task mTask; + private int mPhase = PHASE_BOUNDS; + private final ActivityRecord mActivity = + DesktopModeLaunchParamsModifierTests.this.mActivity; + private final LaunchParamsController.LaunchParams mCurrentParams = mCurrent; + private final LaunchParamsController.LaunchParams mOutParams = mResult; + + private CalculateRequestBuilder setTask(Task task) { + mTask = task; + return this; + } + + private CalculateRequestBuilder setPhase(int phase) { + mPhase = phase; + return this; + } + + @Result + private int calculate() { + return mTarget.onCalculate(mTask, /* layout*/ null, mActivity, /* source */ + null, /* options */ null, /* request */ null, mPhase, mCurrentParams, + mOutParams); + } + } +} |