blob: 46de60772766732b18df3ca49a7d21b07b488409 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.wm.shell.unfold;
import static com.android.wm.shell.unfold.UnfoldAnimationControllerTest.TestUnfoldTaskAnimator.UNSET;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.TaskInfo;
import android.testing.AndroidTestingRunner;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.unfold.animation.UnfoldTaskAnimator;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Predicate;
/**
* Tests for {@link UnfoldAnimationController}.
*
* Build/Install/Run:
* atest WMShellUnitTests:UnfoldAnimationControllerTest
*/
@RunWith(AndroidTestingRunner.class)
public class UnfoldAnimationControllerTest extends ShellTestCase {
@Mock
private TransactionPool mTransactionPool;
@Mock
private UnfoldTransitionHandler mUnfoldTransitionHandler;
@Mock
private SurfaceControl mLeash;
private UnfoldAnimationController mUnfoldAnimationController;
private final TestShellUnfoldProgressProvider mProgressProvider =
new TestShellUnfoldProgressProvider();
private final TestShellExecutor mShellExecutor = new TestShellExecutor();
private final TestUnfoldTaskAnimator mTaskAnimator1 = new TestUnfoldTaskAnimator();
private final TestUnfoldTaskAnimator mTaskAnimator2 = new TestUnfoldTaskAnimator();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mTransactionPool.acquire()).thenReturn(mock(SurfaceControl.Transaction.class));
final List<UnfoldTaskAnimator> animators = new ArrayList<>();
animators.add(mTaskAnimator1);
animators.add(mTaskAnimator2);
mUnfoldAnimationController = new UnfoldAnimationController(
mTransactionPool,
mProgressProvider,
animators,
() -> Optional.of(mUnfoldTransitionHandler),
mShellExecutor
);
}
@Test
public void testAppearedMatchingTask_appliesUnfoldProgress() {
mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
.setWindowingMode(2).build();
mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
mUnfoldAnimationController.onStateChangeProgress(0.5f);
assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(0.5f);
}
@Test
public void testAppearedMatchingTaskTwoDifferentAnimators_appliesUnfoldProgressToBoth() {
mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 1);
mTaskAnimator2.setTaskMatcher((info) -> info.getWindowingMode() == 2);
RunningTaskInfo taskInfo1 = new TestRunningTaskInfoBuilder()
.setWindowingMode(1).build();
RunningTaskInfo taskInfo2 = new TestRunningTaskInfoBuilder()
.setWindowingMode(2).build();
mUnfoldAnimationController.onTaskAppeared(taskInfo1, mLeash);
mUnfoldAnimationController.onTaskAppeared(taskInfo2, mLeash);
mUnfoldAnimationController.onStateChangeProgress(0.5f);
assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(0.5f);
assertThat(mTaskAnimator2.mLastAppliedProgress).isEqualTo(0.5f);
}
@Test
public void testAppearedNonMatchingTask_doesNotApplyUnfoldProgress() {
mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
.setWindowingMode(0).build();
mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
mUnfoldAnimationController.onStateChangeProgress(0.5f);
assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(UNSET);
}
@Test
public void testAppearedAndChangedToNonMatchingTask_doesNotApplyUnfoldProgress() {
mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
.setWindowingMode(2).build();
mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
taskInfo.configuration.windowConfiguration.setWindowingMode(0);
mUnfoldAnimationController.onTaskInfoChanged(taskInfo);
mUnfoldAnimationController.onStateChangeProgress(0.5f);
assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(UNSET);
}
@Test
public void testAppearedAndChangedToNonMatchingTaskAndBack_appliesUnfoldProgress() {
mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
.setWindowingMode(2).build();
mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
taskInfo.configuration.windowConfiguration.setWindowingMode(0);
mUnfoldAnimationController.onTaskInfoChanged(taskInfo);
taskInfo.configuration.windowConfiguration.setWindowingMode(2);
mUnfoldAnimationController.onTaskInfoChanged(taskInfo);
mUnfoldAnimationController.onStateChangeProgress(0.5f);
assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(0.5f);
}
@Test
public void testAppearedNonMatchingTaskAndChangedToMatching_appliesUnfoldProgress() {
mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
.setWindowingMode(0).build();
mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
taskInfo.configuration.windowConfiguration.setWindowingMode(2);
mUnfoldAnimationController.onTaskInfoChanged(taskInfo);
mUnfoldAnimationController.onStateChangeProgress(0.5f);
assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(0.5f);
}
@Test
public void testAppearedMatchingTaskAndChanged_appliesUnfoldProgress() {
mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
.setWindowingMode(2).build();
mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
mUnfoldAnimationController.onTaskInfoChanged(taskInfo);
mUnfoldAnimationController.onStateChangeProgress(0.5f);
assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(0.5f);
}
@Test
public void testShellTransitionRunning_doesNotApplyUnfoldProgress() {
when(mUnfoldTransitionHandler.willHandleTransition()).thenReturn(true);
mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
.setWindowingMode(2).build();
mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
mUnfoldAnimationController.onStateChangeProgress(0.5f);
assertThat(mTaskAnimator1.mLastAppliedProgress).isEqualTo(UNSET);
}
@Test
public void testApplicableTaskDisappeared_resetsSurface() {
mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 0);
RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
.setWindowingMode(0).build();
mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
assertThat(mTaskAnimator1.mResetTasks).doesNotContain(taskInfo.taskId);
mUnfoldAnimationController.onTaskVanished(taskInfo);
assertThat(mTaskAnimator1.mResetTasks).contains(taskInfo.taskId);
}
@Test
public void testApplicablePinnedTaskDisappeared_doesNotResetSurface() {
mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
.setWindowingMode(2).build();
mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
assertThat(mTaskAnimator1.mResetTasks).doesNotContain(taskInfo.taskId);
mUnfoldAnimationController.onTaskVanished(taskInfo);
assertThat(mTaskAnimator1.mResetTasks).doesNotContain(taskInfo.taskId);
}
@Test
public void testNonApplicableTaskAppearedDisappeared_doesNotResetSurface() {
mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
.setWindowingMode(0).build();
mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
mUnfoldAnimationController.onTaskVanished(taskInfo);
assertThat(mTaskAnimator1.mResetTasks).doesNotContain(taskInfo.taskId);
}
@Test
public void testInit_initsAndStartsAnimators() {
mUnfoldAnimationController.init();
assertThat(mTaskAnimator1.mInitialized).isTrue();
assertThat(mTaskAnimator1.mStarted).isTrue();
}
private static class TestShellUnfoldProgressProvider implements ShellUnfoldProgressProvider,
ShellUnfoldProgressProvider.UnfoldListener {
private final List<UnfoldListener> mListeners = new ArrayList<>();
@Override
public void addListener(Executor executor, UnfoldListener listener) {
mListeners.add(listener);
}
@Override
public void onStateChangeStarted() {
mListeners.forEach(UnfoldListener::onStateChangeStarted);
}
@Override
public void onStateChangeProgress(float progress) {
mListeners.forEach(unfoldListener -> unfoldListener.onStateChangeProgress(progress));
}
@Override
public void onStateChangeFinished() {
mListeners.forEach(UnfoldListener::onStateChangeFinished);
}
}
public static class TestUnfoldTaskAnimator implements UnfoldTaskAnimator {
public static final float UNSET = -1f;
private Predicate<TaskInfo> mTaskMatcher = (info) -> false;
Map<Integer, TaskInfo> mTasksMap = new HashMap<>();
Set<Integer> mResetTasks = new HashSet<>();
boolean mInitialized = false;
boolean mStarted = false;
float mLastAppliedProgress = UNSET;
@Override
public void init() {
mInitialized = true;
}
@Override
public void start() {
mStarted = true;
}
@Override
public void stop() {
mStarted = false;
}
@Override
public boolean isApplicableTask(TaskInfo taskInfo) {
return mTaskMatcher.test(taskInfo);
}
@Override
public void applyAnimationProgress(float progress, Transaction transaction) {
mLastAppliedProgress = progress;
}
public void setTaskMatcher(Predicate<TaskInfo> taskMatcher) {
mTaskMatcher = taskMatcher;
}
@Override
public void onTaskAppeared(TaskInfo taskInfo, SurfaceControl leash) {
mTasksMap.put(taskInfo.taskId, taskInfo);
}
@Override
public void onTaskVanished(TaskInfo taskInfo) {
mTasksMap.remove(taskInfo.taskId);
}
@Override
public void onTaskChanged(TaskInfo taskInfo) {
mTasksMap.put(taskInfo.taskId, taskInfo);
}
@Override
public void resetSurface(TaskInfo taskInfo, Transaction transaction) {
mResetTasks.add(taskInfo.taskId);
}
@Override
public void resetAllSurfaces(Transaction transaction) {
mTasksMap.values().forEach((t) -> mResetTasks.add(t.taskId));
}
@Override
public boolean hasActiveTasks() {
return mTasksMap.size() > 0;
}
public List<TaskInfo> getCurrentTasks() {
return new ArrayList<>(mTasksMap.values());
}
}
}