Merge remote-tracking branch 'aosp/android12L-release' into 12.1-dev
This commit is contained in:
+226
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+159
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user