Merge remote-tracking branch 'aosp/android12L-release' into 12.1-dev

This commit is contained in:
Suphon Thanakornpakapong
2022-05-08 20:29:17 +07:00
825 changed files with 38324 additions and 29270 deletions
@@ -0,0 +1,226 @@
/*
* Copyright (C) 2021 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.launcher3.model;
import static android.os.Process.myUserHandle;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetId;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@SmallTest
@RunWith(AndroidJUnit4.class)
public final class WidgetsPredicationUpdateTaskTest {
private AppWidgetProviderInfo mApp1Provider1;
private AppWidgetProviderInfo mApp1Provider2;
private AppWidgetProviderInfo mApp2Provider1;
private AppWidgetProviderInfo mApp4Provider1;
private AppWidgetProviderInfo mApp4Provider2;
private AppWidgetProviderInfo mApp5Provider1;
private List<AppWidgetProviderInfo> allWidgets;
private FakeBgDataModelCallback mCallback = new FakeBgDataModelCallback();
private LauncherModelHelper mModelHelper;
private UserHandle mUserHandle;
@Mock
private IconCache mIconCache;
@Before
public void setup() throws Exception {
mModelHelper = new LauncherModelHelper();
MockitoAnnotations.initMocks(this);
doAnswer(invocation -> {
ComponentWithLabel componentWithLabel = invocation.getArgument(0);
return componentWithLabel.getComponent().getShortClassName();
}).when(mIconCache).getTitleNoCache(any());
mUserHandle = myUserHandle();
mApp1Provider1 = createAppWidgetProviderInfo(
ComponentName.createRelative("app1", "provider1"));
mApp1Provider2 = createAppWidgetProviderInfo(
ComponentName.createRelative("app1", "provider2"));
mApp2Provider1 = createAppWidgetProviderInfo(
ComponentName.createRelative("app2", "provider1"));
mApp4Provider1 = createAppWidgetProviderInfo(
ComponentName.createRelative("app4", "provider1"));
mApp4Provider2 = createAppWidgetProviderInfo(
ComponentName.createRelative("app4", ".provider2"));
mApp5Provider1 = createAppWidgetProviderInfo(
ComponentName.createRelative("app5", "provider1"));
allWidgets = Arrays.asList(mApp1Provider1, mApp1Provider2, mApp2Provider1,
mApp4Provider1, mApp4Provider2, mApp5Provider1);
AppWidgetManager manager = mModelHelper.sandboxContext.spyService(AppWidgetManager.class);
doReturn(allWidgets).when(manager).getInstalledProviders();
doReturn(allWidgets).when(manager).getInstalledProvidersForProfile(eq(myUserHandle()));
doAnswer(i -> {
String pkg = i.getArgument(0);
Log.e("Hello", "Getting v " + pkg);
return TextUtils.isEmpty(pkg) ? allWidgets : allWidgets.stream()
.filter(a -> pkg.equals(a.provider.getPackageName()))
.collect(Collectors.toList());
}).when(manager).getInstalledProvidersForPackage(any(), eq(myUserHandle()));
// 2 widgets, app4/provider1 & app5/provider1, have already been added to the workspace.
mModelHelper.initializeData("widgets_predication_update_task_data");
MAIN_EXECUTOR.submit(() -> mModelHelper.getModel().addCallbacks(mCallback)).get();
MODEL_EXECUTOR.post(() -> mModelHelper.getBgDataModel().widgetsModel.update(
LauncherAppState.getInstance(mModelHelper.sandboxContext),
/* packageUser= */ null));
MODEL_EXECUTOR.submit(() -> { }).get();
MAIN_EXECUTOR.submit(() -> { }).get();
}
@After
public void tearDown() {
mModelHelper.destroy();
}
@Test
public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder()
throws Exception {
// WHEN newPredicationTask is executed with app predication of 5 apps.
AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "className",
mUserHandle);
AppTarget app2 = new AppTarget(new AppTargetId("app2"), "app2", "className",
mUserHandle);
AppTarget app3 = new AppTarget(new AppTargetId("app3"), "app3", "className",
mUserHandle);
AppTarget app4 = new AppTarget(new AppTargetId("app4"), "app4", "className",
mUserHandle);
AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "className",
mUserHandle);
mModelHelper.executeTaskForTest(
newWidgetsPredicationTask(List.of(app5, app3, app2, app4, app1)))
.forEach(Runnable::run);
// THEN only 3 widgets are returned because
// 1. app5/provider1 & app4/provider1 have already been added to workspace. They are
// excluded from the result.
// 2. app3 doesn't have a widget.
// 3. only 1 widget is picked from app1 because we only want to promote one widget per app.
List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
.stream()
.map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
.collect(Collectors.toList());
assertThat(recommendedWidgets).hasSize(3);
assertWidgetInfo(recommendedWidgets.get(0).info, mApp2Provider1);
assertWidgetInfo(recommendedWidgets.get(1).info, mApp4Provider2);
assertWidgetInfo(recommendedWidgets.get(2).info, mApp1Provider1);
}
@Test
public void widgetsRecommendationRan_localFilterDisabled_shouldReturnWidgetsInPredicationOrder()
throws Exception {
if (FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER.get()) {
return;
}
// WHEN newPredicationTask is executed with 5 predicated widgets.
AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1",
mUserHandle);
AppTarget widget2 = new AppTarget(new AppTargetId("app1"), "app1", "provider2",
mUserHandle);
// Not installed app
AppTarget widget3 = new AppTarget(new AppTargetId("app2"), "app3", "provider1",
mUserHandle);
// Not installed widget
AppTarget widget4 = new AppTarget(new AppTargetId("app4"), "app4", "provider3",
mUserHandle);
AppTarget widget5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
mUserHandle);
mModelHelper.executeTaskForTest(
newWidgetsPredicationTask(List.of(widget5, widget3, widget2, widget4, widget1)))
.forEach(Runnable::run);
// THEN only 3 widgets are returned because the launcher only filters out non-exist widgets.
List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
.stream()
.map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
.collect(Collectors.toList());
assertThat(recommendedWidgets).hasSize(3);
assertWidgetInfo(recommendedWidgets.get(0).info, mApp5Provider1);
assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider2);
assertWidgetInfo(recommendedWidgets.get(2).info, mApp1Provider1);
}
private void assertWidgetInfo(
LauncherAppWidgetProviderInfo actual, AppWidgetProviderInfo expected) {
assertThat(actual.provider).isEqualTo(expected.provider);
assertThat(actual.getUser()).isEqualTo(expected.getProfile());
}
private WidgetsPredictionUpdateTask newWidgetsPredicationTask(List<AppTarget> appTargets) {
return new WidgetsPredictionUpdateTask(
new PredictorState(CONTAINER_WIDGETS_PREDICTION, "test_widgets_prediction"),
appTargets);
}
private final class FakeBgDataModelCallback implements BgDataModel.Callbacks {
private FixedContainerItems mRecommendedWidgets = null;
@Override
public void bindExtraContainerItems(FixedContainerItems item) {
mRecommendedWidgets = item;
}
}
}
@@ -0,0 +1,159 @@
package com.android.launcher3.taskbar;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.SCREEN_PIN_LONG_PRESS_THRESHOLD;
import static com.android.quickstep.OverviewCommandHelper.TYPE_HOME;
import static com.android.quickstep.OverviewCommandHelper.TYPE_TOGGLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.os.Handler;
import androidx.test.runner.AndroidJUnit4;
import com.android.quickstep.OverviewCommandHelper;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TouchInteractionService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class TaskbarNavButtonControllerTest {
private final static int DISPLAY_ID = 2;
@Mock
SystemUiProxy mockSystemUiProxy;
@Mock
TouchInteractionService mockService;
@Mock
OverviewCommandHelper mockCommandHelper;
@Mock
Handler mockHandler;
private TaskbarNavButtonController mNavButtonController;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
when(mockService.getDisplayId()).thenReturn(DISPLAY_ID);
when(mockService.getOverviewCommandHelper()).thenReturn(mockCommandHelper);
mNavButtonController = new TaskbarNavButtonController(mockService,
mockSystemUiProxy, mockHandler);
}
@Test
public void testPressBack() {
mNavButtonController.onButtonClick(BUTTON_BACK);
verify(mockSystemUiProxy, times(1)).onBackPressed();
}
@Test
public void testPressImeSwitcher() {
mNavButtonController.onButtonClick(BUTTON_IME_SWITCH);
verify(mockSystemUiProxy, times(1)).onImeSwitcherPressed();
}
@Test
public void testPressA11yShortClick() {
mNavButtonController.onButtonClick(BUTTON_A11Y);
verify(mockSystemUiProxy, times(1))
.notifyAccessibilityButtonClicked(DISPLAY_ID);
}
@Test
public void testPressA11yLongClick() {
mNavButtonController.onButtonLongClick(BUTTON_A11Y);
verify(mockSystemUiProxy, times(1)).notifyAccessibilityButtonLongClicked();
}
@Test
public void testLongPressHome() {
mNavButtonController.onButtonLongClick(BUTTON_HOME);
verify(mockSystemUiProxy, times(1)).startAssistant(any());
}
@Test
public void testPressHome() {
mNavButtonController.onButtonClick(BUTTON_HOME);
verify(mockCommandHelper, times(1)).addCommand(TYPE_HOME);
}
@Test
public void testPressRecents() {
mNavButtonController.onButtonClick(BUTTON_RECENTS);
verify(mockCommandHelper, times(1)).addCommand(TYPE_TOGGLE);
}
@Test
public void testPressRecentsWithScreenPinned() {
mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
mNavButtonController.onButtonClick(BUTTON_RECENTS);
verify(mockCommandHelper, times(0)).addCommand(TYPE_TOGGLE);
}
@Test
public void testLongPressBackRecentsNotPinned() {
mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
mNavButtonController.onButtonLongClick(BUTTON_BACK);
verify(mockSystemUiProxy, times(0)).stopScreenPinning();
}
@Test
public void testLongPressBackRecentsPinned() {
mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
mNavButtonController.onButtonLongClick(BUTTON_BACK);
verify(mockSystemUiProxy, times(1)).stopScreenPinning();
}
@Test
public void testLongPressBackRecentsTooLongPinned() {
mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
try {
Thread.sleep(SCREEN_PIN_LONG_PRESS_THRESHOLD + 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
mNavButtonController.onButtonLongClick(BUTTON_BACK);
verify(mockSystemUiProxy, times(0)).stopScreenPinning();
}
@Test
public void testLongPressBackRecentsMultipleAttemptPinned() {
mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
try {
Thread.sleep(SCREEN_PIN_LONG_PRESS_THRESHOLD + 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
mNavButtonController.onButtonLongClick(BUTTON_BACK);
verify(mockSystemUiProxy, times(0)).stopScreenPinning();
// Try again w/in threshold
mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
mNavButtonController.onButtonLongClick(BUTTON_BACK);
verify(mockSystemUiProxy, times(1)).stopScreenPinning();
}
@Test
public void testLongPressHomeScreenPinned() {
mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
mNavButtonController.onButtonLongClick(BUTTON_HOME);
verify(mockSystemUiProxy, times(0)).startAssistant(any());
}
}
@@ -44,7 +44,7 @@ public abstract class AbstractQuickStepTest extends AbstractLauncherUiTest {
protected void onLauncherActivityClose(Launcher launcher) {
RecentsView recentsView = launcher.getOverviewPanel();
if (recentsView != null) {
recentsView.finishRecentsAnimation(true, null);
recentsView.finishRecentsAnimation(false /* toRecents */, null);
}
}
@@ -82,6 +82,6 @@ public abstract class AbstractQuickStepTest extends AbstractLauncherUiTest {
RecentsView recentsView = launcher.getOverviewPanel();
return recentsView.getSizeStrategy().isInLiveTileMode()
&& recentsView.getRunningTaskId() != -1;
&& recentsView.getRunningTaskViewId() != -1;
}
}
@@ -42,7 +42,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.RemoteException;
import android.util.Log;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -56,7 +55,6 @@ import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.tapl.OverviewTask;
import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.testcomponent.TestCommandReceiver;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.FailureWatcher;
import com.android.quickstep.views.RecentsView;
@@ -98,6 +96,7 @@ public class FallbackRecentsTest {
mDevice = UiDevice.getInstance(instrumentation);
mDevice.setOrientationNatural();
mLauncher = new LauncherInstrumentation();
mLauncher.enableDebugTracing();
// b/143488140
//mLauncher.enableCheckEventsForSuccessfulGestures();
@@ -187,15 +186,9 @@ public class FallbackRecentsTest {
protected <T> T getFromRecents(Function<RecentsActivity, T> f) {
if (!TestHelpers.isInLauncherProcess()) return null;
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.FALLBACK_ACTIVITY_NO_SET, "getFromRecents");
}
Object[] result = new Object[1];
Wait.atMost("Failed to get from recents", () -> MAIN_EXECUTOR.submit(() -> {
RecentsActivity activity = RecentsActivity.ACTIVITY_TRACKER.getCreatedActivity();
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.FALLBACK_ACTIVITY_NO_SET, "activity=" + activity);
}
if (activity == null) {
return false;
}
@@ -0,0 +1,124 @@
/*
* Copyright 2021 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.quickstep;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.view.View;
import android.view.WindowInsetsController;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.shared.rotation.RotationButton;
import com.android.systemui.shared.rotation.RotationButtonController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
/** SysUI equivalent */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class NavigationBarRotationContextTest {
private static final int DEFAULT_ROTATE = 0;
private static final int DEFAULT_DISPLAY = 0;
private RotationButtonController mRotationButtonController;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
Context mTargetContext = InstrumentationRegistry.getTargetContext();
final View view = new View(mTargetContext);
RotationButton rotationButton = mock(RotationButton.class);
mRotationButtonController = new RotationButtonController(mTargetContext, 0, 0, 0, 0, 0, 0,
() -> 0);
mRotationButtonController.setRotationButton(rotationButton, null);
// Due to a mockito issue, only spy the object after setting the initial state
mRotationButtonController = spy(mRotationButtonController);
final AnimatedVectorDrawable kbd = mock(AnimatedVectorDrawable.class);
doReturn(view).when(rotationButton).getCurrentView();
doReturn(true).when(rotationButton).acceptRotationProposal();
}
@Test
public void testOnInvalidRotationProposal() {
mRotationButtonController.onRotationProposal(DEFAULT_ROTATE + 1,
false /* isValid */);
verify(mRotationButtonController, times(1))
.setRotateSuggestionButtonState(false /* visible */);
}
@Test
public void testOnSameRotationProposal() {
mRotationButtonController.onRotationProposal(DEFAULT_ROTATE,
true /* isValid */);
verify(mRotationButtonController, times(1))
.setRotateSuggestionButtonState(false /* visible */);
}
@Test
public void testOnRotationProposalShowButtonShowNav() {
// No navigation bar should not call to set visibility state
mRotationButtonController.onBehaviorChanged(DEFAULT_DISPLAY,
WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
mRotationButtonController.onNavigationBarWindowVisibilityChange(false /* showing */);
verify(mRotationButtonController, times(0)).setRotateSuggestionButtonState(
false /* visible */);
verify(mRotationButtonController, times(0)).setRotateSuggestionButtonState(
true /* visible */);
// No navigation bar with rotation change should not call to set visibility state
mRotationButtonController.onRotationProposal(DEFAULT_ROTATE + 1,
true /* isValid */);
verify(mRotationButtonController, times(0)).setRotateSuggestionButtonState(
false /* visible */);
verify(mRotationButtonController, times(0)).setRotateSuggestionButtonState(
true /* visible */);
// Since rotation has changed rotation should be pending, show mButton when showing nav bar
mRotationButtonController.onNavigationBarWindowVisibilityChange(true /* showing */);
verify(mRotationButtonController, times(1)).setRotateSuggestionButtonState(
true /* visible */);
}
@Test
public void testOnRotationProposalShowButton() {
// Navigation bar being visible should not call to set visibility state
mRotationButtonController.onNavigationBarWindowVisibilityChange(true /* showing */);
verify(mRotationButtonController, times(0))
.setRotateSuggestionButtonState(false /* visible */);
verify(mRotationButtonController, times(0))
.setRotateSuggestionButtonState(true /* visible */);
// Navigation bar is visible and rotation requested
mRotationButtonController.onRotationProposal(DEFAULT_ROTATE + 1,
true /* isValid */);
verify(mRotationButtonController, times(1))
.setRotateSuggestionButtonState(true /* visible */);
}
}
@@ -30,7 +30,6 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.util.Log;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiDevice;
import com.android.launcher3.tapl.LauncherInstrumentation;
@@ -49,6 +48,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Test rule that allows executing a test with Quickstep on and then Quickstep off.
@@ -182,17 +182,12 @@ public class NavigationModeSwitchRule implements TestRule {
};
targetContext.getMainExecutor().execute(() ->
SYS_UI_NAVIGATION_MODE.addModeChangeListener(listener));
// b/139137636
// latch.await(60, TimeUnit.SECONDS);
latch.await(60, TimeUnit.SECONDS);
targetContext.getMainExecutor().execute(() ->
SYS_UI_NAVIGATION_MODE.removeModeChangeListener(listener));
Wait.atMost(() -> "Navigation mode didn't change to " + expectedMode,
() -> currentSysUiNavigationMode() == expectedMode, WAIT_TIME_MS,
launcher);
// b/139137636
// assertTrue(launcher, "Navigation mode didn't change to " + expectedMode,
// currentSysUiNavigationMode() == expectedMode, description);
assertTrue(launcher, "Navigation mode didn't change to " + expectedMode,
currentSysUiNavigationMode() == expectedMode, description);
}
@@ -221,12 +216,7 @@ public class NavigationModeSwitchRule implements TestRule {
private static void assertTrue(LauncherInstrumentation launcher, String message,
boolean condition, Description description) {
if (launcher.getDevice().hasObject(By.textStartsWith(""))) {
// The condition above is "screen is not empty". We are not treating
// "Screen is empty" as an anomaly here. It's an acceptable state when
// Launcher just starts under instrumentation.
launcher.checkForAnomaly();
}
launcher.checkForAnomaly(true, true);
if (!condition) {
final AssertionError assertionError = new AssertionError(message);
if (description != null) {
@@ -0,0 +1,355 @@
/*
* Copyright (C) 2019 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.quickstep;
import static android.view.Display.DEFAULT_DISPLAY;
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
import android.hardware.display.DisplayManager;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.MotionEvent;
import android.view.Surface;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.util.DisplayController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class OrientationTouchTransformerTest {
static class ScreenSize {
int mHeight;
int mWidth;
ScreenSize(int height, int width) {
mHeight = height;
mWidth = width;
}
}
private static final ScreenSize NORMAL_SCREEN_SIZE = new ScreenSize(2280, 1080);
private static final ScreenSize LARGE_SCREEN_SIZE = new ScreenSize(3280, 1080);
private static final float DENSITY_DISPLAY_METRICS = 3.0f;
private OrientationTouchTransformer mTouchTransformer;
Resources mResources;
private DisplayController.Info mInfo;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mResources = mock(Resources.class);
when(mResources.getBoolean(anyInt())).thenReturn(true);
when(mResources.getDimension(anyInt())).thenReturn(10.0f);
when(mResources.getDimensionPixelSize(anyInt())).thenReturn(10);
DisplayMetrics mockDisplayMetrics = new DisplayMetrics();
mockDisplayMetrics.density = DENSITY_DISPLAY_METRICS;
when(mResources.getDisplayMetrics()).thenReturn(mockDisplayMetrics);
mInfo = createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_0);
mTouchTransformer = new OrientationTouchTransformer(mResources, NO_BUTTON, () -> 0);
}
@Test
public void disabledMultipleRegions_shouldOverrideFirstRegion() {
float portraitRegionY =
generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
float landscapeRegionY =
generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_90) + 1;
mTouchTransformer.createOrAddTouchRegion(mInfo);
tapAndAssertTrue(100, portraitRegionY,
event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
tapAndAssertFalse(100, landscapeRegionY,
event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
tapAndAssertTrue(0, portraitRegionY,
event -> mTouchTransformer.touchInAssistantRegion(event));
tapAndAssertFalse(0, landscapeRegionY,
event -> mTouchTransformer.touchInAssistantRegion(event));
// Override region
mTouchTransformer
.createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
tapAndAssertFalse(100, portraitRegionY,
event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
tapAndAssertTrue(100, landscapeRegionY,
event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
tapAndAssertFalse(0, portraitRegionY,
event -> mTouchTransformer.touchInAssistantRegion(event));
tapAndAssertTrue(0, landscapeRegionY,
event -> mTouchTransformer.touchInAssistantRegion(event));
// Override region again
mTouchTransformer.createOrAddTouchRegion(mInfo);
tapAndAssertTrue(100, portraitRegionY,
event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
tapAndAssertFalse(100, landscapeRegionY,
event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
tapAndAssertTrue(0, portraitRegionY,
event -> mTouchTransformer.touchInAssistantRegion(event));
tapAndAssertFalse(0, landscapeRegionY,
event -> mTouchTransformer.touchInAssistantRegion(event));
}
@Test
public void enableMultipleRegions_shouldOverrideFirstRegion() {
float portraitRegionY =
generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
float landscapeRegionY =
generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_90) + 1;
mTouchTransformer
.createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
tapAndAssertFalse(100, portraitRegionY,
event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
tapAndAssertTrue(100, landscapeRegionY,
event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
tapAndAssertFalse(0, portraitRegionY,
event -> mTouchTransformer.touchInAssistantRegion(event));
tapAndAssertTrue(0, landscapeRegionY,
event -> mTouchTransformer.touchInAssistantRegion(event));
// We have to add 0 rotation second so that gets set as the current rotation, otherwise
// matrix transform will fail (tests only work in Portrait at the moment)
mTouchTransformer.enableMultipleRegions(true, mInfo);
mTouchTransformer.createOrAddTouchRegion(mInfo);
tapAndAssertTrue(100, portraitRegionY,
event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
tapAndAssertFalse(100, landscapeRegionY,
event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
tapAndAssertTrue(0, portraitRegionY,
event -> mTouchTransformer.touchInAssistantRegion(event));
tapAndAssertFalse(0, landscapeRegionY,
event -> mTouchTransformer.touchInAssistantRegion(event));
}
@Test
public void enableMultipleRegions_assistantTriggersInMostRecent() {
float portraitRegionY =
generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
float landscapeRegionY =
generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_90) + 1;
mTouchTransformer.enableMultipleRegions(true, mInfo);
mTouchTransformer
.createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
mTouchTransformer.createOrAddTouchRegion(mInfo);
tapAndAssertTrue(0, portraitRegionY,
event -> mTouchTransformer.touchInAssistantRegion(event));
tapAndAssertFalse(0, landscapeRegionY,
event -> mTouchTransformer.touchInAssistantRegion(event));
}
@Test
public void enableMultipleRegions_assistantTriggersInCurrentOrientationAfterDisable() {
float portraitRegionY =
generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
float landscapeRegionY =
generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_90) + 1;
mTouchTransformer.enableMultipleRegions(true, mInfo);
mTouchTransformer.createOrAddTouchRegion(mInfo);
mTouchTransformer
.createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
mTouchTransformer.enableMultipleRegions(false, mInfo);
tapAndAssertTrue(0, portraitRegionY,
event -> mTouchTransformer.touchInAssistantRegion(event));
tapAndAssertFalse(0, landscapeRegionY,
event -> mTouchTransformer.touchInAssistantRegion(event));
}
@Test
public void assistantTriggersInCurrentScreenAfterScreenSizeChange() {
float smallerScreenPortraitRegionY =
generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
float largerScreenPortraitRegionY =
generateTouchRegionHeight(LARGE_SCREEN_SIZE, Surface.ROTATION_0) + 1;
mTouchTransformer.enableMultipleRegions(false,
createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_0));
tapAndAssertTrue(0, smallerScreenPortraitRegionY,
event -> mTouchTransformer.touchInAssistantRegion(event));
mTouchTransformer
.enableMultipleRegions(false, createDisplayInfo(LARGE_SCREEN_SIZE, Surface.ROTATION_0));
tapAndAssertTrue(0, largerScreenPortraitRegionY,
event -> mTouchTransformer.touchInAssistantRegion(event));
tapAndAssertFalse(0, smallerScreenPortraitRegionY,
event -> mTouchTransformer.touchInAssistantRegion(event));
}
@Test
public void applyTransform_taskNotFrozen_notInRegion() {
mTouchTransformer.createOrAddTouchRegion(mInfo);
tapAndAssertFalse(100, 100,
event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
}
@Test
public void applyTransform_taskFrozen_noRotate_outOfRegion() {
mTouchTransformer.createOrAddTouchRegion(mInfo);
mTouchTransformer.enableMultipleRegions(true, mInfo);
tapAndAssertFalse(100, 100,
event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
}
@Test
public void applyTransform_taskFrozen_noRotate_inRegion() {
mTouchTransformer.createOrAddTouchRegion(mInfo);
mTouchTransformer.enableMultipleRegions(true, mInfo);
float y = generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
tapAndAssertTrue(100, y,
event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
}
@Test
public void applyTransform_taskNotFrozen_noRotate_inDefaultRegion() {
mTouchTransformer.createOrAddTouchRegion(mInfo);
float y = generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
tapAndAssertTrue(100, y,
event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
}
@Test
public void applyTransform_taskNotFrozen_90Rotate_inRegion() {
mTouchTransformer
.createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
float y = generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_90) + 1;
tapAndAssertTrue(100, y,
event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
}
@Test
public void applyTransform_taskNotFrozen_90Rotate_withTwoRegions() {
mTouchTransformer.createOrAddTouchRegion(mInfo);
mTouchTransformer.enableMultipleRegions(true, mInfo);
mTouchTransformer
.createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
// Landscape point
float y1 = generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_90) + 1;
MotionEvent inRegion1_down = generateMotionEvent(MotionEvent.ACTION_DOWN, 10, y1);
MotionEvent inRegion1_up = generateMotionEvent(MotionEvent.ACTION_UP, 10, y1);
// Portrait point in landscape orientation axis
MotionEvent inRegion2 = generateMotionEvent(MotionEvent.ACTION_DOWN, 10, 10);
mTouchTransformer.transform(inRegion1_down);
// no-op
mTouchTransformer.transform(inRegion2);
assertTrue(mTouchTransformer.touchInValidSwipeRegions(
inRegion1_down.getX(), inRegion1_down.getY()));
// We only process one gesture region until we see a MotionEvent.ACTION_UP
assertFalse(mTouchTransformer.touchInValidSwipeRegions(inRegion2.getX(), inRegion2.getY()));
mTouchTransformer.transform(inRegion1_up);
}
@Test
public void applyTransform_90Rotate_inRotatedRegion() {
// Create regions for both 0 Rotation and 90 Rotation
mTouchTransformer.createOrAddTouchRegion(mInfo);
mTouchTransformer.enableMultipleRegions(true, mInfo);
mTouchTransformer
.createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
// Portrait point in landscape orientation axis
float x1 = generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0);
// bottom of screen, from landscape perspective right side of screen
MotionEvent inRegion2 = generateAndTransformMotionEvent(MotionEvent.ACTION_DOWN, x1, 370);
assertTrue(mTouchTransformer.touchInValidSwipeRegions(inRegion2.getX(), inRegion2.getY()));
}
private DisplayController.Info createDisplayInfo(ScreenSize screenSize, int rotation) {
Context context = getApplicationContext();
Display display = spy(context.getSystemService(DisplayManager.class)
.getDisplay(DEFAULT_DISPLAY));
Point p = new Point(screenSize.mWidth, screenSize.mHeight);
if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
p.set(screenSize.mHeight, screenSize.mWidth);
}
doReturn(rotation).when(display).getRotation();
doAnswer(i -> {
((Point) i.getArgument(0)).set(p.x, p.y);
return null;
}).when(display).getRealSize(any(Point.class));
doAnswer(i -> {
((Point) i.getArgument(0)).set(p.x, p.y);
((Point) i.getArgument(1)).set(p.x, p.y);
return null;
}).when(display).getCurrentSizeRange(any(Point.class), any(Point.class));
return new DisplayController.Info(context, display);
}
private float generateTouchRegionHeight(ScreenSize screenSize, int rotation) {
float height = screenSize.mHeight;
if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
height = screenSize.mWidth;
}
return height - ResourceUtils.DEFAULT_NAVBAR_VALUE * DENSITY_DISPLAY_METRICS;
}
private MotionEvent generateMotionEvent(int motionAction, float x, float y) {
return MotionEvent.obtain(0, 0, motionAction, x, y, 0);
}
private MotionEvent generateAndTransformMotionEvent(int motionAction, float x, float y) {
MotionEvent motionEvent = generateMotionEvent(motionAction, x, y);
mTouchTransformer.transform(motionEvent);
return motionEvent;
}
private void tapAndAssertTrue(float x, float y, MotionEventAssertion assertion) {
MotionEvent motionEvent = generateAndTransformMotionEvent(MotionEvent.ACTION_DOWN, x, y);
assertTrue(assertion.getCondition(motionEvent));
generateAndTransformMotionEvent(MotionEvent.ACTION_UP, x, y);
}
private void tapAndAssertFalse(float x, float y, MotionEventAssertion assertion) {
MotionEvent motionEvent = generateAndTransformMotionEvent(MotionEvent.ACTION_DOWN, x, y);
assertFalse(assertion.getCondition(motionEvent));
generateAndTransformMotionEvent(MotionEvent.ACTION_UP, x, y);
}
private interface MotionEventAssertion {
boolean getCondition(MotionEvent motionEvent);
}
}
@@ -30,70 +30,76 @@ import android.app.ActivityManager;
import androidx.test.filters.SmallTest;
import com.android.launcher3.util.LooperExecutor;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.quickstep.util.GroupTask;
import com.android.systemui.shared.system.KeyguardManagerCompat;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@SmallTest
public class RecentTasksListTest {
private ActivityManagerWrapper mockActivityManagerWrapper;
@Mock
private SystemUiProxy mockSystemUiProxy;
// Class under test
private RecentTasksList mRecentTasksList;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
LooperExecutor mockMainThreadExecutor = mock(LooperExecutor.class);
KeyguardManagerCompat mockKeyguardManagerCompat = mock(KeyguardManagerCompat.class);
mockActivityManagerWrapper = mock(ActivityManagerWrapper.class);
mRecentTasksList = new RecentTasksList(mockMainThreadExecutor, mockKeyguardManagerCompat,
mockActivityManagerWrapper);
mockSystemUiProxy);
}
@Test
public void onTaskRemoved_doesNotFetchTasks() {
mRecentTasksList.onTaskRemoved(0);
verify(mockActivityManagerWrapper, times(0))
.getRecentTasks(anyInt(), anyInt());
}
@Test
public void onTaskStackChanged_doesNotFetchTasks() {
mRecentTasksList.onTaskStackChanged();
verify(mockActivityManagerWrapper, times(0))
public void onRecentTasksChanged_doesNotFetchTasks() {
mRecentTasksList.onRecentTasksChanged();
verify(mockSystemUiProxy, times(0))
.getRecentTasks(anyInt(), anyInt());
}
@Test
public void loadTasksInBackground_onlyKeys_noValidTaskDescription() {
ActivityManager.RecentTaskInfo recentTaskInfo = new ActivityManager.RecentTaskInfo();
when(mockActivityManagerWrapper.getRecentTasks(anyInt(), anyInt()))
.thenReturn(Collections.singletonList(recentTaskInfo));
GroupedRecentTaskInfo recentTaskInfos = new GroupedRecentTaskInfo(
new ActivityManager.RecentTaskInfo(), new ActivityManager.RecentTaskInfo(), null);
when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
List<Task> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1, true);
List<GroupTask> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1,
true);
assertEquals(1, taskList.size());
assertNull(taskList.get(0).taskDescription.getLabel());
assertNull(taskList.get(0).task1.taskDescription.getLabel());
assertNull(taskList.get(0).task2.taskDescription.getLabel());
}
@Test
public void loadTasksInBackground_moreThanKeys_hasValidTaskDescription() {
String taskDescription = "Wheeee!";
ActivityManager.RecentTaskInfo recentTaskInfo = new ActivityManager.RecentTaskInfo();
recentTaskInfo.taskDescription = new ActivityManager.TaskDescription(taskDescription);
when(mockActivityManagerWrapper.getRecentTasks(anyInt(), anyInt()))
.thenReturn(Collections.singletonList(recentTaskInfo));
ActivityManager.RecentTaskInfo task1 = new ActivityManager.RecentTaskInfo();
task1.taskDescription = new ActivityManager.TaskDescription(taskDescription);
ActivityManager.RecentTaskInfo task2 = new ActivityManager.RecentTaskInfo();
task2.taskDescription = new ActivityManager.TaskDescription();
GroupedRecentTaskInfo recentTaskInfos = new GroupedRecentTaskInfo(
task1, task2, null);
when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
List<Task> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1, false);
List<GroupTask> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, -1,
false);
assertEquals(1, taskList.size());
assertEquals(taskDescription, taskList.get(0).taskDescription.getLabel());
assertEquals(taskDescription, taskList.get(0).task1.taskDescription.getLabel());
assertNull(taskList.get(0).task2.taskDescription.getLabel());
}
}
@@ -25,6 +25,7 @@ import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.Launcher;
import com.android.launcher3.ui.TaplTestsLauncher3;
import com.android.launcher3.util.RaceConditionReproducer;
import com.android.quickstep.NavigationModeSwitchRule.Mode;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
@@ -45,6 +46,7 @@ public class StartLauncherViaGestureTests extends AbstractQuickStepTest {
@Before
public void setUp() throws Exception {
super.setUp();
TaplTestsLauncher3.initialize(this);
// b/143488140
mLauncher.pressHome();
// Start an activity where the gestures start.
@@ -23,7 +23,6 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.content.Intent;
import android.util.Log;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -32,13 +31,15 @@ import androidx.test.uiautomator.Until;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.tapl.AllApps;
import com.android.launcher3.tapl.Background;
import com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel;
import com.android.launcher3.tapl.Overview;
import com.android.launcher3.tapl.OverviewActions;
import com.android.launcher3.tapl.OverviewTask;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.ui.TaplTestsLauncher3;
import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import com.android.quickstep.views.RecentsView;
@@ -51,6 +52,9 @@ import org.junit.runner.RunWith;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class TaplTestsQuickstep extends AbstractQuickStepTest {
private static final String APP_NAME = "LauncherTestApp";
@Before
public void setUp() throws Exception {
super.setUp();
@@ -146,13 +150,10 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
launcher -> assertEquals("Dismissing a task didn't remove 1 task from Overview",
numTasks - 1, getTaskCount(launcher)));
// Test UIDevice.pressHome, once we are in AllApps.
mDevice.pressHome();
waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
// Test dismissing all tasks.
mLauncher.getWorkspace().switchToOverview().dismissAllTasks();
waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
mLauncher.pressHome().switchToOverview().dismissAllTasks();
assertTrue("Launcher internal state is not Home",
isInState(() -> LauncherState.NORMAL));
executeOnLauncher(
launcher -> assertEquals("Still have tasks after dismissing all",
0, getTaskCount(launcher)));
@@ -164,6 +165,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
@Test
@NavigationModeSwitch
@PortraitLandscape
@ScreenRecord // b/195673272
public void testOverviewActions() throws Exception {
// Experimenting for b/165029151:
final Overview overview = mLauncher.pressHome().switchToOverview();
@@ -175,7 +177,6 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
OverviewActions actionsView =
mLauncher.pressHome().switchToOverview().getOverviewActions();
actionsView.clickAndDismissScreenshot();
actionsView.clickAndDismissShare();
}
private int getCurrentOverviewPage(Launcher launcher) {
@@ -186,6 +187,14 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
return launcher.<RecentsView>getOverviewPanel().getTaskViewCount();
}
private int getTopRowTaskCountForTablet(Launcher launcher) {
return launcher.<RecentsView>getOverviewPanel().getTopRowTaskCountForTablet();
}
private int getBottomRowTaskCountForTablet(Launcher launcher) {
return launcher.<RecentsView>getOverviewPanel().getBottomRowTaskCountForTablet();
}
@Test
@NavigationModeSwitch
@PortraitLandscape
@@ -264,7 +273,9 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
background.quickSwitchToPreviousAppSwipeLeft();
assertTrue("The 2nd app we should have quick switched to is not running",
isTestActivityRunning(3));
getAndAssertBackground();
background = getAndAssertBackground();
background.switchToOverview();
}
private boolean isTestActivityRunning(int activityNumber) {
@@ -283,4 +294,96 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
isTestActivityRunning(2));
getAndAssertBackground();
}
// TODO(b/204830798): test with all navigation modes(add @NavigationModeSwitch annotation)
// after the bug resolved.
@Ignore("b/205027405")
@Test
@PortraitLandscape
@ScreenRecord
public void testPressBack() throws Exception {
mLauncher.getWorkspace().switchToAllApps();
mLauncher.pressBack();
mLauncher.getWorkspace();
waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
allApps.freeze();
try {
allApps.getAppIcon(APP_NAME).dragToWorkspace(false, false);
} finally {
allApps.unfreeze();
}
mLauncher.getWorkspace().getWorkspaceAppIcon(APP_NAME).launch(getAppPackageName());
mLauncher.pressBack();
mLauncher.getWorkspace();
waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
}
@Test
@PortraitLandscape
public void testOverviewForTablet() throws Exception {
// TODO(b/210158657): Re-enable for OOP
if (!mLauncher.isTablet() || !TestHelpers.isInLauncherProcess()) {
return;
}
for (int i = 2; i <= 14; i++) {
startTestActivity(i);
}
Overview overview = mLauncher.pressHome().switchToOverview();
executeOnLauncher(
launcher -> assertTrue("Don't have at least 13 tasks",
getTaskCount(launcher) >= 13));
// Test scroll the first task off screen
overview.scrollCurrentTaskOffScreen();
assertTrue("Launcher internal state is not Overview",
isInState(() -> LauncherState.OVERVIEW));
executeOnLauncher(launcher -> assertTrue("Current task in Overview is still 0",
getCurrentOverviewPage(launcher) > 0));
// Test opening the task.
overview.getCurrentTask().open();
assertTrue("Test activity didn't open from Overview",
mDevice.wait(Until.hasObject(By.pkg(getAppPackageName()).text("TestActivity8")),
DEFAULT_UI_TIMEOUT));
// Scroll the task offscreen as it is now first
overview = mLauncher.pressHome().switchToOverview();
overview.scrollCurrentTaskOffScreen();
assertTrue("Launcher internal state is not Overview",
isInState(() -> LauncherState.OVERVIEW));
executeOnLauncher(launcher -> assertTrue("Current task in Overview is still 0",
getCurrentOverviewPage(launcher) > 0));
// Test dismissing the later task.
final Integer numTasks = getFromLauncher(this::getTaskCount);
overview.getCurrentTask().dismiss();
executeOnLauncher(
launcher -> assertEquals("Dismissing a task didn't remove 1 task from Overview",
numTasks - 1, getTaskCount(launcher)));
executeOnLauncher(launcher -> assertTrue("Grid did not rebalance after dismissal",
(Math.abs(getTopRowTaskCountForTablet(launcher) - getBottomRowTaskCountForTablet(
launcher)) <= 1)));
// Test dismissing more tasks.
assertTrue("Launcher internal state didn't remain in Overview",
isInState(() -> LauncherState.OVERVIEW));
overview.getCurrentTask().dismiss();
assertTrue("Launcher internal state didn't remain in Overview",
isInState(() -> LauncherState.OVERVIEW));
overview.getCurrentTask().dismiss();
executeOnLauncher(launcher -> assertTrue("Grid did not rebalance after multiple dismissals",
(Math.abs(getTopRowTaskCountForTablet(launcher) - getBottomRowTaskCountForTablet(
launcher)) <= 1)));
// Test dismissing all tasks.
mLauncher.pressHome().switchToOverview().dismissAllTasks();
assertTrue("Launcher internal state is not Home",
isInState(() -> LauncherState.NORMAL));
executeOnLauncher(
launcher -> assertEquals("Still have tasks after dismissing all",
0, getTaskCount(launcher)));
}
}
@@ -19,9 +19,9 @@ import static androidx.test.InstrumentationRegistry.getContext;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static androidx.test.InstrumentationRegistry.getTargetContext;
import static com.android.launcher3.common.WidgetUtils.createWidgetInfo;
import static com.android.launcher3.testcomponent.TestCommandReceiver.EXTRA_VALUE;
import static com.android.launcher3.testcomponent.TestCommandReceiver.SET_LIST_VIEW_SERVICE_BINDER;
import static com.android.launcher3.util.WidgetUtils.createWidgetInfo;
import static com.android.quickstep.NavigationModeSwitchRule.Mode.ZERO_BUTTON;
import static org.junit.Assert.assertEquals;
@@ -0,0 +1,90 @@
/*
* 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.quickstep.util;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_90;
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import android.content.Context;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.quickstep.FallbackActivityInterface;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Tests for {@link RecentsOrientedState}
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
public class RecentsOrientedStateTest {
private RecentsOrientedState mR1, mR2;
@Before
public void setup() {
Context context = getApplicationContext();
mR1 = new RecentsOrientedState(context, FallbackActivityInterface.INSTANCE, i -> { });
mR2 = new RecentsOrientedState(context, FallbackActivityInterface.INSTANCE, i -> { });
assertEquals(mR1.getStateId(), mR2.getStateId());
}
@Test
public void stateId_changesWithFlags() {
mR1.setGestureActive(true);
mR2.setGestureActive(false);
assertNotEquals(mR1.getStateId(), mR2.getStateId());
mR2.setGestureActive(true);
assertEquals(mR1.getStateId(), mR2.getStateId());
}
@Test
public void stateId_changesWithRecentsRotation() {
mR1.setRecentsRotation(ROTATION_90);
mR2.setRecentsRotation(ROTATION_180);
assertNotEquals(mR1.getStateId(), mR2.getStateId());
mR2.setRecentsRotation(ROTATION_90);
assertEquals(mR1.getStateId(), mR2.getStateId());
}
@Test
public void stateId_changesWithDisplayRotation() {
mR1.update(ROTATION_0, ROTATION_90);
mR2.update(ROTATION_0, ROTATION_180);
assertNotEquals(mR1.getStateId(), mR2.getStateId());
mR2.update(ROTATION_90, ROTATION_90);
assertNotEquals(mR1.getStateId(), mR2.getStateId());
mR2.update(ROTATION_90, ROTATION_0);
assertNotEquals(mR1.getStateId(), mR2.getStateId());
mR2.update(ROTATION_0, ROTATION_90);
assertEquals(mR1.getStateId(), mR2.getStateId());
}
}
@@ -0,0 +1,246 @@
/*
* 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.quickstep.util;
import static android.view.Display.DEFAULT_DISPLAY;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.util.ReflectionHelpers;
import com.android.quickstep.FallbackActivityInterface;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TaskViewSimulatorTest {
@Test
public void taskProperlyScaled_portrait_noRotation_sameInsets1() {
new TaskMatrixVerifier()
.withLauncherSize(1200, 2450)
.withInsets(new Rect(0, 80, 0, 120))
.verifyNoTransforms();
}
@Test
public void taskProperlyScaled_portrait_noRotation_sameInsets2() {
new TaskMatrixVerifier()
.withLauncherSize(1200, 2450)
.withInsets(new Rect(55, 80, 55, 120))
.verifyNoTransforms();
}
@Test
public void taskProperlyScaled_landscape_noRotation_sameInsets1() {
new TaskMatrixVerifier()
.withLauncherSize(2450, 1250)
.withInsets(new Rect(0, 80, 0, 40))
.verifyNoTransforms();
}
@Test
public void taskProperlyScaled_landscape_noRotation_sameInsets2() {
new TaskMatrixVerifier()
.withLauncherSize(2450, 1250)
.withInsets(new Rect(0, 80, 120, 0))
.verifyNoTransforms();
}
@Test
public void taskProperlyScaled_landscape_noRotation_sameInsets3() {
new TaskMatrixVerifier()
.withLauncherSize(2450, 1250)
.withInsets(new Rect(55, 80, 55, 120))
.verifyNoTransforms();
}
@Test
public void taskProperlyScaled_landscape_rotated() {
new TaskMatrixVerifier()
.withLauncherSize(1200, 2450)
.withInsets(new Rect(0, 80, 0, 120))
.withAppBounds(
new Rect(0, 0, 2450, 1200),
new Rect(0, 80, 0, 120),
Surface.ROTATION_90)
.verifyNoTransforms();
}
private static class TaskMatrixVerifier extends TransformParams {
private Point mDisplaySize = new Point();
private Rect mDisplayInsets = new Rect();
private Rect mAppBounds = new Rect();
private Rect mLauncherInsets = new Rect();
private Rect mAppInsets;
private int mAppRotation = -1;
private DeviceProfile mDeviceProfile;
TaskMatrixVerifier withLauncherSize(int width, int height) {
mDisplaySize.set(width, height);
if (mAppBounds.isEmpty()) {
mAppBounds.set(0, 0, width, height);
}
return this;
}
TaskMatrixVerifier withInsets(Rect insets) {
mDisplayInsets.set(insets);
mLauncherInsets.set(insets);
return this;
}
TaskMatrixVerifier withAppBounds(Rect bounds, Rect insets, int appRotation) {
mAppBounds.set(bounds);
mAppInsets = insets;
mAppRotation = appRotation;
return this;
}
void verifyNoTransforms() {
LauncherModelHelper helper = new LauncherModelHelper();
try {
helper.sandboxContext.allow(SystemUiProxy.INSTANCE);
helper.sandboxContext.allow(SysUINavigationMode.INSTANCE);
Display display = mock(Display.class);
doReturn(DEFAULT_DISPLAY).when(display).getDisplayId();
doReturn(mDisplaySize.x > mDisplaySize.y ? Surface.ROTATION_90 : Surface.ROTATION_0)
.when(display).getRotation();
doAnswer(i -> {
((Point) i.getArgument(0)).set(mDisplaySize.x, mDisplaySize.y);
return null;
}).when(display).getRealSize(any());
doAnswer(i -> {
Point smallestSize = i.getArgument(0);
Point largestSize = i.getArgument(1);
smallestSize.x = smallestSize.y = Math.min(mDisplaySize.x, mDisplaySize.y);
largestSize.x = largestSize.y = Math.max(mDisplaySize.x, mDisplaySize.y);
smallestSize.x -= mDisplayInsets.left + mDisplayInsets.right;
largestSize.x -= mDisplayInsets.left + mDisplayInsets.right;
smallestSize.y -= mDisplayInsets.top + mDisplayInsets.bottom;
largestSize.y -= mDisplayInsets.top + mDisplayInsets.bottom;
return null;
}).when(display).getCurrentSizeRange(any(), any());
DisplayController.Info mockInfo = new Info(helper.sandboxContext, display);
DisplayController controller =
DisplayController.INSTANCE.get(helper.sandboxContext);
controller.close();
ReflectionHelpers.setField(controller, "mInfo", mockInfo);
mDeviceProfile = InvariantDeviceProfile.INSTANCE.get(helper.sandboxContext)
.getBestMatch(mAppBounds.width(), mAppBounds.height());
mDeviceProfile.updateInsets(mLauncherInsets);
TaskViewSimulator tvs = new TaskViewSimulator(helper.sandboxContext,
FallbackActivityInterface.INSTANCE);
tvs.setDp(mDeviceProfile);
int launcherRotation = mockInfo.rotation;
if (mAppRotation < 0) {
mAppRotation = launcherRotation;
}
tvs.getOrientationState().update(launcherRotation, mAppRotation);
if (mAppInsets == null) {
mAppInsets = new Rect(mLauncherInsets);
}
tvs.setPreviewBounds(mAppBounds, mAppInsets);
tvs.fullScreenProgress.value = 1;
tvs.recentsViewScale.value = tvs.getFullScreenScale();
tvs.apply(this);
} finally {
helper.destroy();
}
}
@Override
public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
proxy.onBuildTargetParams(builder, mock(RemoteAnimationTargetCompat.class), this);
return new SurfaceParams[] {builder.build()};
}
@Override
public void applySurfaceParams(SurfaceParams[] params) {
// Verify that the task position remains the same
RectF newAppBounds = new RectF(mAppBounds);
params[0].matrix.mapRect(newAppBounds);
Assert.assertThat(newAppBounds, new AlmostSame(mAppBounds));
System.err.println("Bounds mapped: " + mAppBounds + " => " + newAppBounds);
}
}
private static class AlmostSame extends TypeSafeMatcher<RectF> {
// Allow .1% error margin to account for float to int conversions
private final float mErrorFactor = .001f;
private final Rect mExpected;
AlmostSame(Rect expected) {
mExpected = expected;
}
@Override
protected boolean matchesSafely(RectF item) {
float errorWidth = mErrorFactor * mExpected.width();
float errorHeight = mErrorFactor * mExpected.height();
return Math.abs(item.left - mExpected.left) < errorWidth
&& Math.abs(item.top - mExpected.top) < errorHeight
&& Math.abs(item.right - mExpected.right) < errorWidth
&& Math.abs(item.bottom - mExpected.bottom) < errorHeight;
}
@Override
public void describeTo(Description description) {
description.appendValue(mExpected);
}
}
}