diff options
Diffstat (limited to 'libs')
115 files changed, 1966 insertions, 688 deletions
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java index 4ce294213526..bfccb29bc952 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/BackupHelper.java @@ -16,16 +16,26 @@ package androidx.window.extensions.embedding; +import static android.window.TaskFragmentOrganizer.KEY_RESTORE_TASK_FRAGMENTS_INFO; +import static android.window.TaskFragmentOrganizer.KEY_RESTORE_TASK_FRAGMENT_PARENT_INFO; + import android.os.Build; import android.os.Bundle; +import android.os.IBinder; import android.os.Looper; import android.os.MessageQueue; +import android.util.ArrayMap; import android.util.Log; +import android.util.SparseArray; +import android.window.TaskFragmentInfo; +import android.window.TaskFragmentParentInfo; +import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; import java.util.ArrayList; import java.util.List; +import java.util.Set; /** * Helper class to back up and restore the TaskFragmentOrganizer state, in order to resume @@ -40,11 +50,21 @@ class BackupHelper { @NonNull private final SplitController mController; @NonNull + private final SplitPresenter mPresenter; + @NonNull private final BackupIdler mBackupIdler = new BackupIdler(); private boolean mBackupIdlerScheduled; - BackupHelper(@NonNull SplitController splitController, @NonNull Bundle savedState) { + private final List<ParcelableTaskContainerData> mParcelableTaskContainerDataList = + new ArrayList<>(); + private final ArrayMap<IBinder, TaskFragmentInfo> mTaskFragmentInfos = new ArrayMap<>(); + private final SparseArray<TaskFragmentParentInfo> mTaskFragmentParentInfos = + new SparseArray<>(); + + BackupHelper(@NonNull SplitController splitController, @NonNull SplitPresenter splitPresenter, + @NonNull Bundle savedState) { mController = splitController; + mPresenter = splitPresenter; if (!savedState.isEmpty()) { restoreState(savedState); @@ -67,13 +87,13 @@ class BackupHelper { public boolean queueIdle() { synchronized (mController.mLock) { mBackupIdlerScheduled = false; - startBackup(); + saveState(); } return false; } } - private void startBackup() { + private void saveState() { final List<TaskContainer> taskContainers = mController.getTaskContainers(); if (taskContainers.isEmpty()) { Log.w(TAG, "No task-container to back up"); @@ -97,13 +117,92 @@ class BackupHelper { return; } - final List<ParcelableTaskContainerData> parcelableTaskContainerDataList = - savedState.getParcelableArrayList(KEY_TASK_CONTAINERS, - ParcelableTaskContainerData.class); - for (ParcelableTaskContainerData data : parcelableTaskContainerDataList) { - final TaskContainer taskContainer = new TaskContainer(data, mController); - if (DEBUG) Log.d(TAG, "Restoring task " + taskContainer.getTaskId()); - // TODO(b/289875940): implement the TaskContainer restoration. + if (DEBUG) Log.d(TAG, "Start restoring saved-state"); + mParcelableTaskContainerDataList.addAll(savedState.getParcelableArrayList( + KEY_TASK_CONTAINERS, ParcelableTaskContainerData.class)); + if (DEBUG) Log.d(TAG, "Retrieved tasks : " + mParcelableTaskContainerDataList.size()); + if (mParcelableTaskContainerDataList.isEmpty()) { + return; + } + + final List<TaskFragmentInfo> infos = savedState.getParcelableArrayList( + KEY_RESTORE_TASK_FRAGMENTS_INFO, TaskFragmentInfo.class); + for (TaskFragmentInfo info : infos) { + if (DEBUG) Log.d(TAG, "Retrieved: " + info); + mTaskFragmentInfos.put(info.getFragmentToken(), info); + mPresenter.updateTaskFragmentInfo(info); + } + + final List<TaskFragmentParentInfo> parentInfos = savedState.getParcelableArrayList( + KEY_RESTORE_TASK_FRAGMENT_PARENT_INFO, + TaskFragmentParentInfo.class); + for (TaskFragmentParentInfo info : parentInfos) { + if (DEBUG) Log.d(TAG, "Retrieved: " + info); + mTaskFragmentParentInfos.put(info.getTaskId(), info); + } + } + + boolean hasPendingStateToRestore() { + return !mParcelableTaskContainerDataList.isEmpty(); + } + + /** + * Returns {@code true} if any of the {@link TaskContainer} is restored. + * Otherwise, returns {@code false}. + */ + boolean rebuildTaskContainers(@NonNull WindowContainerTransaction wct, + @NonNull Set<EmbeddingRule> rules) { + if (mParcelableTaskContainerDataList.isEmpty()) { + return false; + } + + if (DEBUG) Log.d(TAG, "Rebuilding TaskContainers."); + final ArrayMap<String, EmbeddingRule> embeddingRuleMap = new ArrayMap<>(); + for (EmbeddingRule rule : rules) { + embeddingRuleMap.put(rule.getTag(), rule); + } + + boolean restoredAny = false; + for (int i = mParcelableTaskContainerDataList.size() - 1; i >= 0; i--) { + final ParcelableTaskContainerData parcelableTaskContainerData = + mParcelableTaskContainerDataList.get(i); + final List<String> tags = parcelableTaskContainerData.getSplitRuleTags(); + if (!embeddingRuleMap.containsAll(tags)) { + // has unknown tag, unable to restore. + if (DEBUG) { + Log.d(TAG, "Rebuilding TaskContainer abort! Unknown Tag. Task#" + + parcelableTaskContainerData.mTaskId); + } + continue; + } + + mParcelableTaskContainerDataList.remove(parcelableTaskContainerData); + final TaskContainer taskContainer = new TaskContainer(parcelableTaskContainerData, + mController, mTaskFragmentInfos); + if (DEBUG) Log.d(TAG, "Created TaskContainer " + taskContainer); + mController.addTaskContainer(taskContainer.getTaskId(), taskContainer); + + for (ParcelableSplitContainerData splitData : + parcelableTaskContainerData.getParcelableSplitContainerDataList()) { + final SplitRule rule = (SplitRule) embeddingRuleMap.get(splitData.mSplitRuleTag); + assert rule != null; + if (mController.getContainer(splitData.getPrimaryContainerToken()) != null + && mController.getContainer(splitData.getSecondaryContainerToken()) + != null) { + taskContainer.addSplitContainer( + new SplitContainer(splitData, mController, rule)); + } + } + + mController.onTaskFragmentParentRestored(wct, taskContainer.getTaskId(), + mTaskFragmentParentInfos.get(taskContainer.getTaskId())); + restoredAny = true; + } + + if (mParcelableTaskContainerDataList.isEmpty()) { + mTaskFragmentParentInfos.clear(); + mTaskFragmentInfos.clear(); } + return restoredAny; } -} +}
\ No newline at end of file diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java index 817cfce69b2e..cb280c530c1b 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableSplitContainerData.java @@ -89,13 +89,13 @@ class ParcelableSplitContainerData implements Parcelable { }; @NonNull - private IBinder getPrimaryContainerToken() { + IBinder getPrimaryContainerToken() { return mSplitContainer != null ? mSplitContainer.getPrimaryContainer().getToken() : mPrimaryContainerToken; } @NonNull - private IBinder getSecondaryContainerToken() { + IBinder getSecondaryContainerToken() { return mSplitContainer != null ? mSplitContainer.getSecondaryContainer().getToken() : mSecondaryContainerToken; } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java index 7377d005cda4..97aa69985907 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/ParcelableTaskContainerData.java @@ -108,6 +108,15 @@ class ParcelableTaskContainerData implements Parcelable { : mParcelableSplitContainerDataList; } + @NonNull + List<String> getSplitRuleTags() { + final List<String> tags = new ArrayList<>(); + for (ParcelableSplitContainerData data : getParcelableSplitContainerDataList()) { + tags.add(data.mSplitRuleTag); + } + return tags; + } + @Override public int describeContents() { return 0; diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java index 6d436ec01d98..faf73c24073f 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java @@ -86,6 +86,25 @@ class SplitContainer { } } + /** This is only used when restoring it from a {@link ParcelableSplitContainerData}. */ + SplitContainer(@NonNull ParcelableSplitContainerData parcelableData, + @NonNull SplitController splitController, @NonNull SplitRule splitRule) { + mParcelableData = parcelableData; + mPrimaryContainer = splitController.getContainer(parcelableData.getPrimaryContainerToken()); + mSecondaryContainer = splitController.getContainer( + parcelableData.getSecondaryContainerToken()); + mSplitRule = splitRule; + mDefaultSplitAttributes = splitRule.getDefaultSplitAttributes(); + mCurrentSplitAttributes = mDefaultSplitAttributes; + + if (shouldFinishPrimaryWithSecondary(splitRule)) { + mSecondaryContainer.addContainerToFinishOnExit(mPrimaryContainer); + } + if (shouldFinishSecondaryWithPrimary(splitRule)) { + mPrimaryContainer.addContainerToFinishOnExit(mSecondaryContainer); + } + } + void setPrimaryContainer(@NonNull TaskFragmentContainer primaryContainer) { if (!mParcelableData.mIsPrimaryContainerMutable) { throw new IllegalStateException("Cannot update primary TaskFragmentContainer"); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index f2f2b7ea7174..db4bb0e5e75e 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -279,6 +279,26 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen Log.i(TAG, "Setting embedding rules. Size: " + rules.size()); mSplitRules.clear(); mSplitRules.addAll(rules); + + if (!Flags.aeBackStackRestore() || !mPresenter.isRebuildTaskContainersNeeded()) { + return; + } + + try { + final TransactionRecord transactionRecord = + mTransactionManager.startNewTransaction(); + final WindowContainerTransaction wct = transactionRecord.getTransaction(); + if (mPresenter.rebuildTaskContainers(wct, rules)) { + transactionRecord.apply(false /* shouldApplyIndependently */); + updateCallbackIfNecessary(); + } else { + transactionRecord.abort(); + } + } catch (IllegalStateException ex) { + Log.e(TAG, "Having an existing transaction while running restoration with" + + "new rules!! It is likely too late to perform the restoration " + + "already!?", ex); + } } } @@ -903,6 +923,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } @GuardedBy("mLock") + void onTaskFragmentParentRestored(@NonNull WindowContainerTransaction wct, int taskId, + @NonNull TaskFragmentParentInfo parentInfo) { + onTaskFragmentParentInfoChanged(wct, taskId, parentInfo); + } + + @GuardedBy("mLock") void updateContainersInTaskIfVisible(@NonNull WindowContainerTransaction wct, int taskId) { final TaskContainer taskContainer = getTaskContainer(taskId); if (taskContainer == null) { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index abc7b291fc32..0c0ded9bad74 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -24,6 +24,7 @@ import static androidx.window.extensions.embedding.SplitController.TAG; import static androidx.window.extensions.embedding.WindowAttributes.DIM_AREA_ON_TASK; import android.annotation.AnimRes; +import android.annotation.NonNull; import android.app.Activity; import android.app.ActivityThread; import android.app.WindowConfiguration; @@ -47,7 +48,6 @@ import android.window.TaskFragmentCreationParams; import android.window.WindowContainerTransaction; import androidx.annotation.IntDef; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.window.extensions.core.util.function.Function; import androidx.window.extensions.embedding.SplitAttributes.SplitType; @@ -67,6 +67,7 @@ import com.android.window.flags.Flags; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.concurrent.Executor; /** @@ -174,7 +175,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { } else { registerOrganizer(); } - mBackupHelper = new BackupHelper(controller, outSavedState); + mBackupHelper = new BackupHelper(controller, this, outSavedState); if (!SplitController.ENABLE_SHELL_TRANSITIONS) { // TODO(b/207070762): cleanup with legacy app transition // Animation will be handled by WM Shell when Shell transition is enabled. @@ -186,6 +187,15 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { mBackupHelper.scheduleBackup(); } + boolean isRebuildTaskContainersNeeded() { + return mBackupHelper.hasPendingStateToRestore(); + } + + boolean rebuildTaskContainers(@NonNull WindowContainerTransaction wct, + @NonNull Set<EmbeddingRule> rules) { + return mBackupHelper.rebuildTaskContainers(wct, rules); + } + /** * Deletes the specified container and all other associated and dependent containers in the same * transaction. diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index 608a3bee7509..74cce68f270b 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -31,6 +31,7 @@ import android.app.WindowConfiguration.WindowingMode; import android.content.res.Configuration; import android.graphics.Rect; import android.os.IBinder; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.Log; @@ -147,14 +148,23 @@ class TaskContainer { /** This is only used when restoring it from a {@link ParcelableTaskContainerData}. */ TaskContainer(@NonNull ParcelableTaskContainerData data, - @NonNull SplitController splitController) { + @NonNull SplitController splitController, + @NonNull ArrayMap<IBinder, TaskFragmentInfo> taskFragmentInfoMap) { mParcelableTaskContainerData = new ParcelableTaskContainerData(data, this); + mInfo = new TaskFragmentParentInfo(new Configuration(), 0 /* displayId */, -1 /* taskId */, + false /* visible */, false /* hasDirectActivity */, null /* decorSurface */); mSplitController = splitController; for (ParcelableTaskFragmentContainerData tfData : data.getParcelableTaskFragmentContainerDataList()) { - final TaskFragmentContainer container = - new TaskFragmentContainer(tfData, splitController, this); - mContainers.add(container); + final TaskFragmentInfo info = taskFragmentInfoMap.get(tfData.mToken); + if (info != null && !info.isEmpty()) { + final TaskFragmentContainer container = + new TaskFragmentContainer(tfData, splitController, this); + container.setInfo(new WindowContainerTransaction(), info); + mContainers.add(container); + } else { + Log.d(TAG, "Drop " + tfData + " while restoring Task " + data.mTaskId); + } } } diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt new file mode 100644 index 000000000000..35d459f27534 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2024 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.bubbles.bar + +import android.app.ActivityManager +import android.content.Context +import android.graphics.Insets +import android.graphics.Rect +import android.view.LayoutInflater +import android.view.View +import android.view.WindowManager +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation +import com.android.internal.protolog.ProtoLog +import com.android.wm.shell.R +import com.android.wm.shell.bubbles.Bubble +import com.android.wm.shell.bubbles.BubbleData +import com.android.wm.shell.bubbles.BubbleExpandedViewManager +import com.android.wm.shell.bubbles.BubblePositioner +import com.android.wm.shell.bubbles.BubbleTaskView +import com.android.wm.shell.bubbles.BubbleTaskViewFactory +import com.android.wm.shell.bubbles.DeviceConfig +import com.android.wm.shell.bubbles.RegionSamplingProvider +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.shared.bubbles.BubbleBarLocation +import com.android.wm.shell.shared.handles.RegionSamplingHelper +import com.android.wm.shell.taskview.TaskView +import com.android.wm.shell.taskview.TaskViewTaskController +import com.google.common.truth.Truth.assertThat +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever +import java.util.Collections +import java.util.concurrent.Executor + +/** Tests for [BubbleBarExpandedViewTest] */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class BubbleBarExpandedViewTest { + companion object { + const val SCREEN_WIDTH = 2000 + const val SCREEN_HEIGHT = 1000 + } + + private val context = ApplicationProvider.getApplicationContext<Context>() + private val windowManager = context.getSystemService(WindowManager::class.java) + + private lateinit var mainExecutor: TestExecutor + private lateinit var bgExecutor: TestExecutor + + private lateinit var expandedViewManager: BubbleExpandedViewManager + private lateinit var positioner: BubblePositioner + private lateinit var bubbleTaskView: BubbleTaskView + + private lateinit var bubbleExpandedView: BubbleBarExpandedView + private var testableRegionSamplingHelper: TestableRegionSamplingHelper? = null + private var regionSamplingProvider: TestRegionSamplingProvider? = null + + @Before + fun setUp() { + ProtoLog.REQUIRE_PROTOLOGTOOL = false + mainExecutor = TestExecutor() + bgExecutor = TestExecutor() + positioner = BubblePositioner(context, windowManager) + positioner.setShowingInBubbleBar(true) + val deviceConfig = + DeviceConfig( + windowBounds = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT), + isLargeScreen = true, + isSmallTablet = false, + isLandscape = true, + isRtl = false, + insets = Insets.of(10, 20, 30, 40) + ) + positioner.update(deviceConfig) + + expandedViewManager = createExpandedViewManager() + bubbleTaskView = FakeBubbleTaskViewFactory().create() + + val inflater = LayoutInflater.from(context) + + regionSamplingProvider = TestRegionSamplingProvider() + + bubbleExpandedView = (inflater.inflate( + R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */ + ) as BubbleBarExpandedView) + bubbleExpandedView.initialize( + expandedViewManager, + positioner, + false /* isOverflow */, + bubbleTaskView, + mainExecutor, + bgExecutor, + regionSamplingProvider + ) + + getInstrumentation().runOnMainSync(Runnable { + bubbleExpandedView.onAttachedToWindow() + // Helper should be created once attached to window + testableRegionSamplingHelper = regionSamplingProvider!!.helper + }) + } + + @After + fun tearDown() { + testableRegionSamplingHelper?.stopAndDestroy() + } + + @Test + fun testCreateSamplingHelper_onAttach() { + assertThat(testableRegionSamplingHelper).isNotNull() + } + + @Test + fun testDestroySamplingHelper_onDetach() { + bubbleExpandedView.onDetachedFromWindow() + assertThat(testableRegionSamplingHelper!!.isDestroyed).isTrue() + } + + @Test + fun testStopSampling_onDragStart() { + bubbleExpandedView.setContentVisibility(true) + assertThat(testableRegionSamplingHelper!!.isStarted).isTrue() + + bubbleExpandedView.setDragging(true) + assertThat(testableRegionSamplingHelper!!.isStopped).isTrue() + } + + @Test + fun testStartSampling_onDragEnd() { + bubbleExpandedView.setDragging(true) + bubbleExpandedView.setContentVisibility(true) + assertThat(testableRegionSamplingHelper!!.isStopped).isTrue() + + bubbleExpandedView.setDragging(false) + assertThat(testableRegionSamplingHelper!!.isStarted).isTrue() + } + + @Test + fun testStartSampling_onContentVisible() { + bubbleExpandedView.setContentVisibility(true) + assertThat(testableRegionSamplingHelper!!.setWindowVisible).isTrue() + assertThat(testableRegionSamplingHelper!!.isStarted).isTrue() + } + + @Test + fun testStopSampling_onContentInvisible() { + bubbleExpandedView.setContentVisibility(false) + + assertThat(testableRegionSamplingHelper!!.setWindowInvisible).isTrue() + assertThat(testableRegionSamplingHelper!!.isStopped).isTrue() + } + + @Test + fun testSampling_startStopAnimating_visible() { + bubbleExpandedView.isAnimating = true + bubbleExpandedView.setContentVisibility(true) + assertThat(testableRegionSamplingHelper!!.isStopped).isTrue() + + bubbleExpandedView.isAnimating = false + assertThat(testableRegionSamplingHelper!!.isStarted).isTrue() + } + + @Test + fun testSampling_startStopAnimating_invisible() { + bubbleExpandedView.isAnimating = true + bubbleExpandedView.setContentVisibility(false) + assertThat(testableRegionSamplingHelper!!.isStopped).isTrue() + testableRegionSamplingHelper!!.reset() + + bubbleExpandedView.isAnimating = false + assertThat(testableRegionSamplingHelper!!.isStopped).isTrue() + } + + private inner class FakeBubbleTaskViewFactory : BubbleTaskViewFactory { + override fun create(): BubbleTaskView { + val taskViewTaskController = mock<TaskViewTaskController>() + val taskView = TaskView(context, taskViewTaskController) + val taskInfo = mock<ActivityManager.RunningTaskInfo>() + whenever(taskViewTaskController.taskInfo).thenReturn(taskInfo) + return BubbleTaskView(taskView, mainExecutor) + } + } + + private inner class TestRegionSamplingProvider : RegionSamplingProvider { + + lateinit var helper: TestableRegionSamplingHelper + + override fun createHelper( + sampledView: View?, + callback: RegionSamplingHelper.SamplingCallback?, + backgroundExecutor: Executor?, + mainExecutor: Executor? + ): RegionSamplingHelper { + helper = TestableRegionSamplingHelper(sampledView, callback, backgroundExecutor, + mainExecutor) + return helper + } + } + + private inner class TestableRegionSamplingHelper( + sampledView: View?, + samplingCallback: SamplingCallback?, + backgroundExecutor: Executor?, + mainExecutor: Executor? + ) : RegionSamplingHelper(sampledView, samplingCallback, backgroundExecutor, mainExecutor) { + + var isStarted = false + var isStopped = false + var isDestroyed = false + var setWindowVisible = false + var setWindowInvisible = false + + override fun start(initialSamplingBounds: Rect) { + super.start(initialSamplingBounds) + isStarted = true + } + + override fun stop() { + super.stop() + isStopped = true + } + + override fun stopAndDestroy() { + super.stopAndDestroy() + isDestroyed = true + } + + override fun setWindowVisible(visible: Boolean) { + super.setWindowVisible(visible) + if (visible) { + setWindowVisible = true + } else { + setWindowInvisible = true + } + } + + fun reset() { + isStarted = false + isStopped = false + isDestroyed = false + setWindowVisible = false + setWindowInvisible = false + } + } + + private fun createExpandedViewManager(): BubbleExpandedViewManager { + return object : BubbleExpandedViewManager { + override val overflowBubbles: List<Bubble> + get() = Collections.emptyList() + + override fun setOverflowListener(listener: BubbleData.Listener) { + } + + override fun collapseStack() { + } + + override fun updateWindowFlagsForBackpress(intercept: Boolean) { + } + + override fun promoteBubbleFromOverflow(bubble: Bubble) { + } + + override fun removeBubble(key: String, reason: Int) { + } + + override fun dismissBubble(bubble: Bubble, reason: Int) { + } + + override fun setAppBubbleTaskId(key: String, taskId: Int) { + } + + override fun isStackExpanded(): Boolean { + return true + } + + override fun isShowingAsBubbleBar(): Boolean { + return true + } + + override fun hideCurrentInputMethod() { + } + + override fun updateBubbleBarLocation(location: BubbleBarLocation) { + } + } + } + + private class TestExecutor : ShellExecutor { + + private val runnables: MutableList<Runnable> = mutableListOf() + + override fun execute(runnable: Runnable) { + runnables.add(runnable) + } + + override fun executeDelayed(runnable: Runnable, delayMillis: Long) { + execute(runnable) + } + + override fun removeCallbacks(runnable: Runnable?) {} + + override fun hasCallback(runnable: Runnable?): Boolean = false + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 2d98a2b675a3..755e0d5f742d 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -569,7 +569,7 @@ <!-- The thickness in dp for all desktop drag transition regions. --> <dimen name="desktop_mode_transition_region_thickness">44dp</dimen> - <item type="dimen" format="float" name="desktop_mode_fullscreen_region_scale">0.4</item> + <item type="dimen" format="float" name="desktop_mode_fullscreen_region_scale">0.2</item> <!-- The height on the screen where drag to the left or right edge will result in a desktop task snapping to split size. The empty space between this and the top is to allow diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/handles/RegionSamplingHelper.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/handles/RegionSamplingHelper.java index b92b8ef657a3..a06cf78d0898 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/handles/RegionSamplingHelper.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/handles/RegionSamplingHelper.java @@ -329,7 +329,7 @@ public class RegionSamplingHelper implements View.OnAttachStateChangeListener, /** * Get the sampled region of interest from the sampled view * @param sampledView The view that this helper is attached to for convenience - * @return the region to be sampled in sceen coordinates. Return {@code null} to avoid + * @return the region to be sampled in screen coordinates. Return {@code null} to avoid * sampling in this frame */ Rect getSampledRegion(View sampledView); diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java index cf39415b3fe6..6c83d88032df 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/pip/PipContentOverlay.java @@ -29,7 +29,6 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.TypedValue; import android.view.SurfaceControl; -import android.view.SurfaceSession; import android.window.TaskSnapshot; /** @@ -75,7 +74,7 @@ public abstract class PipContentOverlay { public PipColorOverlay(Context context) { mContext = context; - mLeash = new SurfaceControl.Builder(new SurfaceSession()) + mLeash = new SurfaceControl.Builder() .setCallsite(TAG) .setName(LAYER_NAME) .setColorLayer() @@ -123,7 +122,7 @@ public abstract class PipContentOverlay { public PipSnapshotOverlay(TaskSnapshot snapshot, Rect sourceRectHint) { mSnapshot = snapshot; mSourceRectHint = new Rect(sourceRectHint); - mLeash = new SurfaceControl.Builder(new SurfaceSession()) + mLeash = new SurfaceControl.Builder() .setCallsite(TAG) .setName(LAYER_NAME) .build(); @@ -183,7 +182,7 @@ public abstract class PipContentOverlay { mBitmap = Bitmap.createBitmap(overlaySize, overlaySize, Bitmap.Config.ARGB_8888); prepareAppIconOverlay(appIcon); - mLeash = new SurfaceControl.Builder(new SurfaceSession()) + mLeash = new SurfaceControl.Builder() .setCallsite(TAG) .setName(LAYER_NAME) .build(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt new file mode 100644 index 000000000000..05ce36120c4f --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2024 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. + */ + +@file:JvmName("AppToWebUtils") + +package com.android.wm.shell.apptoweb + +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri + +private val browserIntent = Intent() + .setAction(Intent.ACTION_VIEW) + .addCategory(Intent.CATEGORY_BROWSABLE) + .setData(Uri.parse("http:")) + +/** + * Returns a boolean indicating whether a given package is a browser app. + */ +fun isBrowserApp(context: Context, packageName: String, userId: Int): Boolean { + browserIntent.setPackage(packageName) + val list = context.packageManager.queryIntentActivitiesAsUser( + browserIntent, PackageManager.MATCH_ALL, userId + ) + + list.forEach { + if (it.activityInfo != null && it.handleAllWebDataURI) { + return true + } + } + return false +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AssistContentRequester.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AssistContentRequester.kt new file mode 100644 index 000000000000..249185eca323 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AssistContentRequester.kt @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2024 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.apptoweb + +import android.app.ActivityTaskManager +import android.app.IActivityTaskManager +import android.app.IAssistDataReceiver +import android.app.assist.AssistContent +import android.content.Context +import android.graphics.Bitmap +import android.os.Bundle +import android.os.RemoteException +import android.util.Slog +import java.lang.ref.WeakReference +import java.util.Collections +import java.util.WeakHashMap +import java.util.concurrent.Executor + +/** + * Can be used to request the AssistContent from a provided task id, useful for getting the web uri + * if provided from the task. + */ +class AssistContentRequester( + context: Context, + private val callBackExecutor: Executor, + private val systemInteractionExecutor: Executor +) { + interface Callback { + // Called when the [AssistContent] of the requested task is available. + fun onAssistContentAvailable(assistContent: AssistContent?) + } + + private val activityTaskManager: IActivityTaskManager = ActivityTaskManager.getService() + private val attributionTag: String? = context.attributionTag + private val packageName: String = context.applicationContext.packageName + + // If system loses the callback, our internal cache of original callback will also get cleared. + private val pendingCallbacks = Collections.synchronizedMap(WeakHashMap<Any, Callback>()) + + /** + * Request the [AssistContent] from the task with the provided id. + * + * @param taskId to query for the content. + * @param callback to call when the content is available, called on the main thread. + */ + fun requestAssistContent(taskId: Int, callback: Callback) { + // ActivityTaskManager interaction here is synchronous, so call off the main thread. + systemInteractionExecutor.execute { + try { + val success = activityTaskManager.requestAssistDataForTask( + AssistDataReceiver(callback, this), + taskId, + packageName, + attributionTag, + false /* fetchStructure */ + ) + if (!success) { + executeOnMainExecutor { callback.onAssistContentAvailable(null) } + } + } catch (e: RemoteException) { + Slog.e(TAG, "Requesting assist content failed for task: $taskId", e) + } + } + } + + private fun executeOnMainExecutor(callback: Runnable) { + callBackExecutor.execute(callback) + } + + private class AssistDataReceiver( + callback: Callback, + parent: AssistContentRequester + ) : IAssistDataReceiver.Stub() { + // The AssistDataReceiver binder callback object is passed to a system server, that may + // keep hold of it for longer than the lifetime of the AssistContentRequester object, + // potentially causing a memory leak. In the callback passed to the system server, only + // keep a weak reference to the parent object and lookup its callback if it still exists. + private val parentRef: WeakReference<AssistContentRequester> + private val callbackKey = Any() + + init { + parent.pendingCallbacks[callbackKey] = callback + parentRef = WeakReference(parent) + } + + override fun onHandleAssistData(data: Bundle?) { + val content = data?.getParcelable(ASSIST_KEY_CONTENT, AssistContent::class.java) + if (content == null) { + Slog.d(TAG, "Received AssistData, but no AssistContent found") + return + } + val requester = parentRef.get() + if (requester != null) { + val callback = requester.pendingCallbacks[callbackKey] + if (callback != null) { + requester.executeOnMainExecutor { callback.onAssistContentAvailable(content) } + } else { + Slog.d(TAG, "Callback received after calling UI was disposed of") + } + } else { + Slog.d(TAG, "Callback received after Requester was collected") + } + } + + override fun onHandleAssistScreenshot(screenshot: Bitmap) {} + } + + companion object { + private const val TAG = "AssistContentRequester" + private const val ASSIST_KEY_CONTENT = "content" + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS index bfe1306a60e6..6207e5b020f7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS @@ -1,6 +1,8 @@ atsjenk@google.com jorgegil@google.com madym@google.com +mattsziklay@google.com +mdehaini@google.com pbdr@google.com tkachenkoi@google.com vaniadesmonda@google.com diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 7b3b2071ef02..156399499c5b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -982,7 +982,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mShellBackAnimationRegistry.resetDefaultCrossActivity(); cancelLatencyTracking(); mReceivedNullNavigationInfo = false; - mBackTransitionHandler.mLastTrigger = triggerBack; if (mBackNavigationInfo != null) { mPreviousNavigationType = mBackNavigationInfo.getType(); mBackNavigationInfo.onBackNavigationFinished(triggerBack); @@ -1103,7 +1102,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont endLatencyTracking(); if (!validateAnimationTargets(apps)) { Log.e(TAG, "Invalid animation targets!"); - mBackTransitionHandler.consumeQueuedTransitionIfNeeded(); return; } mBackAnimationFinishedCallback = finishedCallback; @@ -1113,7 +1111,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont return; } kickStartAnimation(); - mBackTransitionHandler.consumeQueuedTransitionIfNeeded(); }); } @@ -1121,7 +1118,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont public void onAnimationCancelled() { mShellExecutor.execute( () -> { - mBackTransitionHandler.consumeQueuedTransitionIfNeeded(); if (!mShellBackAnimationRegistry.cancel( mBackNavigationInfo != null ? mBackNavigationInfo.getType() @@ -1160,8 +1156,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont boolean mCloseTransitionRequested; SurfaceControl.Transaction mFinishOpenTransaction; Transitions.TransitionFinishCallback mFinishOpenTransitionCallback; - QueuedTransition mQueuedTransition = null; - boolean mLastTrigger; // The Transition to make behindActivity become visible IBinder mPrepareOpenTransition; // The Transition to make behindActivity become invisible, if prepare open exist and @@ -1178,13 +1172,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } } - void consumeQueuedTransitionIfNeeded() { - if (mQueuedTransition != null) { - mQueuedTransition.consume(); - mQueuedTransition = null; - } - } - private void applyFinishOpenTransition() { mOpenTransitionInfo = null; mPrepareOpenTransition = null; @@ -1215,7 +1202,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont @NonNull SurfaceControl.Transaction st, @NonNull SurfaceControl.Transaction ft, @NonNull Transitions.TransitionFinishCallback finishCallback) { - if (info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION) { + final boolean isPrepareTransition = + info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION; + if (isPrepareTransition) { kickStartAnimation(); } // Both mShellExecutor and Transitions#mMainExecutor are ShellMainThread, so we don't @@ -1240,21 +1229,14 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } if (mApps == null || mApps.length == 0) { - if (mBackNavigationInfo != null && mShellBackAnimationRegistry - .isWaitingAnimation(mBackNavigationInfo.getType())) { - // Waiting for animation? Queue update to wait for animation start. - consumeQueuedTransitionIfNeeded(); - mQueuedTransition = new QueuedTransition(info, st, ft, finishCallback); - return true; - } else if (mLastTrigger) { - // animation was done, consume directly + if (mCloseTransitionRequested) { + // animation never start, consume directly applyAndFinish(st, ft, finishCallback); return true; - } else { - // animation was cancelled but transition haven't happen, we must handle it - if (mClosePrepareTransition == null && mCurrentTracker.isFinished()) { - createClosePrepareTransition(); - } + } else if (mClosePrepareTransition == null && isPrepareTransition) { + // Gesture animation was cancelled before prepare transition ready, create the + // the close prepare transition + createClosePrepareTransition(); } } @@ -1413,9 +1395,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont if (mPrepareOpenTransition != null) { applyFinishOpenTransition(); } - if (mQueuedTransition != null) { - consumeQueuedTransitionIfNeeded(); - } return; } // Handle the commit transition if this handler is running the open transition. @@ -1423,11 +1402,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont t.apply(); if (mCloseTransitionRequested) { if (mApps == null || mApps.length == 0) { - if (mQueuedTransition == null) { - // animation was done - applyFinishOpenTransition(); - mCloseTransitionRequested = false; - } // let queued transition finish. + // animation was done + applyFinishOpenTransition(); + mCloseTransitionRequested = false; } else { // we are animating, wait until animation finish mOnAnimationFinishCallback = () -> { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index 0c95934abf93..169361ad5f6b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -609,7 +609,8 @@ public class Bubble implements BubbleViewProvider { callback.onBubbleViewsReady(bubble); } }, - mMainExecutor); + mMainExecutor, + mBgExecutor); if (mInflateSynchronously) { mInflationTaskLegacy.onPostExecute(mInflationTaskLegacy.doInBackground()); } else { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt index f32974e1765d..68c4657f2b68 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt @@ -80,7 +80,10 @@ class BubbleOverflow(private val context: Context, private val positioner: Bubbl expandedViewManager, positioner, /* isOverflow= */ true, - /* bubbleTaskView= */ null + /* bubbleTaskView= */ null, + /* mainExecutor= */ null, + /* backgroundExecutor= */ null, + /* regionSamplingProvider= */ null ) } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java index 5f8f0fd0c54c..0c0fd7b10f6e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java @@ -60,6 +60,9 @@ public class BubbleTaskViewHelper { /** Called when back is pressed on the task root. */ void onBackPressed(); + + /** Called when task removal has started. */ + void onTaskRemovalStarted(); } private final Context mContext; @@ -190,6 +193,7 @@ public class BubbleTaskViewHelper { ((ViewGroup) mParentView).removeView(mTaskView); mTaskView = null; } + mListener.onTaskRemovalStarted(); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java index 13855f73fb4a..3982a237dd3b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java @@ -38,6 +38,7 @@ import android.graphics.drawable.Icon; import android.util.Log; import android.util.PathParser; import android.view.LayoutInflater; +import android.view.View; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; @@ -47,6 +48,7 @@ import com.android.launcher3.icons.BubbleIconFactory; import com.android.wm.shell.R; import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView; import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; +import com.android.wm.shell.shared.handles.RegionSamplingHelper; import java.lang.ref.WeakReference; import java.util.Objects; @@ -222,7 +224,16 @@ public class BubbleViewInfoTask { ProtoLog.v(WM_SHELL_BUBBLES, "Task initializing bubble bar expanded view key=%s", mBubble.getKey()); viewInfo.bubbleBarExpandedView.initialize(mExpandedViewManager.get(), - mPositioner.get(), false /* isOverflow */, viewInfo.taskView); + mPositioner.get(), false /* isOverflow */, viewInfo.taskView, + mMainExecutor, mBgExecutor, new RegionSamplingProvider() { + @Override + public RegionSamplingHelper createHelper(View sampledView, + RegionSamplingHelper.SamplingCallback callback, + Executor backgroundExecutor, Executor mainExecutor) { + return RegionSamplingProvider.super.createHelper(sampledView, + callback, backgroundExecutor, mainExecutor); + } + }); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java index 5cfebf8f1647..1b7bb0db6516 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java @@ -38,6 +38,7 @@ import android.os.AsyncTask; import android.util.Log; import android.util.PathParser; import android.view.LayoutInflater; +import android.view.View; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; @@ -46,6 +47,7 @@ import com.android.launcher3.icons.BubbleIconFactory; import com.android.wm.shell.R; import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView; import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; +import com.android.wm.shell.shared.handles.RegionSamplingHelper; import java.lang.ref.WeakReference; import java.util.Objects; @@ -85,6 +87,7 @@ public class BubbleViewInfoTaskLegacy extends private boolean mSkipInflation; private Callback mCallback; private Executor mMainExecutor; + private Executor mBackgroundExecutor; /** * Creates a task to load information for the provided {@link Bubble}. Once all info @@ -100,7 +103,8 @@ public class BubbleViewInfoTaskLegacy extends BubbleIconFactory factory, boolean skipInflation, Callback c, - Executor mainExecutor) { + Executor mainExecutor, + Executor backgroundExecutor) { mBubble = b; mContext = new WeakReference<>(context); mExpandedViewManager = new WeakReference<>(expandedViewManager); @@ -112,6 +116,7 @@ public class BubbleViewInfoTaskLegacy extends mSkipInflation = skipInflation; mCallback = c; mMainExecutor = mainExecutor; + mBackgroundExecutor = backgroundExecutor; } @Override @@ -123,7 +128,7 @@ public class BubbleViewInfoTaskLegacy extends if (mLayerView.get() != null) { return BubbleViewInfo.populateForBubbleBar(mContext.get(), mExpandedViewManager.get(), mTaskViewFactory.get(), mPositioner.get(), mLayerView.get(), mIconFactory, - mBubble, mSkipInflation); + mBubble, mSkipInflation, mMainExecutor, mBackgroundExecutor); } else { return BubbleViewInfo.populate(mContext.get(), mExpandedViewManager.get(), mTaskViewFactory.get(), mPositioner.get(), mStackView.get(), mIconFactory, @@ -188,7 +193,9 @@ public class BubbleViewInfoTaskLegacy extends BubbleBarLayerView layerView, BubbleIconFactory iconFactory, Bubble b, - boolean skipInflation) { + boolean skipInflation, + Executor mainExecutor, + Executor backgroundExecutor) { BubbleViewInfo info = new BubbleViewInfo(); if (!skipInflation && !b.isInflated()) { @@ -197,7 +204,16 @@ public class BubbleViewInfoTaskLegacy extends info.bubbleBarExpandedView = (BubbleBarExpandedView) inflater.inflate( R.layout.bubble_bar_expanded_view, layerView, false /* attachToRoot */); info.bubbleBarExpandedView.initialize( - expandedViewManager, positioner, false /* isOverflow */, bubbleTaskView); + expandedViewManager, positioner, false /* isOverflow */, bubbleTaskView, + mainExecutor, backgroundExecutor, new RegionSamplingProvider() { + @Override + public RegionSamplingHelper createHelper(View sampledView, + RegionSamplingHelper.SamplingCallback callback, + Executor backgroundExecutor, Executor mainExecutor) { + return RegionSamplingProvider.super.createHelper(sampledView, + callback, backgroundExecutor, mainExecutor); + } + }); } if (!populateCommonInfo(info, c, b, iconFactory)) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RegionSamplingProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RegionSamplingProvider.java new file mode 100644 index 000000000000..30f5c8fd56c3 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RegionSamplingProvider.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 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.bubbles; + +import android.view.View; + +import com.android.wm.shell.shared.handles.RegionSamplingHelper; + +import java.util.concurrent.Executor; + +/** + * Wrapper to provide a {@link com.android.wm.shell.shared.handles.RegionSamplingHelper} to allow + * testing it. + */ +public interface RegionSamplingProvider { + + /** Creates and returns the region sampling helper */ + default RegionSamplingHelper createHelper(View sampledView, + RegionSamplingHelper.SamplingCallback callback, + Executor backgroundExecutor, + Executor mainExecutor) { + return new RegionSamplingHelper(sampledView, + callback, backgroundExecutor, mainExecutor); + } + +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java index 565fde0a853c..74c3748dccaf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java @@ -253,6 +253,7 @@ public class BubbleBarAnimationHelper { return; } setDragPivot(bbev); + bbev.setDragging(true); // Corner radius gets scaled, apply the reverse scale to ensure we have the desired radius final float cornerRadius = bbev.getDraggedCornerRadius() / EXPANDED_VIEW_DRAG_SCALE; @@ -329,6 +330,7 @@ public class BubbleBarAnimationHelper { public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); bbev.resetPivot(); + bbev.setDragging(false); } }); startNewDragAnimation(animatorSet); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java index f90b2aa95555..ec235a5d84ab 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java @@ -19,10 +19,7 @@ package com.android.wm.shell.bubbles.bar; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import android.annotation.Nullable; -import android.app.ActivityManager; import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Color; import android.graphics.Insets; import android.graphics.Outline; import android.graphics.Rect; @@ -37,6 +34,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import com.android.wm.shell.R; import com.android.wm.shell.bubbles.Bubble; @@ -46,9 +44,12 @@ import com.android.wm.shell.bubbles.BubblePositioner; import com.android.wm.shell.bubbles.BubbleTaskView; import com.android.wm.shell.bubbles.BubbleTaskViewHelper; import com.android.wm.shell.bubbles.Bubbles; +import com.android.wm.shell.bubbles.RegionSamplingProvider; import com.android.wm.shell.shared.bubbles.BubbleBarLocation; +import com.android.wm.shell.shared.handles.RegionSamplingHelper; import com.android.wm.shell.taskview.TaskView; +import java.util.concurrent.Executor; import java.util.function.Supplier; /** Expanded view of a bubble when it's part of the bubble bar. */ @@ -92,16 +93,35 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView private boolean mIsOverflow; private BubbleTaskViewHelper mBubbleTaskViewHelper; private BubbleBarMenuViewController mMenuViewController; - private @Nullable Supplier<Rect> mLayerBoundsSupplier; - private @Nullable Listener mListener; + @Nullable + private Supplier<Rect> mLayerBoundsSupplier; + @Nullable + private Listener mListener; private BubbleBarHandleView mHandleView; - private @Nullable TaskView mTaskView; - private @Nullable BubbleOverflowContainerView mOverflowView; + @Nullable + private TaskView mTaskView; + @Nullable + private BubbleOverflowContainerView mOverflowView; + /** + * The handle shown in the caption area is tinted based on the background color of the area. + * This can vary so we sample the caption region and update the handle color based on that. + * If we're showing the overflow, the helper and executors will be null. + */ + @Nullable + private RegionSamplingHelper mRegionSamplingHelper; + @Nullable + private RegionSamplingProvider mRegionSamplingProvider; + @Nullable + private Executor mMainExecutor; + @Nullable + private Executor mBackgroundExecutor; + private final Rect mSampleRect = new Rect(); + private final int[] mLoc = new int[2]; + + /** Height of the caption inset at the top of the TaskView */ private int mCaptionHeight; - - private int mBackgroundColor; /** Corner radius used when view is resting */ private float mRestingCornerRadius = 0f; /** Corner radius applied while dragging */ @@ -116,6 +136,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView */ private boolean mIsContentVisible = false; private boolean mIsAnimating; + private boolean mIsDragging; public BubbleBarExpandedView(Context context) { this(context, null); @@ -154,21 +175,20 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView setOnTouchListener((v, event) -> true); } - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - // Hide manage menu when view disappears - mMenuViewController.hideMenu(false /* animated */); - } - /** Initializes the view, must be called before doing anything else. */ public void initialize(BubbleExpandedViewManager expandedViewManager, BubblePositioner positioner, boolean isOverflow, - @Nullable BubbleTaskView bubbleTaskView) { + @Nullable BubbleTaskView bubbleTaskView, + @Nullable Executor mainExecutor, + @Nullable Executor backgroundExecutor, + @Nullable RegionSamplingProvider regionSamplingProvider) { mManager = expandedViewManager; mPositioner = positioner; mIsOverflow = isOverflow; + mMainExecutor = mainExecutor; + mBackgroundExecutor = backgroundExecutor; + mRegionSamplingProvider = regionSamplingProvider; if (mIsOverflow) { mOverflowView = (BubbleOverflowContainerView) LayoutInflater.from(getContext()).inflate( @@ -191,6 +211,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView mTaskView.setEnableSurfaceClipping(true); mTaskView.setCornerRadius(mCurrentCornerRadius); mTaskView.setVisibility(VISIBLE); + mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0)); // Handle view needs to draw on top of task view. bringChildToFront(mHandleView); @@ -245,32 +266,40 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView return mHandleView; } - // TODO (b/275087636): call this when theme/config changes /** Updates the view based on the current theme. */ public void applyThemeAttrs() { + mCaptionHeight = getResources().getDimensionPixelSize( + R.dimen.bubble_bar_expanded_view_caption_height); mRestingCornerRadius = getResources().getDimensionPixelSize( - R.dimen.bubble_bar_expanded_view_corner_radius - ); + R.dimen.bubble_bar_expanded_view_corner_radius); mDraggedCornerRadius = getResources().getDimensionPixelSize( - R.dimen.bubble_bar_expanded_view_corner_radius_dragged - ); + R.dimen.bubble_bar_expanded_view_corner_radius_dragged); mCurrentCornerRadius = mRestingCornerRadius; - final TypedArray ta = mContext.obtainStyledAttributes(new int[]{ - android.R.attr.colorBackgroundFloating}); - mBackgroundColor = ta.getColor(0, Color.WHITE); - ta.recycle(); - mCaptionHeight = getResources().getDimensionPixelSize( - R.dimen.bubble_bar_expanded_view_caption_height); - if (mTaskView != null) { mTaskView.setCornerRadius(mCurrentCornerRadius); - updateHandleColor(true /* animated */); + mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0)); } } @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + // Hide manage menu when view disappears + mMenuViewController.hideMenu(false /* animated */); + if (mRegionSamplingHelper != null) { + mRegionSamplingHelper.stopAndDestroy(); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + recreateRegionSamplingHelper(); + } + + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mTaskView != null) { @@ -284,16 +313,13 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if (mTaskView != null) { - mTaskView.layout(l, t, r, - t + mTaskView.getMeasuredHeight()); - mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0)); + mTaskView.layout(l, t, r, t + mTaskView.getMeasuredHeight()); } } @Override public void onTaskCreated() { setContentVisibility(true); - updateHandleColor(false /* animated */); if (mListener != null) { mListener.onTaskCreated(); } @@ -305,11 +331,70 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView } @Override + public void onTaskRemovalStarted() { + if (mRegionSamplingHelper != null) { + mRegionSamplingHelper.stopAndDestroy(); + } + } + + @Override public void onBackPressed() { if (mListener == null) return; mListener.onBackPressed(); } + /** + * Set whether this view is currently being dragged. + * + * When dragging, the handle is hidden and content shouldn't be sampled. When dragging has + * ended we should start again. + */ + public void setDragging(boolean isDragging) { + if (isDragging != mIsDragging) { + mIsDragging = isDragging; + updateSamplingState(); + } + } + + /** Returns whether region sampling should be enabled, i.e. if task view content is visible. */ + private boolean shouldSampleRegion() { + return mTaskView != null + && mTaskView.getTaskInfo() != null + && !mIsDragging + && !mIsAnimating + && mIsContentVisible; + } + + /** + * Handles starting or stopping the region sampling helper based on + * {@link #shouldSampleRegion()}. + */ + private void updateSamplingState() { + if (mRegionSamplingHelper == null) return; + boolean shouldSample = shouldSampleRegion(); + if (shouldSample) { + mRegionSamplingHelper.start(getCaptionSampleRect()); + } else { + mRegionSamplingHelper.stop(); + } + } + + /** Returns the current area of the caption bar, in screen coordinates. */ + Rect getCaptionSampleRect() { + if (mTaskView == null) return null; + mTaskView.getLocationOnScreen(mLoc); + mSampleRect.set(mLoc[0], mLoc[1], + mLoc[0] + mTaskView.getWidth(), + mLoc[1] + mCaptionHeight); + return mSampleRect; + } + + @VisibleForTesting + @Nullable + public RegionSamplingHelper getRegionSamplingHelper() { + return mRegionSamplingHelper; + } + /** Cleans up the expanded view, should be called when the bubble is no longer active. */ public void cleanUpExpandedState() { mMenuViewController.hideMenu(false /* animated */); @@ -394,27 +479,14 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView if (!mIsAnimating) { mTaskView.setAlpha(visible ? 1f : 0f); + if (mRegionSamplingHelper != null) { + mRegionSamplingHelper.setWindowVisible(visible); + } + updateSamplingState(); } } /** - * Updates the handle color based on the task view status bar or background color; if those - * are transparent it defaults to the background color pulled from system theme attributes. - */ - private void updateHandleColor(boolean animated) { - if (mTaskView == null || mTaskView.getTaskInfo() == null) return; - int color = mBackgroundColor; - ActivityManager.TaskDescription taskDescription = mTaskView.getTaskInfo().taskDescription; - if (taskDescription.getStatusBarColor() != Color.TRANSPARENT) { - color = taskDescription.getStatusBarColor(); - } else if (taskDescription.getBackgroundColor() != Color.TRANSPARENT) { - color = taskDescription.getBackgroundColor(); - } - final boolean isRegionDark = Color.luminance(color) <= 0.5; - mHandleView.updateHandleColor(isRegionDark, animated); - } - - /** * Sets the alpha of both this view and the task view. */ public void setTaskViewAlpha(float alpha) { @@ -442,6 +514,11 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView */ public void setAnimating(boolean animating) { mIsAnimating = animating; + if (mIsAnimating) { + // Stop sampling while animating -- when animating is done setContentVisibility will + // re-trigger sampling if we're visible. + updateSamplingState(); + } // If we're done animating, apply the correct visibility. if (!animating) { setContentVisibility(mIsContentVisible); @@ -481,6 +558,37 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView } } + private void recreateRegionSamplingHelper() { + if (mRegionSamplingHelper != null) { + mRegionSamplingHelper.stopAndDestroy(); + } + if (mMainExecutor == null || mBackgroundExecutor == null + || mRegionSamplingProvider == null) { + // Null when it's the overflow / don't need sampling then. + return; + } + mRegionSamplingHelper = mRegionSamplingProvider.createHelper(this, + new RegionSamplingHelper.SamplingCallback() { + @Override + public void onRegionDarknessChanged(boolean isRegionDark) { + if (mHandleView != null) { + mHandleView.updateHandleColor(isRegionDark, + true /* animated */); + } + } + + @Override + public Rect getSampledRegion(View sampledView) { + return getCaptionSampleRect(); + } + + @Override + public boolean isSamplingEnabled() { + return shouldSampleRegion(); + } + }, mMainExecutor, mBackgroundExecutor); + } + private class HandleViewAccessibilityDelegate extends AccessibilityDelegate { @Override public void onInitializeAccessibilityNodeInfo(@NonNull View host, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java index c91567d7d8be..e781c07f01a7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java @@ -42,7 +42,9 @@ public class BubbleBarHandleView extends View { private final @ColorInt int mHandleLightColor; private final @ColorInt int mHandleDarkColor; - private @Nullable ObjectAnimator mColorChangeAnim; + private @ColorInt int mCurrentColor; + @Nullable + private ObjectAnimator mColorChangeAnim; public BubbleBarHandleView(Context context) { this(context, null /* attrs */); @@ -88,13 +90,17 @@ public class BubbleBarHandleView extends View { * * @param isRegionDark Whether the background behind the handle is dark, and thus the handle * should be light (and vice versa). - * @param animated Whether to animate the change, or apply it immediately. + * @param animated Whether to animate the change, or apply it immediately. */ public void updateHandleColor(boolean isRegionDark, boolean animated) { int newColor = isRegionDark ? mHandleLightColor : mHandleDarkColor; + if (newColor == mCurrentColor) { + return; + } if (mColorChangeAnim != null) { mColorChangeAnim.cancel(); } + mCurrentColor = newColor; if (animated) { mColorChangeAnim = ObjectAnimator.ofArgb(this, "backgroundColor", newColor); mColorChangeAnim.addListener(new AnimatorListenerAdapter() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java index 4b138e43bc3f..dd17e2980e58 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java @@ -17,7 +17,6 @@ package com.android.wm.shell.common; import android.view.SurfaceControl; -import android.view.SurfaceSession; /** * Helpers for handling surface. @@ -25,16 +24,15 @@ import android.view.SurfaceSession; public class SurfaceUtils { /** Creates a dim layer above host surface. */ public static SurfaceControl makeDimLayer(SurfaceControl.Transaction t, SurfaceControl host, - String name, SurfaceSession surfaceSession) { - final SurfaceControl dimLayer = makeColorLayer(host, name, surfaceSession); + String name) { + final SurfaceControl dimLayer = makeColorLayer(host, name); t.setLayer(dimLayer, Integer.MAX_VALUE).setColor(dimLayer, new float[]{0f, 0f, 0f}); return dimLayer; } /** Creates a color layer for host surface. */ - public static SurfaceControl makeColorLayer(SurfaceControl host, String name, - SurfaceSession surfaceSession) { - return new SurfaceControl.Builder(surfaceSession) + public static SurfaceControl makeColorLayer(SurfaceControl host, String name) { + return new SurfaceControl.Builder() .setParent(host) .setColorLayer() .setName(name) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java index ef33b3830e45..3dc86decdb2e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java @@ -42,7 +42,6 @@ import android.view.InsetsState; import android.view.ScrollCaptureResponse; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; -import android.view.SurfaceSession; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; @@ -311,7 +310,7 @@ public class SystemWindows { @Override protected SurfaceControl getParentSurface(IWindow window, WindowManager.LayoutParams attrs) { - SurfaceControl leash = new SurfaceControl.Builder(new SurfaceSession()) + SurfaceControl leash = new SurfaceControl.Builder() .setContainerLayer() .setName("SystemWindowLeash") .setHidden(false) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java index 7175e361f91a..de3152ad7687 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java @@ -43,7 +43,6 @@ import android.view.IWindow; import android.view.LayoutInflater; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; -import android.view.SurfaceSession; import android.view.View; import android.view.WindowManager; import android.view.WindowlessWindowManager; @@ -74,7 +73,6 @@ public class SplitDecorManager extends WindowlessWindowManager { private static final String GAP_BACKGROUND_SURFACE_NAME = "GapBackground"; private final IconProvider mIconProvider; - private final SurfaceSession mSurfaceSession; private Drawable mIcon; private ImageView mVeilIconView; @@ -103,17 +101,15 @@ public class SplitDecorManager extends WindowlessWindowManager { private int mOffsetY; private int mRunningAnimationCount = 0; - public SplitDecorManager(Configuration configuration, IconProvider iconProvider, - SurfaceSession surfaceSession) { + public SplitDecorManager(Configuration configuration, IconProvider iconProvider) { super(configuration, null /* rootSurface */, null /* hostInputToken */); mIconProvider = iconProvider; - mSurfaceSession = surfaceSession; } @Override protected SurfaceControl getParentSurface(IWindow window, WindowManager.LayoutParams attrs) { // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later. - final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) + final SurfaceControl.Builder builder = new SurfaceControl.Builder() .setContainerLayer() .setName(TAG) .setHidden(true) @@ -238,7 +234,7 @@ public class SplitDecorManager extends WindowlessWindowManager { if (mBackgroundLeash == null) { mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash, - RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession); + RESIZING_BACKGROUND_SURFACE_NAME); t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask)) .setLayer(mBackgroundLeash, Integer.MAX_VALUE - 1); } @@ -248,7 +244,7 @@ public class SplitDecorManager extends WindowlessWindowManager { final int left = isLandscape ? mOldMainBounds.width() : 0; final int top = isLandscape ? 0 : mOldMainBounds.height(); mGapBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash, - GAP_BACKGROUND_SURFACE_NAME, mSurfaceSession); + GAP_BACKGROUND_SURFACE_NAME); // Fill up another side bounds area. t.setColor(mGapBackgroundLeash, getResizingBackgroundColor(resizingTask)) .setLayer(mGapBackgroundLeash, Integer.MAX_VALUE - 2) @@ -405,7 +401,7 @@ public class SplitDecorManager extends WindowlessWindowManager { if (mBackgroundLeash == null) { // Initialize background mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash, - RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession); + RESIZING_BACKGROUND_SURFACE_NAME); t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask)) .setLayer(mBackgroundLeash, Integer.MAX_VALUE - 1); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java index 46c1a43f9efe..c5f19742c803 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java @@ -36,7 +36,6 @@ import android.view.InsetsState; import android.view.LayoutInflater; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; -import android.view.SurfaceSession; import android.view.WindowManager; import android.view.WindowlessWindowManager; @@ -98,7 +97,7 @@ public final class SplitWindowManager extends WindowlessWindowManager { @Override protected SurfaceControl getParentSurface(IWindow window, WindowManager.LayoutParams attrs) { // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later. - final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) + final SurfaceControl.Builder builder = new SurfaceControl.Builder() .setContainerLayer() .setName(TAG) .setHidden(true) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java index 0564c95aef5c..d2b4f1ab6b0d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java @@ -38,7 +38,6 @@ import android.util.Log; import android.view.IWindow; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; -import android.view.SurfaceSession; import android.view.View; import android.view.WindowManager; import android.view.WindowlessWindowManager; @@ -173,7 +172,7 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana @Override protected SurfaceControl getParentSurface(IWindow window, WindowManager.LayoutParams attrs) { String className = getClass().getSimpleName(); - final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) + final SurfaceControl.Builder builder = new SurfaceControl.Builder() .setContainerLayer() .setName(className + "Leash") .setHidden(false) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIComponent.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIComponent.kt index 831b331a11e9..abc26cfb3e13 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIComponent.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/api/CompatUIComponent.kt @@ -24,7 +24,6 @@ import android.os.Binder import android.view.IWindow import android.view.SurfaceControl import android.view.SurfaceControlViewHost -import android.view.SurfaceSession import android.view.View import android.view.WindowManager import android.view.WindowlessWindowManager @@ -106,7 +105,7 @@ class CompatUIComponent( attrs: WindowManager.LayoutParams ): SurfaceControl? { val className = javaClass.simpleName - val builder = SurfaceControl.Builder(SurfaceSession()) + val builder = SurfaceControl.Builder() .setContainerLayer() .setName(className + "Leash") .setHidden(false) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 02ecfd983d73..7054c17cfeb0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -38,6 +38,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.activityembedding.ActivityEmbeddingController; import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser; +import com.android.wm.shell.apptoweb.AssistContentRequester; import com.android.wm.shell.bubbles.BubbleController; import com.android.wm.shell.bubbles.BubbleData; import com.android.wm.shell.bubbles.BubbleDataRepository; @@ -240,6 +241,7 @@ public abstract class WMShellModule { RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, InteractionJankMonitor interactionJankMonitor, AppToWebGenericLinksParser genericLinksParser, + AssistContentRequester assistContentRequester, MultiInstanceHelper multiInstanceHelper, Optional<DesktopTasksLimiter> desktopTasksLimiter, Optional<DesktopActivityOrientationChangeHandler> desktopActivityOrientationHandler) { @@ -263,6 +265,7 @@ public abstract class WMShellModule { rootTaskDisplayAreaOrganizer, interactionJankMonitor, genericLinksParser, + assistContentRequester, multiInstanceHelper, desktopTasksLimiter, desktopActivityOrientationHandler); @@ -291,6 +294,15 @@ public abstract class WMShellModule { return new AppToWebGenericLinksParser(context, mainExecutor); } + @Provides + static AssistContentRequester provideAssistContentRequester( + Context context, + @ShellMainThread ShellExecutor shellExecutor, + @ShellBackgroundThread ShellExecutor bgExecutor + ) { + return new AssistContentRequester(context, shellExecutor, bgExecutor); + } + // // Freeform // diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java index bfc0ee803591..72619195fb3f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java @@ -45,6 +45,7 @@ import android.view.animation.DecelerateInterpolator; import androidx.annotation.VisibleForTesting; +import com.android.internal.policy.SystemBarUtils; import com.android.wm.shell.R; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.common.DisplayController; @@ -173,8 +174,7 @@ public class DesktopModeVisualIndicator { final Region region = new Region(); int transitionHeight = mDragStartState == DragStartState.FROM_FREEFORM || mDragStartState == DragStartState.DRAGGED_INTENT - ? mContext.getResources().getDimensionPixelSize( - com.android.wm.shell.R.dimen.desktop_mode_transition_region_thickness) + ? SystemBarUtils.getStatusBarHeight(mContext) : 2 * layout.stableInsets().top; // A Rect at the top of the screen that takes up the center 40%. if (mDragStartState == DragStartState.FROM_FREEFORM) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java index 71cc8df80cad..422656c6d387 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java @@ -38,7 +38,6 @@ import android.view.IWindow; import android.view.LayoutInflater; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; -import android.view.SurfaceSession; import android.view.View; import android.view.WindowManager; import android.view.WindowlessWindowManager; @@ -105,7 +104,7 @@ public final class BackgroundWindowManager extends WindowlessWindowManager { @Override protected SurfaceControl getParentSurface(IWindow window, WindowManager.LayoutParams attrs) { - final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) + final SurfaceControl.Builder builder = new SurfaceControl.Builder() .setColorLayer() .setBufferSize(mDisplayBounds.width(), mDisplayBounds.height()) .setFormat(PixelFormat.RGB_888) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index 4df649ca8c93..f060158766fe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -497,13 +497,8 @@ public class PipAnimationController { mCurrentValue = value; } - boolean shouldApplyCornerRadius() { - return !isOutPipDirection(mTransitionDirection); - } - boolean shouldApplyShadowRadius() { - return !isOutPipDirection(mTransitionDirection) - && !isRemovePipDirection(mTransitionDirection); + return !isRemovePipDirection(mTransitionDirection); } boolean inScaleTransition() { @@ -556,7 +551,7 @@ public class PipAnimationController { final float alpha = getStartValue() * (1 - fraction) + getEndValue() * fraction; setCurrentValue(alpha); getSurfaceTransactionHelper().alpha(tx, leash, alpha) - .round(tx, leash, shouldApplyCornerRadius()) + .round(tx, leash, true /* applyCornerRadius */) .shadow(tx, leash, shouldApplyShadowRadius()); if (!handlePipTransaction(leash, tx, destinationBounds, alpha)) { tx.apply(); @@ -572,7 +567,7 @@ public class PipAnimationController { getSurfaceTransactionHelper() .resetScale(tx, leash, getDestinationBounds()) .crop(tx, leash, getDestinationBounds()) - .round(tx, leash, shouldApplyCornerRadius()) + .round(tx, leash, true /* applyCornerRadius */) .shadow(tx, leash, shouldApplyShadowRadius()); tx.show(leash); tx.apply(); @@ -686,13 +681,11 @@ public class PipAnimationController { getSurfaceTransactionHelper().scaleAndCrop(tx, leash, adjustedSourceRectHint, initialSourceValue, bounds, insets, isInPipDirection, fraction); - if (shouldApplyCornerRadius()) { - final Rect sourceBounds = new Rect(initialContainerRect); - sourceBounds.inset(insets); - getSurfaceTransactionHelper() - .round(tx, leash, sourceBounds, bounds) - .shadow(tx, leash, shouldApplyShadowRadius()); - } + final Rect sourceBounds = new Rect(initialContainerRect); + sourceBounds.inset(insets); + getSurfaceTransactionHelper() + .round(tx, leash, sourceBounds, bounds) + .shadow(tx, leash, shouldApplyShadowRadius()); } if (!handlePipTransaction(leash, tx, bounds, /* alpha= */ 1f)) { tx.apply(); @@ -741,11 +734,9 @@ public class PipAnimationController { .rotateAndScaleWithCrop(tx, leash, initialContainerRect, bounds, insets, degree, x, y, isOutPipDirection, rotationDelta == ROTATION_270 /* clockwise */); - if (shouldApplyCornerRadius()) { - getSurfaceTransactionHelper() - .round(tx, leash, sourceBounds, bounds) - .shadow(tx, leash, shouldApplyShadowRadius()); - } + getSurfaceTransactionHelper() + .round(tx, leash, sourceBounds, bounds) + .shadow(tx, leash, shouldApplyShadowRadius()); if (!handlePipTransaction(leash, tx, bounds, 1f /* alpha */)) { tx.apply(); } @@ -761,7 +752,7 @@ public class PipAnimationController { void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) { getSurfaceTransactionHelper() .alpha(tx, leash, 1f) - .round(tx, leash, shouldApplyCornerRadius()) + .round(tx, leash, true /* applyCornerRadius */) .shadow(tx, leash, shouldApplyShadowRadius()); tx.show(leash); tx.apply(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java deleted file mode 100644 index 1dad4137a9df..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2020 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.splitscreen; - -import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN; - -import android.content.Context; -import android.view.SurfaceSession; -import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; - -import com.android.internal.protolog.ProtoLog; -import com.android.launcher3.icons.IconProvider; -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.windowdecor.WindowDecorViewModel; - -import java.util.Optional; - -/** - * Main stage for split-screen mode. When split-screen is active all standard activity types launch - * on the main stage, except for task that are explicitly pinned to the {@link StageTaskListener}. - * @see StageCoordinator - */ -class MainStage extends StageTaskListener { - private boolean mIsActive = false; - - MainStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId, - StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, - SurfaceSession surfaceSession, IconProvider iconProvider, - Optional<WindowDecorViewModel> windowDecorViewModel) { - super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, - iconProvider, windowDecorViewModel); - } - - boolean isActive() { - return mIsActive; - } - - void activate(WindowContainerTransaction wct, boolean includingTopTask) { - if (mIsActive) return; - ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "activate: main stage includingTopTask=%b", - includingTopTask); - - if (includingTopTask) { - reparentTopTask(wct); - } - - mIsActive = true; - } - - void deactivate(WindowContainerTransaction wct) { - deactivate(wct, false /* toTop */); - } - - void deactivate(WindowContainerTransaction wct, boolean toTop) { - if (!mIsActive) return; - ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "deactivate: main stage toTop=%b rootTaskInfo=%s", - toTop, mRootTaskInfo); - mIsActive = false; - - if (mRootTaskInfo == null) return; - final WindowContainerToken rootToken = mRootTaskInfo.token; - wct.reparentTasks( - rootToken, - null /* newParent */, - null /* windowingModes */, - null /* activityTypes */, - toTop); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java index 526c1d4a179d..b36b1f84d21f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java @@ -44,7 +44,7 @@ public interface SplitScreen { int STAGE_TYPE_UNDEFINED = -1; /** * The main stage type. - * @see MainStage + * @see StageTaskListener */ int STAGE_TYPE_MAIN = 0; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 7e165afce7d4..793e2aa757a3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -61,7 +61,6 @@ import android.view.IRemoteAnimationRunner; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; -import android.view.SurfaceSession; import android.view.WindowManager; import android.widget.Toast; import android.window.RemoteTransition; @@ -897,7 +896,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, private SurfaceControl reparentSplitTasksForAnimation(RemoteAnimationTarget[] apps, SurfaceControl.Transaction t, String callsite) { - final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) + final SurfaceControl.Builder builder = new SurfaceControl.Builder() .setContainerLayer() .setName("RecentsAnimationSplitTasks") .setHidden(false) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index f3959cca050b..dad0d4eb4d8d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -34,6 +34,7 @@ import static android.view.WindowManager.transitTypeToString; import static android.window.TransitionInfo.FLAG_IS_DISPLAY; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; +import static com.android.wm.shell.Flags.enableFlexibleSplit; import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER; import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition; import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage; @@ -103,7 +104,6 @@ import android.view.IRemoteAnimationRunner; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; -import android.view.SurfaceSession; import android.view.WindowManager; import android.widget.Toast; import android.window.DisplayAreaInfo; @@ -154,14 +154,12 @@ import java.util.Set; import java.util.concurrent.Executor; /** - * Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and - * other stages. + * Coordinates the staging (visibility, sizing, ...) of the split-screen stages. * Some high-level rules: * - The {@link StageCoordinator} is only considered active if the other stages contain at * least one child task. - * - The {@link MainStage} should only have children if the coordinator is active. - * - The {@link SplitLayout} divider is only visible if both the {@link MainStage} - * and other stages are visible. + * - The {@link SplitLayout} divider is only visible if multiple {@link StageTaskListener}s are + * visible * - Both stages are put under a single-top root task. * This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and * {@link #onStageHasChildrenChanged(StageListenerImpl).} @@ -172,9 +170,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private static final String TAG = StageCoordinator.class.getSimpleName(); - private final SurfaceSession mSurfaceSession = new SurfaceSession(); - - private final MainStage mMainStage; + private final StageTaskListener mMainStage; private final StageListenerImpl mMainStageListener = new StageListenerImpl(); private final StageTaskListener mSideStage; private final StageListenerImpl mSideStageListener = new StageListenerImpl(); @@ -329,13 +325,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */); ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Creating main/side root task"); - mMainStage = new MainStage( + mMainStage = new StageTaskListener( mContext, mTaskOrganizer, mDisplayId, mMainStageListener, mSyncQueue, - mSurfaceSession, iconProvider, mWindowDecorViewModel); mSideStage = new StageTaskListener( @@ -344,7 +339,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mDisplayId, mSideStageListener, mSyncQueue, - mSurfaceSession, iconProvider, mWindowDecorViewModel); mDisplayController = displayController; @@ -367,8 +361,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @VisibleForTesting StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, - ShellTaskOrganizer taskOrganizer, MainStage mainStage, StageTaskListener sideStage, - DisplayController displayController, DisplayImeController displayImeController, + ShellTaskOrganizer taskOrganizer, StageTaskListener mainStage, + StageTaskListener sideStage, DisplayController displayController, + DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool, ShellExecutor mainExecutor, Handler mainHandler, Optional<RecentTasksController> recentTasks, @@ -420,6 +415,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return mSideStageListener.mVisible && mMainStageListener.mVisible; } + private void activateSplit(WindowContainerTransaction wct, boolean includingTopTask) { + mMainStage.activate(wct, includingTopTask); + } + public boolean isSplitActive() { return mMainStage.isActive(); } @@ -505,10 +504,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "removeFromSideStage: task=%d", taskId); final WindowContainerTransaction wct = new WindowContainerTransaction(); - /** - * {@link MainStage} will be deactivated in {@link #onStageHasChildrenChanged} if the - * other stages no longer have children. - */ + + // MainStage will be deactivated in onStageHasChildrenChanged() if the other stages + // no longer have children. + final boolean result = mSideStage.removeTask(taskId, isSplitActive() ? mMainStage.mRootTaskInfo.token : null, wct); @@ -805,7 +804,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (!isSplitActive()) { // Build a request WCT that will launch both apps such that task 0 is on the main stage // while task 1 is on the side stage. - mMainStage.activate(wct, false /* reparent */); + activateSplit(wct, false /* reparentToTop */); } mSplitLayout.setDivideRatio(snapPosition); updateWindowBounds(mSplitLayout, wct); @@ -872,7 +871,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (!isSplitActive()) { // Build a request WCT that will launch both apps such that task 0 is on the main stage // while task 1 is on the side stage. - mMainStage.activate(wct, false /* reparent */); + activateSplit(wct, false /* reparentToTop */); } setSideStagePosition(splitPosition, wct); @@ -1439,7 +1438,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, setSideStagePosition(startPosition, wct); mSideStage.addTask(taskInfo, wct); } - mMainStage.activate(wct, true /* includingTopTask */); + activateSplit(wct, true /* reparentToTop */); prepareSplitLayout(wct, resizeAnim); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index 459355305280..d64c0a24be68 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -39,7 +39,6 @@ import android.util.Slog; import android.util.SparseArray; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; -import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; @@ -72,6 +71,10 @@ import java.util.function.Predicate; public class StageTaskListener implements ShellTaskOrganizer.TaskListener { private static final String TAG = StageTaskListener.class.getSimpleName(); + // No current way to enforce this but if enableFlexibleSplit() is enabled, then only 1 of the + // stages should have this be set/being used + private boolean mIsActive; + /** Callback interface for listening to changes in a split-screen stage. */ public interface StageListenerCallbacks { void onRootTaskAppeared(); @@ -89,7 +92,6 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { private final Context mContext; private final StageListenerCallbacks mCallbacks; - private final SurfaceSession mSurfaceSession; private final SyncTransactionQueue mSyncQueue; private final IconProvider mIconProvider; private final Optional<WindowDecorViewModel> mWindowDecorViewModel; @@ -104,12 +106,11 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { StageTaskListener(Context context, ShellTaskOrganizer taskOrganizer, int displayId, StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, - SurfaceSession surfaceSession, IconProvider iconProvider, + IconProvider iconProvider, Optional<WindowDecorViewModel> windowDecorViewModel) { mContext = context; mCallbacks = callbacks; mSyncQueue = syncQueue; - mSurfaceSession = surfaceSession; mIconProvider = iconProvider; mWindowDecorViewModel = windowDecorViewModel; taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this); @@ -199,12 +200,11 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { mRootTaskInfo = taskInfo; mSplitDecorManager = new SplitDecorManager( mRootTaskInfo.configuration, - mIconProvider, - mSurfaceSession); + mIconProvider); mCallbacks.onRootTaskAppeared(); sendStatusChanged(); mSyncQueue.runInSync(t -> mDimLayer = - SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession)); + SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer")); } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) { final int taskId = taskInfo.taskId; mChildrenLeashes.put(taskId, leash); @@ -475,6 +475,44 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { }); } + // --------- + // Previously only used in MainStage + boolean isActive() { + return mIsActive; + } + + void activate(WindowContainerTransaction wct, boolean includingTopTask) { + if (mIsActive) return; + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "activate: includingTopTask=%b", + includingTopTask); + + if (includingTopTask) { + reparentTopTask(wct); + } + + mIsActive = true; + } + + void deactivate(WindowContainerTransaction wct) { + deactivate(wct, false /* toTop */); + } + + void deactivate(WindowContainerTransaction wct, boolean toTop) { + if (!mIsActive) return; + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "deactivate: toTop=%b rootTaskInfo=%s", + toTop, mRootTaskInfo); + mIsActive = false; + + if (mRootTaskInfo == null) return; + final WindowContainerToken rootToken = mRootTaskInfo.token; + wct.reparentTasks( + rootToken, + null /* newParent */, + null /* windowingModes */, + null /* activityTypes */, + toTop); + } + // -------- // Previously only used in SideStage boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java index fac3592896ea..2e9b53eee13f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java @@ -33,7 +33,6 @@ import android.hardware.display.DisplayManager; import android.util.SparseArray; import android.view.IWindow; import android.view.SurfaceControl; -import android.view.SurfaceSession; import android.view.WindowManager; import android.view.WindowlessWindowManager; import android.window.SplashScreenView; @@ -204,7 +203,7 @@ public class StartingSurfaceDrawer { @Override protected SurfaceControl getParentSurface(IWindow window, WindowManager.LayoutParams attrs) { - final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) + final SurfaceControl.Builder builder = new SurfaceControl.Builder() .setContainerLayer() .setName("Windowless window") .setHidden(false) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 4fc6c4489f2b..ff4b981f5e8e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -92,7 +92,6 @@ import android.os.UserHandle; import android.util.ArrayMap; import android.view.Choreographer; import android.view.SurfaceControl; -import android.view.SurfaceSession; import android.view.WindowManager; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; @@ -134,8 +133,6 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { private final TransitionAnimation mTransitionAnimation; private final DevicePolicyManager mDevicePolicyManager; - private final SurfaceSession mSurfaceSession = new SurfaceSession(); - /** Keeps track of the currently-running animations associated with each transition. */ private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>(); @@ -705,7 +702,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { TransitionInfo.Change change, TransitionInfo info, int animHint, ArrayList<Animator> animations, Runnable onAnimFinish) { final int rootIdx = TransitionUtil.rootIndexFor(change, info); - final ScreenRotationAnimation anim = new ScreenRotationAnimation(mContext, mSurfaceSession, + final ScreenRotationAnimation anim = new ScreenRotationAnimation(mContext, mTransactionPool, startTransaction, change, info.getRoot(rootIdx).getLeash(), animHint); // The rotation animation may consist of 3 animations: fade-out screenshot, fade-in real @@ -918,7 +915,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); - final WindowThumbnail wt = WindowThumbnail.createAndAttach(mSurfaceSession, + final WindowThumbnail wt = WindowThumbnail.createAndAttach( change.getLeash(), thumbnail, transaction); final Animation a = mTransitionAnimation.createCrossProfileAppsThumbnailAnimationLocked(bounds); @@ -943,7 +940,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { @NonNull Runnable finishCallback, TransitionInfo.Change change, TransitionInfo.AnimationOptions options, float cornerRadius) { final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); - final WindowThumbnail wt = WindowThumbnail.createAndAttach(mSurfaceSession, + final WindowThumbnail wt = WindowThumbnail.createAndAttach( change.getLeash(), options.getThumbnail(), transaction); final Rect bounds = change.getEndAbsBounds(); final int orientation = mContext.getResources().getConfiguration().orientation; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java index 3d79a1c8cebe..c385f9afcf3a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java @@ -74,8 +74,12 @@ public class HomeTransitionObserver implements TransitionObserver, final boolean isBackGesture = change.hasFlags(FLAG_BACK_GESTURE_ANIMATED); if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) { if (Flags.migratePredictiveBackTransition()) { - if (!isBackGesture && TransitionUtil.isOpenOrCloseMode(mode)) { - notifyHomeVisibilityChanged(TransitionUtil.isOpeningType(mode)); + final boolean gestureToHomeTransition = isBackGesture + && TransitionUtil.isClosingType(info.getType()); + if (gestureToHomeTransition + || (!isBackGesture && TransitionUtil.isOpenOrCloseMode(mode))) { + notifyHomeVisibilityChanged(gestureToHomeTransition + || TransitionUtil.isOpeningType(mode)); } } else { if (TransitionUtil.isOpenOrCloseMode(mode) || isBackGesture) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java index 0bf9d368ab74..5802e2ca8133 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java @@ -38,7 +38,6 @@ import android.util.Slog; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; -import android.view.SurfaceSession; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.window.ScreenCapture; @@ -112,7 +111,7 @@ class ScreenRotationAnimation { /** Intensity of light/whiteness of the layout after rotation occurs. */ private float mEndLuma; - ScreenRotationAnimation(Context context, SurfaceSession session, TransactionPool pool, + ScreenRotationAnimation(Context context, TransactionPool pool, Transaction t, TransitionInfo.Change change, SurfaceControl rootLeash, int animHint) { mContext = context; mTransactionPool = pool; @@ -126,7 +125,7 @@ class ScreenRotationAnimation { mStartRotation = change.getStartRotation(); mEndRotation = change.getEndRotation(); - mAnimLeash = new SurfaceControl.Builder(session) + mAnimLeash = new SurfaceControl.Builder() .setParent(rootLeash) .setEffectLayer() .setCallsite("ShellRotationAnimation") @@ -153,7 +152,7 @@ class ScreenRotationAnimation { return; } - mScreenshotLayer = new SurfaceControl.Builder(session) + mScreenshotLayer = new SurfaceControl.Builder() .setParent(mAnimLeash) .setBLASTLayer() .setSecure(screenshotBuffer.containsSecureLayers()) @@ -178,7 +177,7 @@ class ScreenRotationAnimation { t.setCrop(mSurfaceControl, new Rect(0, 0, mEndWidth, mEndHeight)); if (!isCustomRotate()) { - mBackColorSurface = new SurfaceControl.Builder(session) + mBackColorSurface = new SurfaceControl.Builder() .setParent(rootLeash) .setColorLayer() .setOpaque(true) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java index 2c668ed3d84d..341f2bc66716 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/WindowThumbnail.java @@ -21,7 +21,6 @@ import android.graphics.GraphicBuffer; import android.graphics.PixelFormat; import android.hardware.HardwareBuffer; import android.view.SurfaceControl; -import android.view.SurfaceSession; /** * Represents a surface that is displayed over a transition surface. @@ -33,10 +32,10 @@ class WindowThumbnail { private WindowThumbnail() {} /** Create a thumbnail surface and attach it over a parent surface. */ - static WindowThumbnail createAndAttach(SurfaceSession surfaceSession, SurfaceControl parent, + static WindowThumbnail createAndAttach(SurfaceControl parent, HardwareBuffer thumbnailHeader, SurfaceControl.Transaction t) { WindowThumbnail windowThumbnail = new WindowThumbnail(); - windowThumbnail.mSurfaceControl = new SurfaceControl.Builder(surfaceSession) + windowThumbnail.mSurfaceControl = new SurfaceControl.Builder() .setParent(parent) .setName("WindowThumanil : " + parent.toString()) .setCallsite("WindowThumanil") diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index c88c1e28b011..79190689adc1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -90,6 +90,7 @@ import com.android.wm.shell.R; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser; +import com.android.wm.shell.apptoweb.AssistContentRequester; import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayInsetsController; @@ -182,6 +183,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final Region mExclusionRegion = Region.obtain(); private boolean mInImmersiveMode; private final String mSysUIPackageName; + private final AssistContentRequester mAssistContentRequester; private final DisplayChangeController.OnDisplayChangingListener mOnDisplayChangingListener; private final ISystemGestureExclusionListener mGestureExclusionListener = @@ -217,6 +219,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, InteractionJankMonitor interactionJankMonitor, AppToWebGenericLinksParser genericLinksParser, + AssistContentRequester assistContentRequester, MultiInstanceHelper multiInstanceHelper, Optional<DesktopTasksLimiter> desktopTasksLimiter, Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler @@ -238,6 +241,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { transitions, desktopTasksController, genericLinksParser, + assistContentRequester, multiInstanceHelper, new DesktopModeWindowDecoration.Factory(), new InputMonitorFactory(), @@ -267,6 +271,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { Transitions transitions, Optional<DesktopTasksController> desktopTasksController, AppToWebGenericLinksParser genericLinksParser, + AssistContentRequester assistContentRequester, MultiInstanceHelper multiInstanceHelper, DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory, InputMonitorFactory inputMonitorFactory, @@ -304,6 +309,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mInteractionJankMonitor = interactionJankMonitor; mDesktopTasksLimiter = desktopTasksLimiter; mActivityOrientationChangeHandler = activityOrientationChangeHandler; + mAssistContentRequester = assistContentRequester; mOnDisplayChangingListener = (displayId, fromRotation, toRotation, displayAreaInfo, t) -> { DesktopModeWindowDecoration decoration; RunningTaskInfo taskInfo; @@ -626,7 +632,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } else if (id == R.id.caption_handle || id == R.id.open_menu_button) { if (!decoration.isHandleMenuActive()) { moveTaskToFront(decoration.mTaskInfo); - decoration.createHandleMenu(mSplitScreenController); + decoration.createHandleMenu(); } } else if (id == R.id.maximize_window) { // TODO(b/346441962): move click detection logic into the decor's @@ -1270,6 +1276,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mSyncQueue, mRootTaskDisplayAreaOrganizer, mGenericLinksParser, + mAssistContentRequester, mMultiInstanceHelper); mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index aa43c8dab2ad..142be91fe942 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -38,6 +38,7 @@ import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.WindowConfiguration.WindowingMode; +import android.app.assist.AssistContent; import android.content.ComponentName; import android.content.Context; import android.content.pm.ActivityInfo; @@ -75,6 +76,8 @@ import com.android.wm.shell.R; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser; +import com.android.wm.shell.apptoweb.AppToWebUtils; +import com.android.wm.shell.apptoweb.AssistContentRequester; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.MultiInstanceHelper; @@ -150,6 +153,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private CharSequence mAppName; private CapturedLink mCapturedLink; private Uri mGenericLink; + private Uri mWebUri; private Consumer<Uri> mOpenInBrowserClickListener; private ExclusionRegionListener mExclusionRegionListener; @@ -158,6 +162,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private final MaximizeMenuFactory mMaximizeMenuFactory; private final HandleMenuFactory mHandleMenuFactory; private final AppToWebGenericLinksParser mGenericLinksParser; + private final AssistContentRequester mAssistContentRequester; // Hover state for the maximize menu and button. The menu will remain open as long as either of // these is true. See {@link #onMaximizeHoverStateChanged()}. @@ -184,16 +189,16 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin SyncTransactionQueue syncQueue, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, AppToWebGenericLinksParser genericLinksParser, + AssistContentRequester assistContentRequester, MultiInstanceHelper multiInstanceHelper) { this (context, userContext, displayController, splitScreenController, taskOrganizer, taskInfo, taskSurface, handler, bgExecutor, choreographer, syncQueue, - rootTaskDisplayAreaOrganizer, genericLinksParser, SurfaceControl.Builder::new, - SurfaceControl.Transaction::new, WindowContainerTransaction::new, - SurfaceControl::new, new WindowManagerWrapper( + rootTaskDisplayAreaOrganizer, genericLinksParser, assistContentRequester, + SurfaceControl.Builder::new, SurfaceControl.Transaction::new, + WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper( context.getSystemService(WindowManager.class)), - new SurfaceControlViewHostFactory() {}, - DefaultMaximizeMenuFactory.INSTANCE, DefaultHandleMenuFactory.INSTANCE, - multiInstanceHelper); + new SurfaceControlViewHostFactory() {}, DefaultMaximizeMenuFactory.INSTANCE, + DefaultHandleMenuFactory.INSTANCE, multiInstanceHelper); } DesktopModeWindowDecoration( @@ -210,6 +215,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin SyncTransactionQueue syncQueue, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, AppToWebGenericLinksParser genericLinksParser, + AssistContentRequester assistContentRequester, Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier, Supplier<WindowContainerTransaction> windowContainerTransactionSupplier, @@ -230,6 +236,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mSyncQueue = syncQueue; mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; mGenericLinksParser = genericLinksParser; + mAssistContentRequester = assistContentRequester; mMaximizeMenuFactory = maximizeMenuFactory; mHandleMenuFactory = handleMenuFactory; mMultiInstanceHelper = multiInstanceHelper; @@ -478,10 +485,18 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin @Nullable private Uri getBrowserLink() { + // Do not show browser link in browser applications + final ComponentName baseActivity = mTaskInfo.baseActivity; + if (baseActivity != null && AppToWebUtils.isBrowserApp(mContext, + baseActivity.getPackageName(), mUserContext.getUserId())) { + return null; + } // If the captured link is available and has not expired, return the captured link. // Otherwise, return the generic link which is set to null if a generic link is unavailable. if (mCapturedLink != null && !mCapturedLink.mExpired) { return mCapturedLink.mUri; + } else if (mWebUri != null) { + return mWebUri; } return mGenericLink; } @@ -987,18 +1002,32 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } /** - * Create and display handle menu window. + * Updates app info and creates and displays handle menu window. */ - void createHandleMenu(SplitScreenController splitScreenController) { + void createHandleMenu() { + // Requests assist content. When content is received, calls {@link #onAssistContentReceived} + // which sets app info and creates the handle menu. + mAssistContentRequester.requestAssistContent( + mTaskInfo.taskId, this::onAssistContentReceived); + } + + /** + * Called when assist content is received. updates the saved links and creates the handle menu. + */ + @VisibleForTesting + void onAssistContentReceived(@Nullable AssistContent assistContent) { + mWebUri = assistContent == null ? null : assistContent.getWebUri(); loadAppInfoIfNeeded(); updateGenericLink(); + + // Create and display handle menu mHandleMenu = mHandleMenuFactory.create( this, mWindowManagerWrapper, mRelayoutParams.mLayoutResId, mAppIconBitmap, mAppName, - splitScreenController, + mSplitScreenController, DesktopModeStatus.canEnterDesktopMode(mContext), Flags.enableDesktopWindowingMultiInstanceFeatures() && mMultiInstanceHelper @@ -1012,6 +1041,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mHandleMenu.show( /* onToDesktopClickListener= */ () -> { mOnToDesktopClickListener.accept(APP_HANDLE_MENU_BUTTON); + mOnToDesktopClickListener.accept(APP_HANDLE_MENU_BUTTON); return Unit.INSTANCE; }, /* onToFullscreenClickListener= */ mOnToFullscreenClickListener, @@ -1333,6 +1363,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin SyncTransactionQueue syncQueue, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, AppToWebGenericLinksParser genericLinksParser, + AssistContentRequester assistContentRequester, MultiInstanceHelper multiInstanceHelper) { return new DesktopModeWindowDecoration( context, @@ -1348,6 +1379,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin syncQueue, rootTaskDisplayAreaOrganizer, genericLinksParser, + assistContentRequester, multiInstanceHelper); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt index fd6c4d8e604d..fb81ed4169ff 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt @@ -30,7 +30,6 @@ import android.view.Display import android.view.LayoutInflater import android.view.SurfaceControl import android.view.SurfaceControlViewHost -import android.view.SurfaceSession import android.view.WindowManager import android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL import android.view.WindowlessWindowManager @@ -66,7 +65,6 @@ class ResizeVeil @JvmOverloads constructor( private val lightColors = dynamicLightColorScheme(context) private val darkColors = dynamicDarkColorScheme(context) - private val surfaceSession = SurfaceSession() private lateinit var iconView: ImageView private var iconSize = 0 @@ -126,7 +124,7 @@ class ResizeVeil @JvmOverloads constructor( .setCallsite("ResizeVeil#setupResizeVeil") .build() backgroundSurface = surfaceControlBuilderFactory - .create("Resize veil background of Task=" + taskInfo.taskId, surfaceSession) + .create("Resize veil background of Task=" + taskInfo.taskId) .setColorLayer() .setHidden(true) .setParent(veilSurface) @@ -399,10 +397,6 @@ class ResizeVeil @JvmOverloads constructor( fun create(name: String): SurfaceControl.Builder { return SurfaceControl.Builder().setName(name) } - - fun create(name: String, surfaceSession: SurfaceSession): SurfaceControl.Builder { - return SurfaceControl.Builder(surfaceSession).setName(name) - } } companion object { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt index dfa5ab415992..9ef4b8cde8ef 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt @@ -29,6 +29,7 @@ import android.view.View.OnClickListener import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS import android.view.WindowManager import android.widget.ImageButton +import com.android.internal.policy.SystemBarUtils import com.android.window.flags.Flags import com.android.wm.shell.R import com.android.wm.shell.shared.animation.Interpolators @@ -74,7 +75,10 @@ internal class AppHandleViewHolder( ) { captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo)) this.taskInfo = taskInfo - if (!isCaptionVisible && hasStatusBarInputLayer()) { + // If handle is not in status bar region(i.e., bottom stage in vertical split), + // do not create an input layer + if (position.y >= SystemBarUtils.getStatusBarHeight(context)) return + if (!isCaptionVisible && hasStatusBarInputLayer() ) { disposeStatusBarInputLayer() return } @@ -120,7 +124,7 @@ internal class AppHandleViewHolder( inputManager.pilferPointers(v.viewRootImpl.inputToken) } captionHandle.dispatchTouchEvent(event) - true + return@setOnTouchListener true } windowManagerWrapper.updateViewLayout(view, lp) } diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt index ec1d4f7854fd..7640cb1fb616 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt @@ -195,6 +195,25 @@ class DesktopModeFlickerScenarios { .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), ) + val CORNER_RESIZE_TO_MAXIMUM_SIZE = + FlickerConfigEntry( + scenarioId = ScenarioId("CORNER_RESIZE_TO_MAXIMUM_SIZE"), + extractor = + TaggedScenarioExtractorBuilder() + .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW) + .setTransitionMatcher( + TaggedCujTransitionMatcher(associatedTransitionRequired = false) + ) + .build(), + assertions = + AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + + listOf( + AppLayerIncreasesInSize(DESKTOP_MODE_APP), + AppWindowHasMaxDisplayHeight(DESKTOP_MODE_APP), + AppWindowHasMaxDisplayWidth(DESKTOP_MODE_APP) + ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + ) + val SNAP_RESIZE_LEFT_WITH_BUTTON = FlickerConfigEntry( scenarioId = ScenarioId("SNAP_RESIZE_LEFT_WITH_BUTTON"), diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizeLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizeLandscape.kt new file mode 100644 index 000000000000..0b98ba2a9cd4 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizeLandscape.kt @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2024 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.flicker + +import android.tools.Rotation +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE_TO_MAXIMUM_SIZE +import com.android.wm.shell.scenarios.ResizeAppWithCornerResize +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Resize app window using corner resize to the greatest possible height and width in + * landscape mode. + * + * Assert that the maximum window size constraint is maintained. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class ResizeAppToMaximumWindowSizeLandscape : ResizeAppWithCornerResize( + rotation = Rotation.ROTATION_90 +) { + @ExpectedScenarios(["CORNER_RESIZE_TO_MAXIMUM_SIZE"]) + @Test + override fun resizeAppWithCornerResizeToMaximumSize() = + super.resizeAppWithCornerResizeToMaximumSize() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CORNER_RESIZE_TO_MAXIMUM_SIZE) + } +} diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizePortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizePortrait.kt new file mode 100644 index 000000000000..b1c04d38a46c --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizePortrait.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 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.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE_TO_MAXIMUM_SIZE +import com.android.wm.shell.scenarios.ResizeAppWithCornerResize +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Resize app window using corner resize to the greatest possible height and width in + * portrait mode. + * + * Assert that the maximum window size constraint is maintained. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class ResizeAppToMaximumWindowSizePortrait : ResizeAppWithCornerResize() { + @ExpectedScenarios(["CORNER_RESIZE_TO_MAXIMUM_SIZE"]) + @Test + override fun resizeAppWithCornerResizeToMaximumSize() = + super.resizeAppWithCornerResizeToMaximumSize() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CORNER_RESIZE_TO_MAXIMUM_SIZE) + } +} diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/CloseAllAppsWithAppHeaderExitTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/CloseAllAppsWithAppHeaderExitTest.kt new file mode 100644 index 000000000000..a4dc52beb85d --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/CloseAllAppsWithAppHeaderExitTest.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 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.functional + +import android.platform.test.annotations.Postsubmit +import com.android.wm.shell.scenarios.CloseAllAppsWithAppHeaderExit +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner + +/* Functional test for [CloseAllAppsWithAppHeaderExit]. */ +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +class CloseAllAppsWithAppHeaderExitTest() : CloseAllAppsWithAppHeaderExit() diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragTest.kt new file mode 100644 index 000000000000..3d95f97c09ef --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragTest.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 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.functional + +import android.platform.test.annotations.Postsubmit +import com.android.wm.shell.scenarios.EnterDesktopWithDrag +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner + +/* Functional test for [EnterDesktopWithDrag]. */ +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +class EnterDesktopWithDragTest : EnterDesktopWithDrag() diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ExitDesktopWithDragToTopDragZoneTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ExitDesktopWithDragToTopDragZoneTest.kt new file mode 100644 index 000000000000..140c5ec15812 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ExitDesktopWithDragToTopDragZoneTest.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 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.functional + +import android.platform.test.annotations.Postsubmit +import com.android.wm.shell.scenarios.ExitDesktopWithDragToTopDragZone +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner + +/* Functional test for [ExitDesktopWithDragToTopDragZone]. */ +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +class ExitDesktopWithDragToTopDragZoneTest : ExitDesktopWithDragToTopDragZone() diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MaximizeAppWindowTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MaximizeAppWindowTest.kt new file mode 100644 index 000000000000..3d3dcd09cc63 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MaximizeAppWindowTest.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 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.functional + +import android.platform.test.annotations.Postsubmit +import com.android.wm.shell.scenarios.MaximizeAppWindow +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner + +/* Functional test for [MaximizeAppWindow]. */ +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +class MaximizeAppWindowTest : MaximizeAppWindow() diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenAppsInDesktopModeTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenAppsInDesktopModeTest.kt new file mode 100644 index 000000000000..263e89f69e5a --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenAppsInDesktopModeTest.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 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.functional + +import android.platform.test.annotations.Postsubmit +import com.android.wm.shell.scenarios.OpenAppsInDesktopMode +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner + +/* Functional test for [OpenAppsInDesktopMode]. */ +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +class OpenAppsInDesktopModeTest : OpenAppsInDesktopMode() diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowAndPipTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowAndPipTest.kt new file mode 100644 index 000000000000..13f4775c1074 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowAndPipTest.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 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.functional + +import android.platform.test.annotations.Postsubmit +import com.android.wm.shell.scenarios.ResizeAppCornerMultiWindowAndPip +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner + +/* Functional test for [ResizeAppCornerMultiWindowAndPip]. */ +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +class ResizeAppCornerMultiWindowAndPipTest : ResizeAppCornerMultiWindowAndPip() diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowTest.kt new file mode 100644 index 000000000000..bc9bb41bf320 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowTest.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 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.functional + +import android.platform.test.annotations.Postsubmit +import com.android.wm.shell.scenarios.ResizeAppCornerMultiWindow +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner + +/* Functional test for [ResizeAppCornerMultiWindow]. */ +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +class ResizeAppCornerMultiWindowTest : ResizeAppCornerMultiWindow() diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithCornerResizeTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithCornerResizeTest.kt new file mode 100644 index 000000000000..46168eb7c002 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithCornerResizeTest.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 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.functional + +import android.platform.test.annotations.Postsubmit +import com.android.wm.shell.scenarios.ResizeAppWithCornerResize +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner + +/* Functional test for [ResizeAppWithCornerResize]. */ +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +class ResizeAppWithCornerResizeTest : ResizeAppWithCornerResize() diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithEdgeResizeTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithEdgeResizeTest.kt new file mode 100644 index 000000000000..ee2420021339 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithEdgeResizeTest.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 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.functional + +import android.platform.test.annotations.Postsubmit +import com.android.server.wm.flicker.helpers.MotionEventHelper +import com.android.wm.shell.scenarios.ResizeAppWithEdgeResize +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner + +/* Functional test for [ResizeAppWithEdgeResize]. */ +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +class ResizeAppWithEdgeResizeTest : + ResizeAppWithEdgeResize(MotionEventHelper.InputMethod.TOUCHPAD) diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithButtonTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithButtonTest.kt new file mode 100644 index 000000000000..38e85c755481 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithButtonTest.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 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.functional + +import android.platform.test.annotations.Postsubmit +import com.android.wm.shell.scenarios.SnapResizeAppWindowWithButton +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner + +/* Functional test for [SnapResizeAppWindowWithButton]. */ +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +class SnapResizeAppWindowWithButtonTest : SnapResizeAppWindowWithButton() diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithDragTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithDragTest.kt new file mode 100644 index 000000000000..082a3fb0e171 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithDragTest.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 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.functional + +import android.platform.test.annotations.Postsubmit +import com.android.wm.shell.scenarios.SnapResizeAppWindowWithDrag +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner + +/* Functional test for [SnapResizeAppWindowWithDrag]. */ +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +class SnapResizeAppWindowWithDragTest : SnapResizeAppWindowWithDrag() diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SwitchToOverviewFromDesktopTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SwitchToOverviewFromDesktopTest.kt new file mode 100644 index 000000000000..fdd0d8144130 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SwitchToOverviewFromDesktopTest.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 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.functional + +import android.platform.test.annotations.Postsubmit +import com.android.wm.shell.scenarios.SwitchToOverviewFromDesktop +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner + +/* Functional test for [SwitchToOverviewFromDesktop]. */ +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +class SwitchToOverviewFromDesktopTest : SwitchToOverviewFromDesktop() diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt index e9056f3c44d4..351a70094654 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.scenarios -import android.platform.test.annotations.Postsubmit import android.app.Instrumentation import android.tools.NavBar import android.tools.Rotation @@ -33,15 +32,12 @@ import com.android.wm.shell.Utils import org.junit.After import org.junit.Assume import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.BlockJUnit4ClassRunner -@RunWith(BlockJUnit4ClassRunner::class) -@Postsubmit -open class CloseAllAppsWithAppHeaderExit -@JvmOverloads +@Ignore("Base Test Class") +abstract class CloseAllAppsWithAppHeaderExit constructor(val rotation: Rotation = Rotation.ROTATION_0) { private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt index ca1dc1a7f658..3f9927f1fab6 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.scenarios -import android.platform.test.annotations.Postsubmit import com.android.server.wm.flicker.helpers.DesktopModeAppHelper import com.android.server.wm.flicker.helpers.ImeAppHelper import com.android.server.wm.flicker.helpers.MailAppHelper @@ -26,13 +25,11 @@ import com.android.window.flags.Flags import org.junit.After import org.junit.Assume import org.junit.Before +import org.junit.Ignore import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.BlockJUnit4ClassRunner -@RunWith(BlockJUnit4ClassRunner::class) -@Postsubmit -open class DragAppWindowMultiWindow : DragAppWindowScenarioTestBase() +@Ignore("Test Base Class") +abstract class DragAppWindowMultiWindow : DragAppWindowScenarioTestBase() { private val imeAppHelper = ImeAppHelper(instrumentation) private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation)) diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt index f4d641411114..967bd29958c2 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.scenarios -import android.platform.test.annotations.Postsubmit import android.tools.NavBar import android.tools.Rotation import android.tools.flicker.rules.ChangeDisplayOrientationRule @@ -25,19 +24,16 @@ import com.android.wm.shell.Utils import org.junit.After import org.junit.Assume import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.BlockJUnit4ClassRunner -@RunWith(BlockJUnit4ClassRunner::class) -@Postsubmit -open class EnterDesktopWithDrag -@JvmOverloads +@Ignore("Test Base Class") +abstract class EnterDesktopWithDrag constructor( val rotation: Rotation = Rotation.ROTATION_0, isResizeable: Boolean = true, - isLandscapeApp: Boolean = true + isLandscapeApp: Boolean = true, ) : DesktopScenarioCustomAppTestBase(isResizeable, isLandscapeApp) { @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation) diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt index b616e5347ac9..824c4482c1e6 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.scenarios -import android.platform.test.annotations.Postsubmit import android.tools.NavBar import android.tools.Rotation import com.android.window.flags.Flags @@ -24,19 +23,16 @@ import com.android.wm.shell.Utils import org.junit.After import org.junit.Assume import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.BlockJUnit4ClassRunner -@RunWith(BlockJUnit4ClassRunner::class) -@Postsubmit -open class ExitDesktopWithDragToTopDragZone -@JvmOverloads +@Ignore("Test Base Class") +abstract class ExitDesktopWithDragToTopDragZone constructor( val rotation: Rotation = Rotation.ROTATION_0, isResizeable: Boolean = true, - isLandscapeApp: Boolean = true + isLandscapeApp: Boolean = true, ) : DesktopScenarioCustomAppTestBase(isResizeable, isLandscapeApp) { @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation) diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt index 426f40b5e81b..a54d497bf511 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MaximizeAppWindow.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.scenarios import android.app.Instrumentation -import android.platform.test.annotations.Postsubmit import android.tools.NavBar import android.tools.Rotation import android.tools.flicker.rules.ChangeDisplayOrientationRule @@ -33,15 +32,12 @@ import com.android.wm.shell.Utils import org.junit.After import org.junit.Assume import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.BlockJUnit4ClassRunner -@RunWith(BlockJUnit4ClassRunner::class) -@Postsubmit -open class MaximizeAppWindow -@JvmOverloads +@Ignore("Test Base Class") +abstract class MaximizeAppWindow constructor(private val rotation: Rotation = Rotation.ROTATION_0, isResizable: Boolean = true) { private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt index a5c794b06c4a..aad266fb8374 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.scenarios import android.app.Instrumentation -import android.platform.test.annotations.Postsubmit import android.tools.flicker.rules.ChangeDisplayOrientationRule import android.tools.NavBar import android.tools.Rotation @@ -36,14 +35,12 @@ import com.android.wm.shell.Utils import org.junit.After import org.junit.Assume import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.BlockJUnit4ClassRunner -@RunWith(BlockJUnit4ClassRunner::class) -@Postsubmit -open class OpenAppsInDesktopMode(val rotation: Rotation = Rotation.ROTATION_0) { +@Ignore("Test Base Class") +abstract class OpenAppsInDesktopMode(val rotation: Rotation = Rotation.ROTATION_0) { private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() private val tapl = LauncherInstrumentation() @@ -83,4 +80,4 @@ open class OpenAppsInDesktopMode(val rotation: Rotation = Rotation.ROTATION_0) { secondApp.exit(wmHelper) firstApp.exit(wmHelper) } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt index b6bca7a94287..bfee3181cbc0 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.scenarios -import android.platform.test.annotations.Postsubmit import android.app.Instrumentation import android.tools.NavBar import android.tools.Rotation @@ -34,15 +33,12 @@ import com.android.wm.shell.Utils import org.junit.After import org.junit.Assume import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.BlockJUnit4ClassRunner -@RunWith(BlockJUnit4ClassRunner::class) -@Postsubmit -open class ResizeAppCornerMultiWindow -@JvmOverloads +@Ignore("Test Base Class") +abstract class ResizeAppCornerMultiWindow constructor(val rotation: Rotation = Rotation.ROTATION_0, val horizontalChange: Int = 50, val verticalChange: Int = -50) { diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt index 285ea13bb9d4..5b1b64e7c562 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.scenarios -import android.platform.test.annotations.Postsubmit import android.app.Instrumentation import android.tools.NavBar import android.tools.Rotation @@ -35,15 +34,12 @@ import com.android.wm.shell.Utils import org.junit.After import org.junit.Assume import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.BlockJUnit4ClassRunner -@RunWith(BlockJUnit4ClassRunner::class) -@Postsubmit -open class ResizeAppCornerMultiWindowAndPip -@JvmOverloads +@Ignore("Test Base Class") +abstract class ResizeAppCornerMultiWindowAndPip constructor(val rotation: Rotation = Rotation.ROTATION_0, val horizontalChange: Int = 50, val verticalChange: Int = -50) { diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt index 42940a99b59f..bd25639466a3 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.scenarios -import android.platform.test.annotations.Postsubmit import android.app.Instrumentation import android.tools.NavBar import android.tools.Rotation @@ -32,16 +31,12 @@ import com.android.wm.shell.Utils import org.junit.After import org.junit.Assume import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.BlockJUnit4ClassRunner -@RunWith(BlockJUnit4ClassRunner::class) -@Postsubmit -open class ResizeAppWithCornerResize -@JvmOverloads -constructor( +@Ignore("Test Base Class") +abstract class ResizeAppWithCornerResize( val rotation: Rotation = Rotation.ROTATION_0, val horizontalChange: Int = 200, val verticalChange: Int = -200, @@ -83,6 +78,25 @@ constructor( ) } + @Test + open fun resizeAppWithCornerResizeToMaximumSize() { + val maxResizeChange = 3000 + testApp.cornerResize( + wmHelper, + device, + DesktopModeAppHelper.Corners.RIGHT_TOP, + maxResizeChange, + -maxResizeChange + ) + testApp.cornerResize( + wmHelper, + device, + DesktopModeAppHelper.Corners.LEFT_BOTTOM, + -maxResizeChange, + maxResizeChange + ) + } + @After fun teardown() { testApp.exit(wmHelper) diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt index d094967e91e0..67802387b267 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.scenarios -import android.platform.test.annotations.Postsubmit import android.app.Instrumentation import android.tools.NavBar import android.tools.Rotation @@ -32,15 +31,12 @@ import com.android.wm.shell.Utils import org.junit.After import org.junit.Assume import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.BlockJUnit4ClassRunner -@RunWith(BlockJUnit4ClassRunner::class) -@Postsubmit -open class ResizeAppWithEdgeResize -@JvmOverloads +@Ignore("Test Base Class") +abstract class ResizeAppWithEdgeResize constructor( val inputMethod: MotionEventHelper.InputMethod, val rotation: Rotation = Rotation.ROTATION_90 diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt index 33242db66f9f..2b40497844ef 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.scenarios import android.app.Instrumentation -import android.platform.test.annotations.Postsubmit import android.tools.NavBar import android.tools.Rotation import android.tools.traces.parsers.WindowManagerStateHelper @@ -32,15 +31,12 @@ import com.android.wm.shell.Utils import org.junit.After import org.junit.Assume import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.BlockJUnit4ClassRunner -@RunWith(BlockJUnit4ClassRunner::class) -@Postsubmit -open class SnapResizeAppWindowWithButton -@JvmOverloads +@Ignore("Test Base Class") +abstract class SnapResizeAppWindowWithButton constructor(private val toLeft: Boolean = true, isResizable: Boolean = true) { private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt index 14eb779165bb..b4bd7e1c5211 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.scenarios import android.app.Instrumentation -import android.platform.test.annotations.Postsubmit import android.tools.NavBar import android.tools.Rotation import android.tools.traces.parsers.WindowManagerStateHelper @@ -32,15 +31,12 @@ import com.android.wm.shell.Utils import org.junit.After import org.junit.Assume import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.BlockJUnit4ClassRunner -@RunWith(BlockJUnit4ClassRunner::class) -@Postsubmit -open class SnapResizeAppWindowWithDrag -@JvmOverloads +@Ignore("Test Base Class") +abstract class SnapResizeAppWindowWithDrag constructor(private val toLeft: Boolean = true, isResizable: Boolean = true) { private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() @@ -72,4 +68,4 @@ constructor(private val toLeft: Boolean = true, isResizable: Boolean = true) { fun teardown() { testApp.exit(wmHelper) } -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt index 53e36e23fd95..dad2eb633c72 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt @@ -16,7 +16,6 @@ package com.android.wm.shell.scenarios -import android.platform.test.annotations.Postsubmit import android.app.Instrumentation import android.tools.NavBar import android.tools.Rotation @@ -31,20 +30,17 @@ import com.android.wm.shell.Utils import org.junit.After import org.junit.Assume import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.BlockJUnit4ClassRunner /** * Base test for opening recent apps overview from desktop mode. * * Navigation mode can be passed as a constructor parameter, by default it is set to gesture navigation. */ -@RunWith(BlockJUnit4ClassRunner::class) -@Postsubmit -open class SwitchToOverviewFromDesktop -@JvmOverloads +@Ignore("Base Test Class") +abstract class SwitchToOverviewFromDesktop constructor(val navigationMode: NavBar = NavBar.MODE_GESTURAL) { private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java index 413e49562435..e514dc38208e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java @@ -49,7 +49,6 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.SparseArray; import android.view.SurfaceControl; -import android.view.SurfaceSession; import android.window.ITaskOrganizer; import android.window.ITaskOrganizerController; import android.window.TaskAppearedInfo; @@ -169,7 +168,7 @@ public class ShellTaskOrganizerTests extends ShellTestCase { public void testTaskLeashReleasedAfterVanished() throws RemoteException { assumeFalse(ENABLE_SHELL_TRANSITIONS); RunningTaskInfo taskInfo = createTaskInfo(/* taskId= */ 1, WINDOWING_MODE_MULTI_WINDOW); - SurfaceControl taskLeash = new SurfaceControl.Builder(new SurfaceSession()) + SurfaceControl taskLeash = new SurfaceControl.Builder() .setName("task").build(); mOrganizer.registerOrganizer(); mOrganizer.onTaskAppeared(taskInfo, taskLeash); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt index f558e87c4ed7..2b7f86f36477 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicatorTest.kt @@ -22,6 +22,7 @@ import android.graphics.Region import android.testing.AndroidTestingRunner import android.view.SurfaceControl import androidx.test.filters.SmallTest +import com.android.internal.policy.SystemBarUtils import com.android.wm.shell.R import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTestCase @@ -67,8 +68,7 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() { createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FREEFORM) testRegion = visualIndicator.calculateFullscreenRegion(displayLayout, CAPTION_HEIGHT) - val transitionHeight = context.resources.getDimensionPixelSize( - R.dimen.desktop_mode_transition_region_thickness) + val transitionHeight = SystemBarUtils.getStatusBarHeight(context) val toFullscreenScale = mContext.resources.getFloat( R.dimen.desktop_mode_fullscreen_region_scale ) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java deleted file mode 100644 index b1befc46f383..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2020 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.splitscreen; - -import static android.view.Display.DEFAULT_DISPLAY; - -import static com.google.common.truth.Truth.assertThat; - -import android.app.ActivityManager; -import android.view.SurfaceControl; -import android.view.SurfaceSession; -import android.window.WindowContainerTransaction; - -import androidx.test.annotation.UiThreadTest; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.launcher3.icons.IconProvider; -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.TestRunningTaskInfoBuilder; -import com.android.wm.shell.common.SyncTransactionQueue; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.Optional; - -/** Tests for {@link MainStage} */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class MainStageTests extends ShellTestCase { - @Mock private ShellTaskOrganizer mTaskOrganizer; - @Mock private StageTaskListener.StageListenerCallbacks mCallbacks; - @Mock private SyncTransactionQueue mSyncQueue; - @Mock private ActivityManager.RunningTaskInfo mRootTaskInfo; - @Mock private SurfaceControl mRootLeash; - @Mock private IconProvider mIconProvider; - private WindowContainerTransaction mWct = new WindowContainerTransaction(); - private SurfaceSession mSurfaceSession = new SurfaceSession(); - private MainStage mMainStage; - - @Before - @UiThreadTest - public void setup() { - MockitoAnnotations.initMocks(this); - mRootTaskInfo = new TestRunningTaskInfoBuilder().build(); - mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, - mSyncQueue, mSurfaceSession, mIconProvider, Optional.empty()); - mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash); - } - - @Test - public void testActiveDeactivate() { - mMainStage.activate(mWct, true /* reparent */); - assertThat(mMainStage.isActive()).isTrue(); - - mMainStage.deactivate(mWct); - assertThat(mMainStage.isActive()).isFalse(); - } -} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java index 4de227836104..66dcef6f14cc 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java @@ -24,7 +24,6 @@ import android.content.Context; import android.graphics.Rect; import android.os.Handler; import android.view.SurfaceControl; -import android.view.SurfaceSession; import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.wm.shell.ShellTaskOrganizer; @@ -74,10 +73,10 @@ public class SplitTestUtils { final SurfaceControl mRootLeash; TestStageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, - ShellTaskOrganizer taskOrganizer, MainStage mainStage, StageTaskListener sideStage, - DisplayController displayController, DisplayImeController imeController, - DisplayInsetsController insetsController, SplitLayout splitLayout, - Transitions transitions, TransactionPool transactionPool, + ShellTaskOrganizer taskOrganizer, StageTaskListener mainStage, + StageTaskListener sideStage, DisplayController displayController, + DisplayImeController imeController, DisplayInsetsController insetsController, + SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool, ShellExecutor mainExecutor, Handler mainHandler, Optional<RecentTasksController> recentTasks, LaunchAdjacentController launchAdjacentController, @@ -89,7 +88,7 @@ public class SplitTestUtils { // Prepare root task for testing. mRootTask = new TestRunningTaskInfoBuilder().build(); - mRootLeash = new SurfaceControl.Builder(new SurfaceSession()).setName("test").build(); + mRootLeash = new SurfaceControl.Builder().setName("test").build(); onTaskAppeared(mRootTask, mRootLeash); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index e16743386489..ce3944a5855e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -53,7 +53,6 @@ import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.view.SurfaceControl; -import android.view.SurfaceSession; import android.window.IRemoteTransition; import android.window.RemoteTransition; import android.window.TransitionInfo; @@ -106,7 +105,6 @@ public class SplitTransitionTests extends ShellTestCase { @Mock private DisplayInsetsController mDisplayInsetsController; @Mock private TransactionPool mTransactionPool; @Mock private Transitions mTransitions; - @Mock private SurfaceSession mSurfaceSession; @Mock private IconProvider mIconProvider; @Mock private WindowDecorViewModel mWindowDecorViewModel; @Mock private ShellExecutor mMainExecutor; @@ -116,7 +114,7 @@ public class SplitTransitionTests extends ShellTestCase { @Mock private SplitScreen.SplitInvocationListener mInvocationListener; private final TestShellExecutor mTestShellExecutor = new TestShellExecutor(); private SplitLayout mSplitLayout; - private MainStage mMainStage; + private StageTaskListener mMainStage; private StageTaskListener mSideStage; private StageCoordinator mStageCoordinator; private SplitScreenTransitions mSplitScreenTransitions; @@ -133,12 +131,12 @@ public class SplitTransitionTests extends ShellTestCase { doReturn(mockExecutor).when(mTransitions).getAnimExecutor(); doReturn(mock(SurfaceControl.Transaction.class)).when(mTransactionPool).acquire(); mSplitLayout = SplitTestUtils.createMockSplitLayout(); - mMainStage = spy(new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock( - StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, + mMainStage = spy(new StageTaskListener(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock( + StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mIconProvider, Optional.of(mWindowDecorViewModel))); mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface()); mSideStage = spy(new StageTaskListener(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock( - StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, + StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mIconProvider, Optional.of(mWindowDecorViewModel))); mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface()); mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index c9e1414c39b9..a6c16c43c8cb 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -50,7 +50,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.view.SurfaceControl; -import android.view.SurfaceSession; import android.window.RemoteTransition; import android.window.WindowContainerTransaction; @@ -97,7 +96,7 @@ public class StageCoordinatorTests extends ShellTestCase { @Mock private SyncTransactionQueue mSyncQueue; @Mock - private MainStage mMainStage; + private StageTaskListener mMainStage; @Mock private StageTaskListener mSideStage; @Mock @@ -119,7 +118,6 @@ public class StageCoordinatorTests extends ShellTestCase { private final Rect mBounds2 = new Rect(5, 10, 15, 20); private final Rect mRootBounds = new Rect(0, 0, 45, 60); - private SurfaceSession mSurfaceSession = new SurfaceSession(); private SurfaceControl mRootLeash; private SurfaceControl mDividerLeash; private ActivityManager.RunningTaskInfo mRootTask; @@ -139,7 +137,7 @@ public class StageCoordinatorTests extends ShellTestCase { mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool, mMainExecutor, mMainHandler, Optional.empty(), mLaunchAdjacentController, Optional.empty())); - mDividerLeash = new SurfaceControl.Builder(mSurfaceSession).setName("fakeDivider").build(); + mDividerLeash = new SurfaceControl.Builder().setName("fakeDivider").build(); when(mSplitLayout.getBounds1()).thenReturn(mBounds1); when(mSplitLayout.getBounds2()).thenReturn(mBounds2); @@ -149,7 +147,7 @@ public class StageCoordinatorTests extends ShellTestCase { when(mSplitLayout.getDividerLeash()).thenReturn(mDividerLeash); mRootTask = new TestRunningTaskInfoBuilder().build(); - mRootLeash = new SurfaceControl.Builder(mSurfaceSession).setName("test").build(); + mRootLeash = new SurfaceControl.Builder().setName("test").build(); mStageCoordinator.onTaskAppeared(mRootTask, mRootLeash); mSideStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().build(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java index acd612eb34d9..b7b7d0d35bcf 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java @@ -32,7 +32,6 @@ import static org.mockito.Mockito.verify; import android.app.ActivityManager; import android.os.SystemProperties; import android.view.SurfaceControl; -import android.view.SurfaceSession; import android.window.WindowContainerTransaction; import androidx.test.annotation.UiThreadTest; @@ -82,7 +81,6 @@ public final class StageTaskListenerTests extends ShellTestCase { private WindowContainerTransaction mWct; @Captor private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor; - private SurfaceSession mSurfaceSession = new SurfaceSession(); private SurfaceControl mSurfaceControl; private ActivityManager.RunningTaskInfo mRootTask; private StageTaskListener mStageTaskListener; @@ -97,12 +95,11 @@ public final class StageTaskListenerTests extends ShellTestCase { DEFAULT_DISPLAY, mCallbacks, mSyncQueue, - mSurfaceSession, mIconProvider, Optional.of(mWindowDecorViewModel)); mRootTask = new TestRunningTaskInfoBuilder().build(); mRootTask.parentTaskId = INVALID_TASK_ID; - mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession).setName("test").build(); + mSurfaceControl = new SurfaceControl.Builder().setName("test").build(); mStageTaskListener.onTaskAppeared(mRootTask, mSurfaceControl); } @@ -199,4 +196,13 @@ public final class StageTaskListenerTests extends ShellTestCase { assertThat(mStageTaskListener.removeTask(task.taskId, null, mWct)).isTrue(); verify(mWct).reparent(eq(task.token), isNull(), eq(false)); } + + @Test + public void testActiveDeactivate() { + mStageTaskListener.activate(mWct, true /* reparent */); + assertThat(mStageTaskListener.isActive()).isTrue(); + + mStageTaskListener.deactivate(mWct); + assertThat(mStageTaskListener.isActive()).isFalse(); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java index 198488582700..17fd95b69dba 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java @@ -49,7 +49,6 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.SurfaceControl; import android.view.SurfaceHolder; -import android.view.SurfaceSession; import android.view.ViewTreeObserver; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; @@ -95,7 +94,6 @@ public class TaskViewTest extends ShellTestCase { Looper mViewLooper; TestHandler mViewHandler; - SurfaceSession mSession; SurfaceControl mLeash; Context mContext; @@ -106,7 +104,7 @@ public class TaskViewTest extends ShellTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); - mLeash = new SurfaceControl.Builder(mSession) + mLeash = new SurfaceControl.Builder() .setName("test") .build(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java index f51a9608d442..8f49de0a98fb 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_OPEN; +import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED; @@ -39,6 +40,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.view.SurfaceControl; @@ -213,6 +215,35 @@ public class HomeTransitionObserverTest extends ShellTestCase { verify(mListener, times(1)).onHomeVisibilityChanged(true); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_MIGRATE_PREDICTIVE_BACK_TRANSITION) + public void testHomeActivityWithBackGestureNotifiesHomeIsVisibleAfterClose() + throws RemoteException { + TransitionInfo info = mock(TransitionInfo.class); + TransitionInfo.Change change = mock(TransitionInfo.Change.class); + ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class); + when(change.getTaskInfo()).thenReturn(taskInfo); + when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change))); + when(info.getType()).thenReturn(TRANSIT_PREPARE_BACK_NAVIGATION); + + when(change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)).thenReturn(true); + setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN, true); + + mHomeTransitionObserver.onTransitionReady(mock(IBinder.class), + info, + mock(SurfaceControl.Transaction.class), + mock(SurfaceControl.Transaction.class)); + verify(mListener, times(0)).onHomeVisibilityChanged(anyBoolean()); + + when(info.getType()).thenReturn(TRANSIT_TO_BACK); + setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_CHANGE, true); + mHomeTransitionObserver.onTransitionReady(mock(IBinder.class), + info, + mock(SurfaceControl.Transaction.class), + mock(SurfaceControl.Transaction.class)); + verify(mListener, times(1)).onHomeVisibilityChanged(true); + } + /** * Helper class to initialize variables for the rest. */ diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index be0549b6655d..3dd8a2bacbcd 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -73,6 +73,7 @@ import com.android.wm.shell.ShellTestCase import com.android.wm.shell.TestRunningTaskInfoBuilder import com.android.wm.shell.TestShellExecutor import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser +import com.android.wm.shell.apptoweb.AssistContentRequester import com.android.wm.shell.common.DisplayChangeController import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayInsetsController @@ -165,6 +166,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { @Mock private lateinit var mockInteractionJankMonitor: InteractionJankMonitor @Mock private lateinit var mockGenericLinksParser: AppToWebGenericLinksParser @Mock private lateinit var mockUserHandle: UserHandle + @Mock private lateinit var mockAssistContentRequester: AssistContentRequester @Mock private lateinit var mockToast: Toast private val bgExecutor = TestShellExecutor() @Mock private lateinit var mockMultiInstanceHelper: MultiInstanceHelper @@ -218,6 +220,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { mockTransitions, Optional.of(mockDesktopTasksController), mockGenericLinksParser, + mockAssistContentRequester, mockMultiInstanceHelper, mockDesktopModeWindowDecorFactory, mockInputMonitorFactory, @@ -1131,7 +1134,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { whenever( mockDesktopModeWindowDecorFactory.create( any(), any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(), - any(), any(), any()) + any(), any(), any(), any()) ).thenReturn(decoration) decoration.mTaskInfo = task whenever(decoration.isFocused).thenReturn(task.isFocused) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java index 258c86094a36..b9e542a0e0c1 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java @@ -46,6 +46,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; +import android.app.assist.AssistContent; import android.content.ComponentName; import android.content.Context; import android.content.pm.ActivityInfo; @@ -88,6 +89,7 @@ import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser; +import com.android.wm.shell.apptoweb.AssistContentRequester; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.MultiInstanceHelper; import com.android.wm.shell.common.ShellExecutor; @@ -133,6 +135,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { private static final Uri TEST_URI1 = Uri.parse("https://www.google.com/"); private static final Uri TEST_URI2 = Uri.parse("https://docs.google.com/"); + private static final Uri TEST_URI3 = Uri.parse("https://slides.google.com/"); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); @@ -175,6 +178,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { @Mock private WindowManager mMockWindowManager; @Mock + private AssistContentRequester mMockAssistContentRequester; + @Mock private HandleMenu mMockHandleMenu; @Mock private HandleMenuFactory mMockHandleMenuFactory; @@ -189,7 +194,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { private SurfaceControl.Transaction mMockTransaction; private StaticMockitoSession mMockitoSession; private TestableContext mTestableContext; - private ShellExecutor mBgExecutor = new TestShellExecutor(); + private final ShellExecutor mBgExecutor = new TestShellExecutor(); + private final AssistContent mAssistContent = new AssistContent(); /** Set up run before test class. */ @BeforeClass @@ -673,10 +679,11 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { public void capturedLink_handleMenuBrowserLinkSetToCapturedLinkIfValid() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */); final DesktopModeWindowDecoration decor = createWindowDecoration( - taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* generic link */); + taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* web uri */, + TEST_URI3 /* generic link */); // Verify handle menu's browser link set as captured link - decor.createHandleMenu(mMockSplitScreenController); + createHandleMenu(decor); verifyHandleMenuCreated(TEST_URI1); } @@ -685,7 +692,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { public void capturedLink_postsOnCapturedLinkExpiredRunnable() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */); final DesktopModeWindowDecoration decor = createWindowDecoration( - taskInfo, TEST_URI1 /* captured link */, null /* generic link */); + taskInfo, TEST_URI1 /* captured link */, null /* web uri */, + null /* generic link */); final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); // Run runnable to set captured link to expired @@ -694,7 +702,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { // Verify captured link is no longer valid by verifying link is not set as handle menu // browser link. - decor.createHandleMenu(mMockSplitScreenController); + createHandleMenu(decor); verifyHandleMenuCreated(null /* uri */); } @@ -703,7 +711,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { public void capturedLink_capturedLinkNotResetToSameLink() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */); final DesktopModeWindowDecoration decor = createWindowDecoration( - taskInfo, TEST_URI1 /* captured link */, null /* generic link */); + taskInfo, TEST_URI1 /* captured link */, null /* web uri */, + null /* generic link */); final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); // Run runnable to set captured link to expired @@ -714,7 +723,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { decor.relayout(taskInfo); // Verify handle menu's browser link not set to captured link since link is expired - decor.createHandleMenu(mMockSplitScreenController); + createHandleMenu(decor); verifyHandleMenuCreated(null /* uri */); } @@ -723,11 +732,12 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { public void capturedLink_capturedLinkStillUsedIfExpiredAfterHandleMenuCreation() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */); final DesktopModeWindowDecoration decor = createWindowDecoration( - taskInfo, TEST_URI1 /* captured link */, null /* generic link */); + taskInfo, TEST_URI1 /* captured link */, null /* web uri */, + null /* generic link */); final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); // Create handle menu before link expires - decor.createHandleMenu(mMockSplitScreenController); + createHandleMenu(decor); // Run runnable to set captured link to expired verify(mMockHandler).postDelayed(runnableArgument.capture(), anyLong()); @@ -735,7 +745,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { // Verify handle menu's browser link is set to captured link since menu was opened before // captured link expired - decor.createHandleMenu(mMockSplitScreenController); + createHandleMenu(decor); verifyHandleMenuCreated(TEST_URI1); } @@ -744,12 +754,13 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { public void capturedLink_capturedLinkExpiresAfterClick() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */); final DesktopModeWindowDecoration decor = createWindowDecoration( - taskInfo, TEST_URI1 /* captured link */, null /* generic link */); + taskInfo, TEST_URI1 /* captured link */, null /* web uri */, + null /* generic link */); final ArgumentCaptor<Function1<Uri, Unit>> openInBrowserCaptor = ArgumentCaptor.forClass(Function1.class); // Simulate menu opening and clicking open in browser button - decor.createHandleMenu(mMockSplitScreenController); + createHandleMenu(decor); verify(mMockHandleMenu).show( any(), any(), @@ -763,7 +774,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { // Verify handle menu's browser link not set to captured link since link not valid after // open in browser clicked - decor.createHandleMenu(mMockSplitScreenController); + createHandleMenu(decor); verifyHandleMenuCreated(null /* uri */); } @@ -772,10 +783,11 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { public void capturedLink_openInBrowserListenerCalledOnClick() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */); final DesktopModeWindowDecoration decor = createWindowDecoration( - taskInfo, TEST_URI1 /* captured link */, null /* generic link */); + taskInfo, TEST_URI1 /* captured link */, null /* web uri */, + null /* generic link */); final ArgumentCaptor<Function1<Uri, Unit>> openInBrowserCaptor = ArgumentCaptor.forClass(Function1.class); - decor.createHandleMenu(mMockSplitScreenController); + createHandleMenu(decor); verify(mMockHandleMenu).show( any(), any(), @@ -793,24 +805,38 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB) - public void genericLink_genericLinkUsedWhenCapturedLinkUnavailable() { + public void webUriLink_webUriLinkUsedWhenCapturedLinkUnavailable() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */); final DesktopModeWindowDecoration decor = createWindowDecoration( - taskInfo, null /* captured link */, TEST_URI2 /* generic link */); - - // Verify handle menu's browser link set as generic link no captured link is available - decor.createHandleMenu(mMockSplitScreenController); + taskInfo, null /* captured link */, TEST_URI2 /* web uri */, + TEST_URI3 /* generic link */); + // Verify handle menu's browser link set as web uri link when captured link is unavailable + createHandleMenu(decor); verifyHandleMenuCreated(TEST_URI2); } @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB) + public void genericLink_genericLinkUsedWhenCapturedLinkAndWebUriUnavailable() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */); + final DesktopModeWindowDecoration decor = createWindowDecoration( + taskInfo, null /* captured link */, null /* web uri */, + TEST_URI3 /* generic link */); + + // Verify handle menu's browser link set as generic link when captured link and web uri link + // are unavailable + createHandleMenu(decor); + verifyHandleMenuCreated(TEST_URI3); + } + + @Test public void handleMenu_onCloseMenuClick_closesMenu() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo, true /* relayout */); final ArgumentCaptor<Function0<Unit>> closeClickListener = ArgumentCaptor.forClass(Function0.class); - decoration.createHandleMenu(mMockSplitScreenController); + createHandleMenu(decoration); verify(mMockHandleMenu).show( any(), any(), @@ -860,9 +886,10 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { private DesktopModeWindowDecoration createWindowDecoration( ActivityManager.RunningTaskInfo taskInfo, @Nullable Uri capturedLink, - @Nullable Uri genericLink) { + @Nullable Uri webUri, @Nullable Uri genericLink) { taskInfo.capturedLink = capturedLink; taskInfo.capturedLinkTimestamp = System.currentTimeMillis(); + mAssistContent.setWebUri(webUri); final String genericLinkString = genericLink == null ? null : genericLink.toString(); doReturn(genericLinkString).when(mMockGenericLinksParser).getGenericLink(any()); // Relayout to set captured link @@ -894,11 +921,10 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { mContext, mMockDisplayController, mMockSplitScreenController, mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl, mMockHandler, mBgExecutor, mMockChoreographer, mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer, - mMockGenericLinksParser, SurfaceControl.Builder::new, mMockTransactionSupplier, - WindowContainerTransaction::new, SurfaceControl::new, - new WindowManagerWrapper(mMockWindowManager), - mMockSurfaceControlViewHostFactory, maximizeMenuFactory, mMockHandleMenuFactory, - mMockMultiInstanceHelper); + mMockGenericLinksParser, mMockAssistContentRequester, SurfaceControl.Builder::new, + mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new, + new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory, + maximizeMenuFactory, mMockHandleMenuFactory, mMockMultiInstanceHelper); windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener, mMockTouchEventListener, mMockTouchEventListener); windowDecor.setExclusionRegionListener(mMockExclusionRegionListener); @@ -926,6 +952,13 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { } + private void createHandleMenu(@NonNull DesktopModeWindowDecoration decor) { + decor.createHandleMenu(); + // Call DesktopModeWindowDecoration#onAssistContentReceived because decor waits to receive + // {@link AssistContent} before creating the menu + decor.onAssistContentReceived(mAssistContent); + } + private static boolean hasNoInputChannelFeature(RelayoutParams params) { return (params.mInputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) != 0; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt index a07be79579eb..e0d16aab1e07 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt @@ -97,7 +97,7 @@ class ResizeVeilTest : ShellTestCase() { .thenReturn(spyResizeVeilSurfaceBuilder) doReturn(mockResizeVeilSurface).whenever(spyResizeVeilSurfaceBuilder).build() whenever(mockSurfaceControlBuilderFactory - .create(eq("Resize veil background of Task=" + taskInfo.taskId), any())) + .create(eq("Resize veil background of Task=" + taskInfo.taskId))) .thenReturn(spyBackgroundSurfaceBuilder) doReturn(mockBackgroundSurface).whenever(spyBackgroundSurfaceBuilder).build() whenever(mockSurfaceControlBuilderFactory diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index e6182454ad8a..5955915c9fcd 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -1420,18 +1420,20 @@ void AssetManager::mergeInfoLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo Mutex AssetManager::SharedZip::gLock; DefaultKeyedVector<String8, wp<AssetManager::SharedZip> > AssetManager::SharedZip::gOpen; -AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen) - : mPath(path), mZipFile(NULL), mModWhen(modWhen), - mResourceTableAsset(NULL), mResourceTable(NULL) -{ - if (kIsDebug) { - ALOGI("Creating SharedZip %p %s\n", this, mPath.c_str()); - } - ALOGV("+++ opening zip '%s'\n", mPath.c_str()); - mZipFile = ZipFileRO::open(mPath.c_str()); - if (mZipFile == NULL) { - ALOGD("failed to open Zip archive '%s'\n", mPath.c_str()); - } +AssetManager::SharedZip::SharedZip(const String8& path, ModDate modWhen) + : mPath(path), + mZipFile(NULL), + mModWhen(modWhen), + mResourceTableAsset(NULL), + mResourceTable(NULL) { + if (kIsDebug) { + ALOGI("Creating SharedZip %p %s\n", this, mPath.c_str()); + } + ALOGV("+++ opening zip '%s'\n", mPath.c_str()); + mZipFile = ZipFileRO::open(mPath.c_str()); + if (mZipFile == NULL) { + ALOGD("failed to open Zip archive '%s'\n", mPath.c_str()); + } } AssetManager::SharedZip::SharedZip(int fd, const String8& path) @@ -1453,7 +1455,7 @@ sp<AssetManager::SharedZip> AssetManager::SharedZip::get(const String8& path, bool createIfNotPresent) { AutoMutex _l(gLock); - time_t modWhen = getFileModDate(path.c_str()); + auto modWhen = getFileModDate(path.c_str()); sp<SharedZip> zip = gOpen.valueFor(path).promote(); if (zip != NULL && zip->mModWhen == modWhen) { return zip; @@ -1520,8 +1522,8 @@ ResTable* AssetManager::SharedZip::setResourceTable(ResTable* res) bool AssetManager::SharedZip::isUpToDate() { - time_t modWhen = getFileModDate(mPath.c_str()); - return mModWhen == modWhen; + auto modWhen = getFileModDate(mPath.c_str()); + return mModWhen == modWhen; } void AssetManager::SharedZip::addOverlay(const asset_path& ap) diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h index ce0985b38986..376c881ea376 100644 --- a/libs/androidfw/include/androidfw/AssetManager.h +++ b/libs/androidfw/include/androidfw/AssetManager.h @@ -280,21 +280,21 @@ private: ~SharedZip(); private: - SharedZip(const String8& path, time_t modWhen); - SharedZip(int fd, const String8& path); - SharedZip(); // <-- not implemented + SharedZip(const String8& path, ModDate modWhen); + SharedZip(int fd, const String8& path); + SharedZip(); // <-- not implemented - String8 mPath; - ZipFileRO* mZipFile; - time_t mModWhen; + String8 mPath; + ZipFileRO* mZipFile; + ModDate mModWhen; - Asset* mResourceTableAsset; - ResTable* mResourceTable; + Asset* mResourceTableAsset; + ResTable* mResourceTable; - Vector<asset_path> mOverlays; + Vector<asset_path> mOverlays; - static Mutex gLock; - static DefaultKeyedVector<String8, wp<SharedZip> > gOpen; + static Mutex gLock; + static DefaultKeyedVector<String8, wp<SharedZip> > gOpen; }; /* diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h index 64b1f0c6ed03..98f1aa86f2db 100644 --- a/libs/androidfw/include/androidfw/Idmap.h +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -25,8 +25,9 @@ #include "android-base/macros.h" #include "android-base/unique_fd.h" #include "androidfw/ConfigDescription.h" -#include "androidfw/StringPiece.h" #include "androidfw/ResourceTypes.h" +#include "androidfw/StringPiece.h" +#include "androidfw/misc.h" #include "utils/ByteOrder.h" namespace android { @@ -202,7 +203,7 @@ class LoadedIdmap { android::base::unique_fd idmap_fd_; std::string_view overlay_apk_path_; std::string_view target_apk_path_; - time_t idmap_last_mod_time_; + ModDate idmap_last_mod_time_; private: DISALLOW_COPY_AND_ASSIGN(LoadedIdmap); diff --git a/libs/androidfw/include/androidfw/misc.h b/libs/androidfw/include/androidfw/misc.h index 077609d20d55..09ae40c35369 100644 --- a/libs/androidfw/include/androidfw/misc.h +++ b/libs/androidfw/include/androidfw/misc.h @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#pragma once -#include <sys/types.h> +#include <time.h> // // Handy utility functions and portability code. // -#ifndef _LIBS_ANDROID_FW_MISC_H -#define _LIBS_ANDROID_FW_MISC_H namespace android { @@ -41,15 +40,35 @@ typedef enum FileType { } FileType; /* get the file's type; follows symlinks */ FileType getFileType(const char* fileName); -/* get the file's modification date; returns -1 w/errno set on failure */ -time_t getFileModDate(const char* fileName); + +// MinGW doesn't support nanosecond resolution in stat() modification time, and given +// that it only matters on the device it's ok to keep it at the second level there. +#ifdef _WIN32 +using ModDate = time_t; +inline constexpr ModDate kInvalidModDate = ModDate(-1); +inline constexpr unsigned long long kModDateResolutionNs = 1ull * 1000 * 1000 * 1000; +inline time_t toTimeT(ModDate m) { + return m; +} +#else +using ModDate = timespec; +inline constexpr ModDate kInvalidModDate = {-1, -1}; +inline constexpr unsigned long long kModDateResolutionNs = 1; +inline time_t toTimeT(ModDate m) { + return m.tv_sec; +} +#endif + +/* get the file's modification date; returns kInvalidModDate w/errno set on failure */ +ModDate getFileModDate(const char* fileName); /* same, but also returns -1 if the file has already been deleted */ -time_t getFileModDate(int fd); +ModDate getFileModDate(int fd); // Check if |path| or |fd| resides on a readonly filesystem. bool isReadonlyFilesystem(const char* path); bool isReadonlyFilesystem(int fd); -}; // namespace android +} // namespace android -#endif // _LIBS_ANDROID_FW_MISC_H +// Whoever uses getFileModDate() will need this as well +bool operator==(const timespec& l, const timespec& r); diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp index 93dcaf549a90..9bdaf18a116a 100644 --- a/libs/androidfw/misc.cpp +++ b/libs/androidfw/misc.cpp @@ -28,11 +28,13 @@ #include <sys/vfs.h> #endif // __linux__ -#include <cstring> -#include <cstdio> #include <errno.h> #include <sys/stat.h> +#include <cstdio> +#include <cstring> +#include <tuple> + namespace android { /* @@ -73,27 +75,32 @@ FileType getFileType(const char* fileName) } } -/* - * Get a file's modification date. - */ -time_t getFileModDate(const char* fileName) { - struct stat sb; - if (stat(fileName, &sb) < 0) { - return (time_t)-1; - } - return sb.st_mtime; +static ModDate getModDate(const struct stat& st) { +#ifdef _WIN32 + return st.st_mtime; +#else + return st.st_mtim; +#endif } -time_t getFileModDate(int fd) { - struct stat sb; - if (fstat(fd, &sb) < 0) { - return (time_t)-1; - } - if (sb.st_nlink <= 0) { - errno = ENOENT; - return (time_t)-1; - } - return sb.st_mtime; +ModDate getFileModDate(const char* fileName) { + struct stat sb; + if (stat(fileName, &sb) < 0) { + return kInvalidModDate; + } + return getModDate(sb); +} + +ModDate getFileModDate(int fd) { + struct stat sb; + if (fstat(fd, &sb) < 0) { + return kInvalidModDate; + } + if (sb.st_nlink <= 0) { + errno = ENOENT; + return kInvalidModDate; + } + return getModDate(sb); } #ifndef __linux__ @@ -124,4 +131,8 @@ bool isReadonlyFilesystem(int fd) { } #endif // __linux__ -}; // namespace android +} // namespace android + +bool operator==(const timespec& l, const timespec& r) { + return std::tie(l.tv_sec, l.tv_nsec) == std::tie(r.tv_sec, l.tv_nsec); +} diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp index 60aa7d88925d..cb2e56f5f5e4 100644 --- a/libs/androidfw/tests/Idmap_test.cpp +++ b/libs/androidfw/tests/Idmap_test.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +#include <chrono> +#include <thread> + #include "android-base/file.h" #include "androidfw/ApkAssets.h" #include "androidfw/AssetManager2.h" @@ -27,6 +30,7 @@ #include "data/overlayable/R.h" #include "data/system/R.h" +using namespace std::chrono_literals; using ::testing::NotNull; namespace overlay = com::android::overlay; @@ -218,10 +222,13 @@ TEST_F(IdmapTest, OverlayAssetsIsUpToDate) { unlink(temp_file.path); ASSERT_FALSE(apk_assets->IsUpToDate()); - sleep(2); + + const auto sleep_duration = + std::chrono::nanoseconds(std::max(kModDateResolutionNs, 1'000'000ull)); + std::this_thread::sleep_for(sleep_duration); base::WriteStringToFile("hello", temp_file.path); - sleep(2); + std::this_thread::sleep_for(sleep_duration); ASSERT_FALSE(apk_assets->IsUpToDate()); } diff --git a/libs/hostgraphics/Android.bp b/libs/hostgraphics/Android.bp index 09232b64616d..a58493aa47ca 100644 --- a/libs/hostgraphics/Android.bp +++ b/libs/hostgraphics/Android.bp @@ -7,6 +7,17 @@ package { default_applicable_licenses: ["frameworks_base_license"], } +cc_library_headers { + name: "libhostgraphics_headers", + host_supported: true, + export_include_dirs: ["include"], + target: { + windows: { + enabled: true, + }, + }, +} + cc_library_host_static { name: "libhostgraphics", @@ -30,12 +41,13 @@ cc_library_host_static { ], header_libs: [ + "libhostgraphics_headers", "libnativebase_headers", "libnativedisplay_headers", "libnativewindow_headers", ], - export_include_dirs: ["."], + export_include_dirs: ["include"], target: { windows: { diff --git a/libs/hostgraphics/gui/BufferItem.h b/libs/hostgraphics/include/gui/BufferItem.h index e95a9231dfaf..e95a9231dfaf 100644 --- a/libs/hostgraphics/gui/BufferItem.h +++ b/libs/hostgraphics/include/gui/BufferItem.h diff --git a/libs/hostgraphics/gui/BufferItemConsumer.h b/libs/hostgraphics/include/gui/BufferItemConsumer.h index c25941151800..c25941151800 100644 --- a/libs/hostgraphics/gui/BufferItemConsumer.h +++ b/libs/hostgraphics/include/gui/BufferItemConsumer.h diff --git a/libs/hostgraphics/gui/BufferQueue.h b/libs/hostgraphics/include/gui/BufferQueue.h index 67a8c00fd267..67a8c00fd267 100644 --- a/libs/hostgraphics/gui/BufferQueue.h +++ b/libs/hostgraphics/include/gui/BufferQueue.h diff --git a/libs/hostgraphics/gui/ConsumerBase.h b/libs/hostgraphics/include/gui/ConsumerBase.h index 7f7309e8a3a8..7f7309e8a3a8 100644 --- a/libs/hostgraphics/gui/ConsumerBase.h +++ b/libs/hostgraphics/include/gui/ConsumerBase.h diff --git a/libs/hostgraphics/gui/IGraphicBufferConsumer.h b/libs/hostgraphics/include/gui/IGraphicBufferConsumer.h index 14ac4fe71cc8..14ac4fe71cc8 100644 --- a/libs/hostgraphics/gui/IGraphicBufferConsumer.h +++ b/libs/hostgraphics/include/gui/IGraphicBufferConsumer.h diff --git a/libs/hostgraphics/gui/IGraphicBufferProducer.h b/libs/hostgraphics/include/gui/IGraphicBufferProducer.h index 8fd8590d10d7..8fd8590d10d7 100644 --- a/libs/hostgraphics/gui/IGraphicBufferProducer.h +++ b/libs/hostgraphics/include/gui/IGraphicBufferProducer.h diff --git a/libs/hostgraphics/gui/Surface.h b/libs/hostgraphics/include/gui/Surface.h index 2774f89cb54c..2774f89cb54c 100644 --- a/libs/hostgraphics/gui/Surface.h +++ b/libs/hostgraphics/include/gui/Surface.h diff --git a/libs/hostgraphics/ui/Fence.h b/libs/hostgraphics/include/ui/Fence.h index 187c3116f61c..187c3116f61c 100644 --- a/libs/hostgraphics/ui/Fence.h +++ b/libs/hostgraphics/include/ui/Fence.h diff --git a/libs/hostgraphics/ui/GraphicBuffer.h b/libs/hostgraphics/include/ui/GraphicBuffer.h index cda45e4660ca..cda45e4660ca 100644 --- a/libs/hostgraphics/ui/GraphicBuffer.h +++ b/libs/hostgraphics/include/ui/GraphicBuffer.h diff --git a/libs/hwui/FeatureFlags.h b/libs/hwui/FeatureFlags.h index c1c30f5379ab..fddcf29b9197 100644 --- a/libs/hwui/FeatureFlags.h +++ b/libs/hwui/FeatureFlags.h @@ -25,22 +25,6 @@ namespace android { namespace text_feature { -inline bool fix_double_underline() { -#ifdef __ANDROID__ - return com_android_text_flags_fix_double_underline(); -#else - return true; -#endif // __ANDROID__ -} - -inline bool deprecate_ui_fonts() { -#ifdef __ANDROID__ - return com_android_text_flags_deprecate_ui_fonts(); -#else - return true; -#endif // __ANDROID__ -} - inline bool letter_spacing_justification() { #ifdef __ANDROID__ return com_android_text_flags_letter_spacing_justification(); diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp index a2748b050a2d..236c3736816e 100644 --- a/libs/hwui/HardwareBitmapUploader.cpp +++ b/libs/hwui/HardwareBitmapUploader.cpp @@ -318,6 +318,11 @@ bool HardwareBitmapUploader::has1010102Support() { return has101012Support; } +bool HardwareBitmapUploader::has10101010Support() { + static bool has1010110Support = checkSupport(AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM); + return has1010110Support; +} + bool HardwareBitmapUploader::hasAlpha8Support() { static bool hasAlpha8Support = checkSupport(AHARDWAREBUFFER_FORMAT_R8_UNORM); return hasAlpha8Support; @@ -376,6 +381,19 @@ static FormatInfo determineFormat(const SkBitmap& skBitmap, bool usingGL) { } formatInfo.format = GL_RGBA; break; + case kRGBA_10x6_SkColorType: + formatInfo.isSupported = HardwareBitmapUploader::has10101010Support(); + if (formatInfo.isSupported) { + formatInfo.type = 0; // Not supported in GL + formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM; + formatInfo.vkFormat = VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16; + } else { + formatInfo.type = GL_UNSIGNED_BYTE; + formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; + formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM; + } + formatInfo.format = 0; // Not supported in GL + break; case kAlpha_8_SkColorType: formatInfo.isSupported = HardwareBitmapUploader::hasAlpha8Support(); if (formatInfo.isSupported) { diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h index 00ee99648889..76cb80b722d0 100644 --- a/libs/hwui/HardwareBitmapUploader.h +++ b/libs/hwui/HardwareBitmapUploader.h @@ -33,12 +33,14 @@ public: #ifdef __ANDROID__ static bool hasFP16Support(); static bool has1010102Support(); + static bool has10101010Support(); static bool hasAlpha8Support(); #else static bool hasFP16Support() { return true; } static bool has1010102Support() { return true; } + static bool has10101010Support() { return true; } static bool hasAlpha8Support() { return true; } #endif }; diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 72e83afbd96f..9e825fb350d6 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -841,9 +841,6 @@ void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& pai sk_sp<SkTextBlob> textBlob(builder.make()); applyLooper(&paintCopy, [&](const SkPaint& p) { mCanvas->drawTextBlob(textBlob, 0, 0, p); }); - if (!text_feature::fix_double_underline()) { - drawTextDecorations(x, y, totalAdvance, paintCopy); - } } void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset, diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index 80b6c0385fca..5af4af27babd 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -110,28 +110,26 @@ void Canvas::drawText(const uint16_t* text, int textSize, int start, int count, DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance()); MinikinUtils::forFontRun(layout, &paint, f); - if (text_feature::fix_double_underline()) { - Paint copied(paint); - PaintFilter* filter = getPaintFilter(); - if (filter != nullptr) { - filter->filterFullPaint(&copied); + Paint copied(paint); + PaintFilter* filter = getPaintFilter(); + if (filter != nullptr) { + filter->filterFullPaint(&copied); + } + const bool isUnderline = copied.isUnderline(); + const bool isStrikeThru = copied.isStrikeThru(); + if (isUnderline || isStrikeThru) { + const SkScalar left = x; + const SkScalar right = x + layout.getAdvance(); + if (isUnderline) { + const SkScalar top = y + f.getUnderlinePosition(); + drawStroke(left, right, top, f.getUnderlineThickness(), copied, this); } - const bool isUnderline = copied.isUnderline(); - const bool isStrikeThru = copied.isStrikeThru(); - if (isUnderline || isStrikeThru) { - const SkScalar left = x; - const SkScalar right = x + layout.getAdvance(); - if (isUnderline) { - const SkScalar top = y + f.getUnderlinePosition(); - drawStroke(left, right, top, f.getUnderlineThickness(), copied, this); - } - if (isStrikeThru) { - float textSize = paint.getSkFont().getSize(); - const float position = textSize * Paint::kStdStrikeThru_Top; - const SkScalar thickness = textSize * Paint::kStdStrikeThru_Thickness; - const SkScalar top = y + position; - drawStroke(left, right, top, thickness, copied, this); - } + if (isStrikeThru) { + float textSize = paint.getSkFont().getSize(); + const float position = textSize * Paint::kStdStrikeThru_Top; + const SkScalar thickness = textSize * Paint::kStdStrikeThru_Thickness; + const SkScalar top = y + position; + drawStroke(left, right, top, thickness, copied, this); } } } diff --git a/libs/hwui/hwui/DrawTextFunctor.h b/libs/hwui/hwui/DrawTextFunctor.h index 0efb2c81af01..d7bf20130b71 100644 --- a/libs/hwui/hwui/DrawTextFunctor.h +++ b/libs/hwui/hwui/DrawTextFunctor.h @@ -142,32 +142,30 @@ public: canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, totalAdvance); } - if (text_feature::fix_double_underline()) { - // Extract underline position and thickness. - if (paint.isUnderline()) { - SkFontMetrics metrics; - paint.getSkFont().getMetrics(&metrics); - const float textSize = paint.getSkFont().getSize(); - SkScalar position; - if (!metrics.hasUnderlinePosition(&position)) { - position = textSize * Paint::kStdUnderline_Top; - } - SkScalar thickness; - if (!metrics.hasUnderlineThickness(&thickness)) { - thickness = textSize * Paint::kStdUnderline_Thickness; - } - - // If multiple fonts are used, use the most bottom position and most thick stroke - // width as the underline position. This follows the CSS standard: - // https://www.w3.org/TR/css-text-decor-3/#text-underline-position-property - // <quote> - // The exact position and thickness of line decorations is UA-defined in this level. - // However, for underlines and overlines the UA must use a single thickness and - // position on each line for the decorations deriving from a single decorating box. - // </quote> - underlinePosition = std::max(underlinePosition, position); - underlineThickness = std::max(underlineThickness, thickness); + // Extract underline position and thickness. + if (paint.isUnderline()) { + SkFontMetrics metrics; + paint.getSkFont().getMetrics(&metrics); + const float textSize = paint.getSkFont().getSize(); + SkScalar position; + if (!metrics.hasUnderlinePosition(&position)) { + position = textSize * Paint::kStdUnderline_Top; } + SkScalar thickness; + if (!metrics.hasUnderlineThickness(&thickness)) { + thickness = textSize * Paint::kStdUnderline_Thickness; + } + + // If multiple fonts are used, use the most bottom position and most thick stroke + // width as the underline position. This follows the CSS standard: + // https://www.w3.org/TR/css-text-decor-3/#text-underline-position-property + // <quote> + // The exact position and thickness of line decorations is UA-defined in this level. + // However, for underlines and overlines the UA must use a single thickness and + // position on each line for the decorations deriving from a single decorating box. + // </quote> + underlinePosition = std::max(underlinePosition, position); + underlineThickness = std::max(underlineThickness, thickness); } } diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp index d66d7f8e83f4..ede385adc779 100644 --- a/libs/hwui/hwui/MinikinUtils.cpp +++ b/libs/hwui/hwui/MinikinUtils.cpp @@ -53,9 +53,7 @@ minikin::MinikinPaint MinikinUtils::prepareMinikinPaint(const Paint* paint, if (familyVariant.has_value()) { minikinPaint.familyVariant = familyVariant.value(); } else { - minikinPaint.familyVariant = text_feature::deprecate_ui_fonts() - ? minikin::FamilyVariant::ELEGANT - : minikin::FamilyVariant::DEFAULT; + minikinPaint.familyVariant = minikin::FamilyVariant::ELEGANT; } return minikinPaint; } diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index dfda25d013ed..8ec04304a808 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -654,6 +654,9 @@ void CanvasContext::draw(bool solelyTextureViewUpdates) { if (vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) { const auto inputEventId = static_cast<int32_t>(mCurrentFrameInfo->get(FrameInfoIndex::InputEventId)); + ATRACE_FORMAT( + "frameTimelineInfo(frameNumber=%llu, vsyncId=%lld, inputEventId=0x%" PRIx32 ")", + frameCompleteNr, vsyncId, inputEventId); const ANativeWindowFrameTimelineInfo ftl = { .frameNumber = frameCompleteNr, .frameTimelineVsyncId = vsyncId, diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index bce84ae77c87..e3023937964e 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -318,6 +318,15 @@ void VulkanManager::setupDevice(skgpu::VulkanExtensions& grExtensions, tailPNext = &deviceFaultFeatures->pNext; } + if (grExtensions.hasExtension(VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME, 1)) { + VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT* formatFeatures = + new VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT; + formatFeatures->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RGBA10X6_FORMATS_FEATURES_EXT; + formatFeatures->pNext = nullptr; + *tailPNext = formatFeatures; + tailPNext = &formatFeatures->pNext; + } + // query to get the physical device features mGetPhysicalDeviceFeatures2(mPhysicalDevice, &features); // this looks like it would slow things down, diff --git a/libs/hwui/tests/unit/UnderlineTest.cpp b/libs/hwui/tests/unit/UnderlineTest.cpp index c70a30477ecf..ecb06d8ca4db 100644 --- a/libs/hwui/tests/unit/UnderlineTest.cpp +++ b/libs/hwui/tests/unit/UnderlineTest.cpp @@ -109,9 +109,7 @@ DrawTextFunctor processFunctor(const std::vector<uint16_t>& text, Paint* paint) return f; } -TEST_WITH_FLAGS(UnderlineTest, Roboto, - REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::text::flags, - fix_double_underline))) { +TEST(UnderlineTest, Roboto) { float textSize = 100; Paint paint; paint.getSkFont().setSize(textSize); @@ -123,9 +121,7 @@ TEST_WITH_FLAGS(UnderlineTest, Roboto, EXPECT_EQ(ROBOTO_THICKNESS_EM * textSize, functor.getUnderlineThickness()); } -TEST_WITH_FLAGS(UnderlineTest, NotoCJK, - REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::text::flags, - fix_double_underline))) { +TEST(UnderlineTest, NotoCJK) { float textSize = 100; Paint paint; paint.getSkFont().setSize(textSize); @@ -137,9 +133,7 @@ TEST_WITH_FLAGS(UnderlineTest, NotoCJK, EXPECT_EQ(NOTO_CJK_THICKNESS_EM * textSize, functor.getUnderlineThickness()); } -TEST_WITH_FLAGS(UnderlineTest, Mixture, - REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::text::flags, - fix_double_underline))) { +TEST(UnderlineTest, Mixture) { float textSize = 100; Paint paint; paint.getSkFont().setSize(textSize); diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp index 6a560b365247..9673c5f03642 100644 --- a/libs/hwui/utils/Color.cpp +++ b/libs/hwui/utils/Color.cpp @@ -49,6 +49,10 @@ static inline SkImageInfo createImageInfo(int32_t width, int32_t height, int32_t colorType = kRGBA_1010102_SkColorType; alphaType = kPremul_SkAlphaType; break; + case AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM: + colorType = kRGBA_10x6_SkColorType; + alphaType = kPremul_SkAlphaType; + break; case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT: colorType = kRGBA_F16_SkColorType; alphaType = kPremul_SkAlphaType; @@ -86,6 +90,8 @@ uint32_t ColorTypeToBufferFormat(SkColorType colorType) { return AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM; case kRGBA_1010102_SkColorType: return AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM; + case kRGBA_10x6_SkColorType: + return AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM; case kARGB_4444_SkColorType: // Hardcoding the value from android::PixelFormat static constexpr uint64_t kRGBA4444 = 7; @@ -108,6 +114,8 @@ SkColorType BufferFormatToColorType(uint32_t format) { return kRGB_565_SkColorType; case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM: return kRGBA_1010102_SkColorType; + case AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM: + return kRGBA_10x6_SkColorType; case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT: return kRGBA_F16_SkColorType; case AHARDWAREBUFFER_FORMAT_R8_UNORM: |