Merge "Changing min sdk to 25" into ub-launcher3-master
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
index 50af4a1..fb1828b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import android.animation.Animator;
@@ -281,6 +282,9 @@
}
});
}
+ if (QUICKSTEP_SPRINGS.get()) {
+ mCurrentAnimation.dispatchOnStartWithVelocity(goingToEnd ? 1f : 0f, velocity);
+ }
anim.start();
}
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index d6a7f21..fbb3618 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -23,7 +23,8 @@
import static com.android.launcher3.LauncherState.FAST_OVERVIEW;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS_SPRING;
+import static com.android.launcher3.allapps.AllAppsTransitionController.SPRING_DAMPING_RATIO;
+import static com.android.launcher3.allapps.AllAppsTransitionController.SPRING_STIFFNESS;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
@@ -394,9 +395,9 @@
}
private Animator createShelfAnim(Launcher activity, float ... progressValues) {
- Animator shiftAnim = new SpringObjectAnimator(activity.getAllAppsController(),
- ALL_APPS_PROGRESS_SPRING, "allAppsSpringFromACH",
- activity.getAllAppsController().getShiftRange(), progressValues);
+ Animator shiftAnim = new SpringObjectAnimator<>(activity.getAllAppsController(),
+ "allAppsSpringFromACH", activity.getAllAppsController().getShiftRange(),
+ SPRING_DAMPING_RATIO, SPRING_STIFFNESS, progressValues);
shiftAnim.setInterpolator(LINEAR);
return shiftAnim;
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 923da7b..c6f293d 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.uioverrides.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
import static com.android.quickstep.util.ClipAnimationHelper.TransformParams;
@@ -66,16 +67,19 @@
import android.widget.ListView;
import androidx.annotation.Nullable;
+import androidx.dynamicanimation.animation.SpringForce;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherAnimUtils.ViewProgressProperty;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PropertyListBuilder;
+import com.android.launcher3.anim.SpringObjectAnimator;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -113,6 +117,10 @@
private static final String TAG = RecentsView.class.getSimpleName();
+ public static final float SPRING_MIN_VISIBLE_CHANGE = 0.001f;
+ public static final float SPRING_DAMPING_RATIO = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
+ public static final float SPRING_STIFFNESS = SpringForce.STIFFNESS_LOW;
+
public static final FloatProperty<RecentsView> CONTENT_ALPHA =
new FloatProperty<RecentsView>("contentAlpha") {
@Override
@@ -941,8 +949,15 @@
private void addDismissedTaskAnimations(View taskView, AnimatorSet anim, long duration) {
addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim);
- addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()),
- duration, LINEAR, anim);
+ if (QUICKSTEP_SPRINGS.get() && taskView instanceof TaskView)
+ addAnim(new SpringObjectAnimator<>(new ViewProgressProperty(taskView,
+ View.TRANSLATION_Y), "taskViewTransY", SPRING_MIN_VISIBLE_CHANGE,
+ SPRING_DAMPING_RATIO, SPRING_STIFFNESS, 0, -taskView.getHeight()),
+ duration, LINEAR, anim);
+ else {
+ addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()),
+ duration, LINEAR, anim);
+ }
}
private void removeTask(Task task, int index, PendingAnimation.OnEndListener onEndListener,
@@ -1012,8 +1027,16 @@
}
int scrollDiff = newScroll[i] - oldScroll[i] + offset;
if (scrollDiff != 0) {
- addAnim(ObjectAnimator.ofFloat(child, TRANSLATION_X, scrollDiff),
- duration, ACCEL, anim);
+ if (QUICKSTEP_SPRINGS.get() && child instanceof TaskView) {
+ addAnim(new SpringObjectAnimator<>(
+ new ViewProgressProperty(child, View.TRANSLATION_X),
+ "taskViewTransX", SPRING_MIN_VISIBLE_CHANGE, SPRING_DAMPING_RATIO,
+ SPRING_STIFFNESS, 0, scrollDiff), duration, ACCEL, anim);
+ } else {
+ addAnim(ObjectAnimator.ofFloat(child, TRANSLATION_X, scrollDiff), duration,
+ ACCEL, anim);
+ }
+
needsCurveUpdates = true;
}
}
@@ -1094,7 +1117,7 @@
return pendingAnimation;
}
- private static void addAnim(ObjectAnimator anim, long duration,
+ private static void addAnim(Animator anim, long duration,
TimeInterpolator interpolator, AnimatorSet set) {
anim.setDuration(duration).setInterpolator(interpolator);
set.play(anim);
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTests.java b/quickstep/tests/src/com/android/quickstep/TaplTests.java
deleted file mode 100644
index 347b7ac..0000000
--- a/quickstep/tests/src/com/android/quickstep/TaplTests.java
+++ /dev/null
@@ -1,462 +0,0 @@
-/*
- * Copyright (C) 2018 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 androidx.test.InstrumentationRegistry.getInstrumentation;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
-import android.content.Intent;
-import android.os.RemoteException;
-import android.util.Log;
-
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-import androidx.test.uiautomator.By;
-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.AllAppsFromOverview;
-import com.android.launcher3.tapl.AppIcon;
-import com.android.launcher3.tapl.Background;
-import com.android.launcher3.tapl.Overview;
-import com.android.launcher3.tapl.OverviewTask;
-import com.android.launcher3.tapl.TestHelpers;
-import com.android.launcher3.tapl.Widgets;
-import com.android.launcher3.tapl.Workspace;
-import com.android.launcher3.views.OptionsPopupView;
-import com.android.launcher3.widget.WidgetsFullSheet;
-import com.android.launcher3.widget.WidgetsRecyclerView;
-import com.android.quickstep.QuickStepOnOffRule.QuickstepOnOff;
-import com.android.quickstep.views.RecentsView;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestWatcher;
-import org.junit.runner.Description;
-import org.junit.runner.RunWith;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class TaplTests extends AbstractQuickStepTest {
- private static final String TAG = "TaplTests";
-
- private static int sScreenshotCount = 0;
-
- @Rule
- public TestWatcher mFailureWatcher = new TestWatcher() {
- private void dumpViewHierarchy() {
- final ByteArrayOutputStream stream = new ByteArrayOutputStream();
- try {
- mDevice.dumpWindowHierarchy(stream);
- stream.flush();
- stream.close();
- for (String line : stream.toString().split("\\r?\\n")) {
- Log.e(TaplTests.TAG, line.trim());
- }
- } catch (IOException e) {
- Log.e(TaplTests.TAG, "error dumping XML to logcat", e);
- }
- }
-
- @Override
- protected void failed(Throwable e, Description description) {
- if (mDevice == null) return;
- final String pathname = getInstrumentation().getTargetContext().
- getFilesDir().getPath() + "/TaplTestScreenshot" + sScreenshotCount++ + ".png";
- Log.e(TaplTests.TAG, "Failed test " + description.getMethodName() +
- ", screenshot will be saved to " + pathname +
- ", track trace is below, UI object dump is further below:\n" +
- Log.getStackTraceString(e));
- dumpViewHierarchy();
- mDevice.takeScreenshot(new File(pathname));
- }
- };
-
- @Before
- public void setUp() throws Exception {
- super.setUp();
-
- clearLauncherData();
-
- mLauncher.pressHome();
- waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
- waitForResumed("Launcher internal state is still Background");
- }
-
- private boolean isInState(LauncherState state) {
- if (!TestHelpers.isInLauncherProcess()) return true;
- return getFromLauncher(launcher -> launcher.getStateManager().getState() == state);
- }
-
- // Please don't add negative test cases for methods that fail only after a long wait.
- private void expectFail(String message, Runnable action) {
- boolean failed = false;
- try {
- action.run();
- } catch (AssertionError e) {
- failed = true;
- }
- assertTrue(message, failed);
- }
-
- private boolean isWorkspaceScrollable(Launcher launcher) {
- return launcher.getWorkspace().getPageCount() > 1;
- }
-
- private boolean isInBackground(Launcher launcher) {
- return !launcher.hasBeenResumed();
- }
-
- private void startTestApps() throws Exception {
- startAppFast(resolveSystemApp(Intent.CATEGORY_APP_MESSAGING));
- startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
- startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CONTACTS));
-
- executeOnLauncher(launcher -> assertTrue(
- "Launcher activity is the top activity; expecting another activity to be the top "
- + "one",
- isInBackground(launcher)));
- }
-
- private int getCurrentWorkspacePage(Launcher launcher) {
- return launcher.getWorkspace().getCurrentPage();
- }
-
- private WidgetsRecyclerView getWidgetsView(Launcher launcher) {
- return WidgetsFullSheet.getWidgetsView(launcher);
- }
-
- @Test
- public void testDevicePressMenu() throws Exception {
- mDevice.pressMenu();
- mDevice.waitForIdle();
- executeOnLauncher(
- launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu",
- OptionsPopupView.getOptionsPopup(launcher) != null));
- }
-
- @Test
- @PortraitLandscape
- public void testPressRecentAppsLauncherAndGetOverview() throws RemoteException {
- mDevice.pressRecentApps();
- waitForState("Launcher internal state didn't switch to Overview", LauncherState.OVERVIEW);
-
- assertNotNull("getOverview() returned null", mLauncher.getOverview());
- }
-
- private void runAllAppsTest(AllApps allApps) throws Exception {
- assertNotNull("allApps parameter is null", allApps);
-
- assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
-
- // Test flinging forward and backward.
- executeOnLauncher(launcher -> assertEquals("All Apps started in already scrolled state", 0,
- getAllAppsScroll(launcher)));
-
- allApps.flingForward();
- assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
- final Integer flingForwardY = getFromLauncher(launcher -> getAllAppsScroll(launcher));
- executeOnLauncher(
- launcher -> assertTrue("flingForward() didn't scroll App Apps", flingForwardY > 0));
-
- allApps.flingBackward();
- assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
- final Integer flingBackwardY = getFromLauncher(launcher -> getAllAppsScroll(launcher));
- executeOnLauncher(launcher -> assertTrue("flingBackward() didn't scroll App Apps",
- flingBackwardY < flingForwardY));
-
- // Test scrolling down to YouTube.
- assertNotNull("All apps: can't fine YouTube", allApps.getAppIcon("YouTube"));
- // Test scrolling up to Camera.
- assertNotNull("All apps: can't fine Camera", allApps.getAppIcon("Camera"));
- // Test failing to find a non-existing app.
- final AllApps allAppsFinal = allApps;
- expectFail("All apps: could find a non-existing app",
- () -> allAppsFinal.getAppIcon("NO APP"));
-
- assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
- }
-
- private int getAllAppsScroll(Launcher launcher) {
- return launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
- }
-
- @Test
- @PortraitLandscape
- public void testAllAppsFromHome() throws Exception {
- // Test opening all apps
- assertNotNull("switchToAllApps() returned null",
- mLauncher.getWorkspace().switchToAllApps());
-
- runAllAppsTest(mLauncher.getAllApps());
-
- // Testing pressHome.
- assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
- assertNotNull("pressHome returned null", mLauncher.pressHome());
- assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
- assertNotNull("getHome returned null", mLauncher.getWorkspace());
- }
-
- @Test
- @QuickstepOnOff
- @PortraitLandscape
- public void testWorkspaceSwitchToAllApps() {
- assertNotNull("switchToAllApps() returned null",
- mLauncher.getWorkspace().switchToAllApps());
- assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
- }
-
- @Test
- public void testAllAppsFromOverview() throws Exception {
- // Test opening all apps from Overview.
- assertNotNull("switchToAllApps() returned null",
- mLauncher.getWorkspace().switchToOverview().switchToAllApps());
-
- runAllAppsTest(mLauncher.getAllAppsFromOverview());
- }
-
- @Test
- public void testWorkspace() throws Exception {
- final Workspace workspace = mLauncher.getWorkspace();
-
- // Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there.
- executeOnLauncher(launcher -> assertFalse("Initial workspace state is scrollable",
- isWorkspaceScrollable(launcher)));
- assertNull("Messages app was found on empty workspace",
- workspace.tryGetWorkspaceAppIcon("Messages"));
-
- workspace.ensureWorkspaceIsScrollable();
-
- executeOnLauncher(
- launcher -> assertEquals("Ensuring workspace scrollable didn't switch to page #1",
- 1, getCurrentWorkspacePage(launcher)));
- executeOnLauncher(
- launcher -> assertTrue("ensureScrollable didn't make workspace scrollable",
- isWorkspaceScrollable(launcher)));
- assertNotNull("ensureScrollable didn't add Messages app",
- workspace.tryGetWorkspaceAppIcon("Messages"));
-
- // Test flinging workspace.
- workspace.flingBackward();
- assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
- executeOnLauncher(
- launcher -> assertEquals("Flinging back didn't switch workspace to page #0",
- 0, getCurrentWorkspacePage(launcher)));
-
- workspace.flingForward();
- executeOnLauncher(
- launcher -> assertEquals("Flinging forward didn't switch workspace to page #1",
- 1, getCurrentWorkspacePage(launcher)));
- assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
-
- // Test starting a workspace app.
- final AppIcon app = workspace.tryGetWorkspaceAppIcon("Messages");
- assertNotNull("No Messages app in workspace", app);
- assertNotNull("AppIcon.launch returned null",
- app.launch(resolveSystemApp(Intent.CATEGORY_APP_MESSAGING)));
- executeOnLauncher(launcher -> assertTrue(
- "Launcher activity is the top activity; expecting another activity to be the top "
- + "one",
- isInBackground(launcher)));
- }
-
- @Test
- @PortraitLandscape
- public void testOverview() throws Exception {
- startTestApps();
- Overview overview = mLauncher.pressHome().switchToOverview();
- assertTrue("Launcher internal state didn't switch to Overview",
- isInState(LauncherState.OVERVIEW));
- executeOnLauncher(
- launcher -> assertTrue("Don't have at least 3 tasks", getTaskCount(launcher) >= 3));
-
- // Test flinging forward and backward.
- executeOnLauncher(launcher -> assertEquals("Current task in Overview is not 0",
- 0, getCurrentOverviewPage(launcher)));
-
- overview.flingForward();
- assertTrue("Launcher internal state is not Overview", isInState(LauncherState.OVERVIEW));
- final Integer currentTaskAfterFlingForward = getFromLauncher(
- launcher -> getCurrentOverviewPage(launcher));
- executeOnLauncher(launcher -> assertTrue("Current task in Overview is still 0",
- currentTaskAfterFlingForward > 0));
-
- overview.flingBackward();
- assertTrue("Launcher internal state is not Overview", isInState(LauncherState.OVERVIEW));
- executeOnLauncher(launcher -> assertTrue("Flinging back in Overview did nothing",
- getCurrentOverviewPage(launcher) < currentTaskAfterFlingForward));
-
- // Test opening a task.
- OverviewTask task = mLauncher.pressHome().switchToOverview().getCurrentTask();
- assertNotNull("overview.getCurrentTask() returned null (1)", task);
- assertNotNull("OverviewTask.open returned null", task.open());
- assertTrue("Contacts app didn't open from Overview", mDevice.wait(Until.hasObject(
- By.pkg(resolveSystemApp(Intent.CATEGORY_APP_CONTACTS)).depth(0)),
- LONG_WAIT_TIME_MS));
- executeOnLauncher(launcher -> assertTrue(
- "Launcher activity is the top activity; expecting another activity to be the top "
- + "one",
- isInBackground(launcher)));
-
- // Test dismissing a task.
- overview = mLauncher.pressHome().switchToOverview();
- assertTrue("Launcher internal state didn't switch to Overview",
- isInState(LauncherState.OVERVIEW));
- final Integer numTasks = getFromLauncher(launcher -> getTaskCount(launcher));
- task = overview.getCurrentTask();
- assertNotNull("overview.getCurrentTask() returned null (2)", task);
- task.dismiss();
- executeOnLauncher(
- launcher -> assertEquals("Dismissing a task didn't remove 1 task from Overview",
- numTasks - 1, getTaskCount(launcher)));
-
- if (!TestHelpers.isInLauncherProcess() ||
- getFromLauncher(launcher -> !launcher.getDeviceProfile().isLandscape)) {
- // Test switching to all apps and back.
- final AllAppsFromOverview allApps = overview.switchToAllApps();
- assertNotNull("overview.switchToAllApps() returned null (1)", allApps);
- assertTrue("Launcher internal state is not All Apps (1)",
- isInState(LauncherState.ALL_APPS));
-
- overview = allApps.switchBackToOverview();
- assertNotNull("allApps.switchBackToOverview() returned null", overview);
- assertTrue("Launcher internal state didn't switch to Overview",
- isInState(LauncherState.OVERVIEW));
-
- // Test UIDevice.pressBack()
- overview.switchToAllApps();
- assertNotNull("overview.switchToAllApps() returned null (2)", allApps);
- assertTrue("Launcher internal state is not All Apps (2)",
- isInState(LauncherState.ALL_APPS));
- mDevice.pressBack();
- mLauncher.getOverview();
- }
-
- // Test UIDevice.pressHome, once we are in AllApps.
- mDevice.pressHome();
- waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
- }
-
- private int getCurrentOverviewPage(Launcher launcher) {
- return launcher.<RecentsView>getOverviewPanel().getCurrentPage();
- }
-
- private int getTaskCount(Launcher launcher) {
- return launcher.<RecentsView>getOverviewPanel().getTaskViewCount();
- }
-
- private void runIconLaunchFromAllAppsTest(AllApps allApps) throws Exception {
- final AppIcon app = allApps.getAppIcon("Calculator");
- assertNotNull("AppIcon.launch returned null", app.launch(
- resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)));
- executeOnLauncher(launcher -> assertTrue(
- "Launcher activity is the top activity; expecting another activity to be the top "
- + "one",
- isInBackground(launcher)));
- }
-
- @Test
- @PortraitLandscape
- public void testAppIconLaunchFromAllAppsFromHome() throws Exception {
- final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
- assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
-
- runIconLaunchFromAllAppsTest(allApps);
- }
-
- @Test
- public void testAppIconLaunchFromAllAppsFromOverview() throws Exception {
- final AllApps allApps =
- mLauncher.getWorkspace().switchToOverview().switchToAllApps();
- assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
-
- runIconLaunchFromAllAppsTest(allApps);
- }
-
- @Test
- @PortraitLandscape
- public void testWidgets() throws Exception {
- // Test opening widgets.
- executeOnLauncher(launcher ->
- assertTrue("Widgets is initially opened", getWidgetsView(launcher) == null));
- Widgets widgets = mLauncher.getWorkspace().openAllWidgets();
- assertNotNull("openAllWidgets() returned null", widgets);
- widgets = mLauncher.getAllWidgets();
- assertNotNull("getAllWidgets() returned null", widgets);
- executeOnLauncher(launcher ->
- assertTrue("Widgets is not shown", getWidgetsView(launcher).isShown()));
- executeOnLauncher(launcher -> assertEquals("Widgets is scrolled upon opening",
- 0, getWidgetsScroll(launcher)));
-
- // Test flinging widgets.
- widgets.flingForward();
- Integer flingForwardY = getFromLauncher(launcher -> getWidgetsScroll(launcher));
- executeOnLauncher(launcher -> assertTrue("Flinging forward didn't scroll widgets",
- flingForwardY > 0));
-
- widgets.flingBackward();
- executeOnLauncher(launcher -> assertTrue("Flinging backward didn't scroll widgets",
- getWidgetsScroll(launcher) < flingForwardY));
-
- mLauncher.pressHome();
- waitForLauncherCondition("Widgets were not closed",
- launcher -> getWidgetsView(launcher) == null);
- }
-
- private int getWidgetsScroll(Launcher launcher) {
- return getWidgetsView(launcher).getCurrentScrollY();
- }
-
- @Test
- @QuickstepOnOff
- @PortraitLandscape
- public void testSwitchToOverview() throws Exception {
- assertNotNull("Workspace.switchToOverview() returned null",
- mLauncher.pressHome().switchToOverview());
- assertTrue("Launcher internal state didn't switch to Overview",
- isInState(LauncherState.OVERVIEW));
- }
-
- @Test
- @QuickstepOnOff
- @PortraitLandscape
- public void testBackground() throws Exception {
- startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
- final Background background = mLauncher.getBackground();
- assertNotNull("Launcher.getBackground() returned null", background);
- executeOnLauncher(launcher -> assertTrue(
- "Launcher activity is the top activity; expecting another activity to be the top "
- + "one",
- isInBackground(launcher)));
-
- assertNotNull("Background.switchToOverview() returned null", background.switchToOverview());
- assertTrue("Launcher internal state didn't switch to Overview",
- isInState(LauncherState.OVERVIEW));
- }
-}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
new file mode 100644
index 0000000..8f7cf92
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2018 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.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Intent;
+import android.os.RemoteException;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+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.AllAppsFromOverview;
+import com.android.launcher3.tapl.Background;
+import com.android.launcher3.tapl.Overview;
+import com.android.launcher3.tapl.OverviewTask;
+import com.android.launcher3.tapl.TestHelpers;
+import com.android.launcher3.ui.TaplTestsLauncher3;
+import com.android.quickstep.QuickStepOnOffRule.QuickstepOnOff;
+import com.android.quickstep.views.RecentsView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TaplTestsQuickstep extends AbstractQuickStepTest {
+ @Rule
+ public TestWatcher mFailureWatcher = new TaplTestsLauncher3.FailureWatcher(mDevice);
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ clearLauncherData();
+
+ mLauncher.pressHome();
+ waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
+ waitForResumed("Launcher internal state is still Background");
+ }
+
+ private void startTestApps() throws Exception {
+ startAppFast(resolveSystemApp(Intent.CATEGORY_APP_MESSAGING));
+ startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+ startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CONTACTS));
+
+ executeOnLauncher(launcher -> assertTrue(
+ "Launcher activity is the top activity; expecting another activity to be the top "
+ + "one",
+ isInBackground(launcher)));
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testPressRecentAppsLauncherAndGetOverview() throws RemoteException {
+ mDevice.pressRecentApps();
+ waitForState("Launcher internal state didn't switch to Overview", LauncherState.OVERVIEW);
+
+ assertNotNull("getOverview() returned null", mLauncher.getOverview());
+ }
+
+ @Test
+ @QuickstepOnOff
+ @PortraitLandscape
+ public void testWorkspaceSwitchToAllApps() {
+ assertNotNull("switchToAllApps() returned null",
+ mLauncher.getWorkspace().switchToAllApps());
+ assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
+ }
+
+ @Test
+ public void testAllAppsFromOverview() throws Exception {
+ // Test opening all apps from Overview.
+ assertNotNull("switchToAllApps() returned null",
+ mLauncher.getWorkspace().switchToOverview().switchToAllApps());
+
+ TaplTestsLauncher3.runAllAppsTest(this, mLauncher.getAllAppsFromOverview());
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testOverview() throws Exception {
+ startTestApps();
+ Overview overview = mLauncher.pressHome().switchToOverview();
+ assertTrue("Launcher internal state didn't switch to Overview",
+ isInState(LauncherState.OVERVIEW));
+ executeOnLauncher(
+ launcher -> assertTrue("Don't have at least 3 tasks", getTaskCount(launcher) >= 3));
+
+ // Test flinging forward and backward.
+ executeOnLauncher(launcher -> assertEquals("Current task in Overview is not 0",
+ 0, getCurrentOverviewPage(launcher)));
+
+ overview.flingForward();
+ assertTrue("Launcher internal state is not Overview", isInState(LauncherState.OVERVIEW));
+ final Integer currentTaskAfterFlingForward = getFromLauncher(
+ launcher -> getCurrentOverviewPage(launcher));
+ executeOnLauncher(launcher -> assertTrue("Current task in Overview is still 0",
+ currentTaskAfterFlingForward > 0));
+
+ overview.flingBackward();
+ assertTrue("Launcher internal state is not Overview", isInState(LauncherState.OVERVIEW));
+ executeOnLauncher(launcher -> assertTrue("Flinging back in Overview did nothing",
+ getCurrentOverviewPage(launcher) < currentTaskAfterFlingForward));
+
+ // Test opening a task.
+ OverviewTask task = mLauncher.pressHome().switchToOverview().getCurrentTask();
+ assertNotNull("overview.getCurrentTask() returned null (1)", task);
+ assertNotNull("OverviewTask.open returned null", task.open());
+ assertTrue("Contacts app didn't open from Overview", mDevice.wait(Until.hasObject(
+ By.pkg(resolveSystemApp(Intent.CATEGORY_APP_CONTACTS)).depth(0)),
+ LONG_WAIT_TIME_MS));
+ executeOnLauncher(launcher -> assertTrue(
+ "Launcher activity is the top activity; expecting another activity to be the top "
+ + "one",
+ isInBackground(launcher)));
+
+ // Test dismissing a task.
+ overview = mLauncher.pressHome().switchToOverview();
+ assertTrue("Launcher internal state didn't switch to Overview",
+ isInState(LauncherState.OVERVIEW));
+ final Integer numTasks = getFromLauncher(launcher -> getTaskCount(launcher));
+ task = overview.getCurrentTask();
+ assertNotNull("overview.getCurrentTask() returned null (2)", task);
+ task.dismiss();
+ executeOnLauncher(
+ launcher -> assertEquals("Dismissing a task didn't remove 1 task from Overview",
+ numTasks - 1, getTaskCount(launcher)));
+
+ if (!TestHelpers.isInLauncherProcess() ||
+ getFromLauncher(launcher -> !launcher.getDeviceProfile().isLandscape)) {
+ // Test switching to all apps and back.
+ final AllAppsFromOverview allApps = overview.switchToAllApps();
+ assertNotNull("overview.switchToAllApps() returned null (1)", allApps);
+ assertTrue("Launcher internal state is not All Apps (1)",
+ isInState(LauncherState.ALL_APPS));
+
+ overview = allApps.switchBackToOverview();
+ assertNotNull("allApps.switchBackToOverview() returned null", overview);
+ assertTrue("Launcher internal state didn't switch to Overview",
+ isInState(LauncherState.OVERVIEW));
+
+ // Test UIDevice.pressBack()
+ overview.switchToAllApps();
+ assertNotNull("overview.switchToAllApps() returned null (2)", allApps);
+ assertTrue("Launcher internal state is not All Apps (2)",
+ isInState(LauncherState.ALL_APPS));
+ mDevice.pressBack();
+ mLauncher.getOverview();
+ }
+
+ // Test UIDevice.pressHome, once we are in AllApps.
+ mDevice.pressHome();
+ waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
+ }
+
+ private int getCurrentOverviewPage(Launcher launcher) {
+ return launcher.<RecentsView>getOverviewPanel().getCurrentPage();
+ }
+
+ private int getTaskCount(Launcher launcher) {
+ return launcher.<RecentsView>getOverviewPanel().getTaskViewCount();
+ }
+
+ @Test
+ public void testAppIconLaunchFromAllAppsFromOverview() throws Exception {
+ final AllApps allApps =
+ mLauncher.getWorkspace().switchToOverview().switchToAllApps();
+ assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
+
+ TaplTestsLauncher3.runIconLaunchFromAllAppsTest(this, allApps);
+ }
+
+ @Test
+ @QuickstepOnOff
+ @PortraitLandscape
+ public void testSwitchToOverview() throws Exception {
+ assertNotNull("Workspace.switchToOverview() returned null",
+ mLauncher.pressHome().switchToOverview());
+ assertTrue("Launcher internal state didn't switch to Overview",
+ isInState(LauncherState.OVERVIEW));
+ }
+
+ @Test
+ @QuickstepOnOff
+ @PortraitLandscape
+ public void testBackground() throws Exception {
+ startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
+ final Background background = mLauncher.getBackground();
+ assertNotNull("Launcher.getBackground() returned null", background);
+ executeOnLauncher(launcher -> assertTrue(
+ "Launcher activity is the top activity; expecting another activity to be the top "
+ + "one",
+ isInBackground(launcher)));
+
+ assertNotNull("Background.switchToOverview() returned null", background.switchToOverview());
+ assertTrue("Launcher internal state didn't switch to Overview",
+ isInState(LauncherState.OVERVIEW));
+ }
+}
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index aad3449..04f2b52 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -91,4 +91,24 @@
lp.height = height;
}
};
+
+ public static class ViewProgressProperty implements ProgressInterface {
+ View mView;
+ Property<View, Float> mProperty;
+
+ public ViewProgressProperty(View view, Property<View, Float> property) {
+ mView = view;
+ mProperty = property;
+ }
+
+ @Override
+ public void setProgress(float progress) {
+ mProperty.set(mView, progress);
+ }
+
+ @Override
+ public float getProgress() {
+ return mProperty.get(mView);
+ }
+ }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index e8e93fe..bcb5eec 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -47,6 +47,9 @@
public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener,
ProgressInterface {
+ public static final float SPRING_DAMPING_RATIO = 0.9f;
+ public static final float SPRING_STIFFNESS = 600f;
+
public static final Property<AllAppsTransitionController, Float> ALL_APPS_PROGRESS =
new Property<AllAppsTransitionController, Float>(Float.class, "allAppsProgress") {
@@ -61,19 +64,6 @@
}
};
- public static final FloatPropertyCompat<AllAppsTransitionController> ALL_APPS_PROGRESS_SPRING
- = new FloatPropertyCompat<AllAppsTransitionController>("allAppsProgressSpring") {
- @Override
- public float getValue(AllAppsTransitionController controller) {
- return controller.mProgress;
- }
-
- @Override
- public void setValue(AllAppsTransitionController controller, float progress) {
- controller.setProgress(progress);
- }
- };
-
private AllAppsContainerView mAppsView;
private ScrimView mScrimView;
@@ -191,8 +181,8 @@
Interpolator interpolator = config.userControlled ? LINEAR : toState == OVERVIEW
? builder.getInterpolator(ANIM_OVERVIEW_SCALE, FAST_OUT_SLOW_IN)
: FAST_OUT_SLOW_IN;
- Animator anim = new SpringObjectAnimator<>(this, ALL_APPS_PROGRESS_SPRING,
- "allAppsSpringFromAATC", 1f / mShiftRange, mProgress, targetProgress);
+ Animator anim = new SpringObjectAnimator<>(this, "allAppsSpringFromAATC", 1f / mShiftRange,
+ SPRING_DAMPING_RATIO, SPRING_STIFFNESS, mProgress, targetProgress);
anim.setDuration(config.duration);
anim.setInterpolator(builder.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator));
anim.addListener(getProgressAnimatorListener());
diff --git a/src/com/android/launcher3/anim/SpringObjectAnimator.java b/src/com/android/launcher3/anim/SpringObjectAnimator.java
index e4aec10..4f45c05 100644
--- a/src/com/android/launcher3/anim/SpringObjectAnimator.java
+++ b/src/com/android/launcher3/anim/SpringObjectAnimator.java
@@ -55,17 +55,27 @@
private boolean mAnimatorEnded = false;
private boolean mEnded = false;
- private static final float SPRING_DAMPING_RATIO = 0.9f;
- private static final float SPRING_STIFFNESS = 600f;
+ private static final FloatPropertyCompat<ProgressInterface> sFloatProperty =
+ new FloatPropertyCompat<ProgressInterface>("springObjectAnimator") {
+ @Override
+ public float getValue(ProgressInterface object) {
+ return object.getProgress();
+ }
- public SpringObjectAnimator(T object, FloatPropertyCompat<T> floatProperty,
- String name, float minimumVisibleChange, float... values) {
+ @Override
+ public void setValue(ProgressInterface object, float progress) {
+ object.setProgress(progress);
+ }
+ };
+
+ public SpringObjectAnimator(T object, String name, float minimumVisibleChange, float damping,
+ float stiffness, float... values) {
mObject = object;
- mSpring = new SpringAnimation(object, floatProperty);
+ mSpring = new SpringAnimation(object, sFloatProperty);
mSpring.setMinimumVisibleChange(minimumVisibleChange);
mSpring.setSpring(new SpringForce(0)
- .setDampingRatio(SPRING_DAMPING_RATIO)
- .setStiffness(SPRING_STIFFNESS));
+ .setDampingRatio(damping)
+ .setStiffness(stiffness));
mSpring.setStartVelocity(0.01f);
mProperty = new SpringProperty<T>(name, mSpring);
mObjectAnimator = ObjectAnimator.ofFloat(object, mProperty, values);
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 6ad69d7..fa4ebaf 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -17,13 +17,16 @@
package com.android.launcher3.config;
import static androidx.core.util.Preconditions.checkNotNull;
+
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.provider.Settings;
+
import androidx.annotation.GuardedBy;
import androidx.annotation.Keep;
import androidx.annotation.VisibleForTesting;
+
import com.android.launcher3.Utilities;
import java.util.ArrayList;
@@ -95,8 +98,9 @@
public static final TogglableFlag APPLY_CONFIG_AT_RUNTIME = new TogglableFlag(
"APPLY_CONFIG_AT_RUNTIME", true, "Apply display changes dynamically");
- public static final TogglableFlag ENABLE_TASK_STABILIZER = new TogglableFlag(
- "ENABLE_TASK_STABILIZER", false, "Stable task list across fast task switches");
+ public static final ToggleableGlobalSettingsFlag ENABLE_TASK_STABILIZER
+ = new ToggleableGlobalSettingsFlag("ENABLE_TASK_STABILIZER", false,
+ "Stable task list across fast task switches");
public static final TogglableFlag QUICKSTEP_SPRINGS = new TogglableFlag("QUICKSTEP_SPRINGS",
false, "Enable springs for quickstep animations");
@@ -249,11 +253,17 @@
@Override
void updateStorage(Context context, boolean value) {
+ if (contentResolver == null) {
+ return;
+ }
Settings.Global.putInt(contentResolver, getKey(), value ? 1 : 0);
}
@Override
boolean getFromStorage(Context context, boolean defaultValue) {
+ if (contentResolver == null) {
+ return defaultValue;
+ }
return Settings.Global.getInt(contentResolver, getKey(), defaultValue ? 1 : 0) == 1;
}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 1b34598..f2a8da5 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -358,4 +358,17 @@
waitForLauncherCondition(
"Launcher still active", launcher -> launcher == null, DEFAULT_UI_TIMEOUT);
}
+
+ protected boolean isInBackground(Launcher launcher) {
+ return !launcher.hasBeenResumed();
+ }
+
+ protected boolean isInState(LauncherState state) {
+ if (!TestHelpers.isInLauncherProcess()) return true;
+ return getFromLauncher(launcher -> launcher.getStateManager().getState() == state);
+ }
+
+ protected int getAllAppsScroll(Launcher launcher) {
+ return launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
+ }
}
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
new file mode 100644
index 0000000..701e33b
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -0,0 +1,303 @@
+/*
+ * 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.launcher3.ui;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Intent;
+import android.util.Log;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.UiDevice;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.tapl.AllApps;
+import com.android.launcher3.tapl.AppIcon;
+import com.android.launcher3.tapl.Widgets;
+import com.android.launcher3.tapl.Workspace;
+import com.android.launcher3.views.OptionsPopupView;
+import com.android.launcher3.widget.WidgetsFullSheet;
+import com.android.launcher3.widget.WidgetsRecyclerView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
+ private static final String TAG = "TaplTestsAosp";
+
+ private static int sScreenshotCount = 0;
+
+ public static class FailureWatcher extends TestWatcher {
+ private UiDevice mDevice;
+
+ public FailureWatcher(UiDevice device) {
+ this.mDevice = device;
+ }
+
+ private void dumpViewHierarchy() {
+ final ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ try {
+ mDevice.dumpWindowHierarchy(stream);
+ stream.flush();
+ stream.close();
+ for (String line : stream.toString().split("\\r?\\n")) {
+ Log.e(TAG, line.trim());
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "error dumping XML to logcat", e);
+ }
+ }
+
+ @Override
+ protected void failed(Throwable e, Description description) {
+ if (mDevice == null) return;
+ final String pathname = getInstrumentation().getTargetContext().
+ getFilesDir().getPath() + "/TaplTestScreenshot" + sScreenshotCount++ + ".png";
+ Log.e(TAG, "Failed test " + description.getMethodName() +
+ ", screenshot will be saved to " + pathname +
+ ", track trace is below, UI object dump is further below:\n" +
+ Log.getStackTraceString(e));
+ dumpViewHierarchy();
+ mDevice.takeScreenshot(new File(pathname));
+ }
+ }
+
+ @Rule
+ public TestWatcher mFailureWatcher = new FailureWatcher(mDevice);
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ clearLauncherData();
+
+ mLauncher.pressHome();
+ waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
+ waitForResumed("Launcher internal state is still Background");
+ }
+
+ // Please don't add negative test cases for methods that fail only after a long wait.
+ public static void expectFail(String message, Runnable action) {
+ boolean failed = false;
+ try {
+ action.run();
+ } catch (AssertionError e) {
+ failed = true;
+ }
+ assertTrue(message, failed);
+ }
+
+ private boolean isWorkspaceScrollable(Launcher launcher) {
+ return launcher.getWorkspace().getPageCount() > 1;
+ }
+
+ private int getCurrentWorkspacePage(Launcher launcher) {
+ return launcher.getWorkspace().getCurrentPage();
+ }
+
+ private WidgetsRecyclerView getWidgetsView(Launcher launcher) {
+ return WidgetsFullSheet.getWidgetsView(launcher);
+ }
+
+ @Test
+ public void testDevicePressMenu() throws Exception {
+ mDevice.pressMenu();
+ mDevice.waitForIdle();
+ executeOnLauncher(
+ launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu",
+ OptionsPopupView.getOptionsPopup(launcher) != null));
+ }
+
+ public static void runAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
+ assertNotNull("allApps parameter is null", allApps);
+
+ assertTrue(
+ "Launcher internal state is not All Apps", test.isInState(LauncherState.ALL_APPS));
+
+ // Test flinging forward and backward.
+ test.executeOnLauncher(launcher -> assertEquals(
+ "All Apps started in already scrolled state", 0, test.getAllAppsScroll(launcher)));
+
+ allApps.flingForward();
+ assertTrue("Launcher internal state is not All Apps",
+ test.isInState(LauncherState.ALL_APPS));
+ final Integer flingForwardY = test.getFromLauncher(
+ launcher -> test.getAllAppsScroll(launcher));
+ test.executeOnLauncher(
+ launcher -> assertTrue("flingForward() didn't scroll App Apps", flingForwardY > 0));
+
+ allApps.flingBackward();
+ assertTrue(
+ "Launcher internal state is not All Apps", test.isInState(LauncherState.ALL_APPS));
+ final Integer flingBackwardY = test.getFromLauncher(
+ launcher -> test.getAllAppsScroll(launcher));
+ test.executeOnLauncher(launcher -> assertTrue("flingBackward() didn't scroll App Apps",
+ flingBackwardY < flingForwardY));
+
+ // Test scrolling down to YouTube.
+ assertNotNull("All apps: can't fine YouTube", allApps.getAppIcon("YouTube"));
+ // Test scrolling up to Camera.
+ assertNotNull("All apps: can't fine Camera", allApps.getAppIcon("Camera"));
+ // Test failing to find a non-existing app.
+ final AllApps allAppsFinal = allApps;
+ expectFail("All apps: could find a non-existing app",
+ () -> allAppsFinal.getAppIcon("NO APP"));
+
+ assertTrue(
+ "Launcher internal state is not All Apps", test.isInState(LauncherState.ALL_APPS));
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testAllAppsFromHome() throws Exception {
+ // Test opening all apps
+ assertNotNull("switchToAllApps() returned null",
+ mLauncher.getWorkspace().switchToAllApps());
+
+ runAllAppsTest(this, mLauncher.getAllApps());
+
+ // Testing pressHome.
+ assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
+ assertNotNull("pressHome returned null", mLauncher.pressHome());
+ assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
+ assertNotNull("getHome returned null", mLauncher.getWorkspace());
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testWorkspaceSwitchToAllApps() {
+ assertNotNull("switchToAllApps() returned null",
+ mLauncher.getWorkspace().switchToAllApps());
+ assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
+ }
+
+ @Test
+ public void testWorkspace() throws Exception {
+ final Workspace workspace = mLauncher.getWorkspace();
+
+ // Test that ensureWorkspaceIsScrollable adds a page by dragging an icon there.
+ executeOnLauncher(launcher -> assertFalse("Initial workspace state is scrollable",
+ isWorkspaceScrollable(launcher)));
+ assertNull("Messages app was found on empty workspace",
+ workspace.tryGetWorkspaceAppIcon("Messages"));
+
+ workspace.ensureWorkspaceIsScrollable();
+
+ executeOnLauncher(
+ launcher -> assertEquals("Ensuring workspace scrollable didn't switch to page #1",
+ 1, getCurrentWorkspacePage(launcher)));
+ executeOnLauncher(
+ launcher -> assertTrue("ensureScrollable didn't make workspace scrollable",
+ isWorkspaceScrollable(launcher)));
+ assertNotNull("ensureScrollable didn't add Messages app",
+ workspace.tryGetWorkspaceAppIcon("Messages"));
+
+ // Test flinging workspace.
+ workspace.flingBackward();
+ assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
+ executeOnLauncher(
+ launcher -> assertEquals("Flinging back didn't switch workspace to page #0",
+ 0, getCurrentWorkspacePage(launcher)));
+
+ workspace.flingForward();
+ executeOnLauncher(
+ launcher -> assertEquals("Flinging forward didn't switch workspace to page #1",
+ 1, getCurrentWorkspacePage(launcher)));
+ assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL));
+
+ // Test starting a workspace app.
+ final AppIcon app = workspace.tryGetWorkspaceAppIcon("Messages");
+ assertNotNull("No Messages app in workspace", app);
+ assertNotNull("AppIcon.launch returned null",
+ app.launch(resolveSystemApp(Intent.CATEGORY_APP_MESSAGING)));
+ executeOnLauncher(launcher -> assertTrue(
+ "Launcher activity is the top activity; expecting another activity to be the top "
+ + "one",
+ isInBackground(launcher)));
+ }
+
+ public static void runIconLaunchFromAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
+ final AppIcon app = allApps.getAppIcon("Calculator");
+ assertNotNull("AppIcon.launch returned null", app.launch(
+ test.resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)));
+ test.executeOnLauncher(launcher -> assertTrue(
+ "Launcher activity is the top activity; expecting another activity to be the top "
+ + "one",
+ test.isInBackground(launcher)));
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testAppIconLaunchFromAllAppsFromHome() throws Exception {
+ final AllApps allApps = mLauncher.getWorkspace().switchToAllApps();
+ assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS));
+
+ runIconLaunchFromAllAppsTest(this, allApps);
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testWidgets() throws Exception {
+ // Test opening widgets.
+ executeOnLauncher(launcher ->
+ assertTrue("Widgets is initially opened", getWidgetsView(launcher) == null));
+ Widgets widgets = mLauncher.getWorkspace().openAllWidgets();
+ assertNotNull("openAllWidgets() returned null", widgets);
+ widgets = mLauncher.getAllWidgets();
+ assertNotNull("getAllWidgets() returned null", widgets);
+ executeOnLauncher(launcher ->
+ assertTrue("Widgets is not shown", getWidgetsView(launcher).isShown()));
+ executeOnLauncher(launcher -> assertEquals("Widgets is scrolled upon opening",
+ 0, getWidgetsScroll(launcher)));
+
+ // Test flinging widgets.
+ widgets.flingForward();
+ Integer flingForwardY = getFromLauncher(launcher -> getWidgetsScroll(launcher));
+ executeOnLauncher(launcher -> assertTrue("Flinging forward didn't scroll widgets",
+ flingForwardY > 0));
+
+ widgets.flingBackward();
+ executeOnLauncher(launcher -> assertTrue("Flinging backward didn't scroll widgets",
+ getWidgetsScroll(launcher) < flingForwardY));
+
+ mLauncher.pressHome();
+ waitForLauncherCondition("Widgets were not closed",
+ launcher -> getWidgetsView(launcher) == null);
+ }
+
+ private int getWidgetsScroll(Launcher launcher) {
+ return getWidgetsView(launcher).getCurrentScrollY();
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index 3ffd30c..d39a38e 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -17,6 +17,7 @@
package com.android.launcher3.tapl;
import android.graphics.Point;
+import android.view.MotionEvent;
import android.widget.TextView;
import androidx.test.uiautomator.By;
@@ -40,9 +41,10 @@
*/
public AppIconMenu openMenu() {
final Point iconCenter = mObject.getVisibleCenter();
- mLauncher.longTap(iconCenter.x, iconCenter.y);
+ mLauncher.sendPointer(MotionEvent.ACTION_DOWN, iconCenter);
final UiObject2 deepShortcutsContainer = mLauncher.waitForLauncherObject(
"deep_shortcuts_container");
+ mLauncher.sendPointer(MotionEvent.ACTION_UP, iconCenter);
return new AppIconMenu(mLauncher, deepShortcutsContainer);
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 49bd73a..444f3bd 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -21,10 +21,13 @@
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.app.UiAutomation;
+import android.graphics.Point;
import android.os.Bundle;
import android.os.Parcelable;
+import android.os.SystemClock;
import android.provider.Settings;
import android.util.Log;
+import android.view.MotionEvent;
import android.view.Surface;
import android.view.accessibility.AccessibilityEvent;
@@ -403,11 +406,6 @@
return mDevice;
}
- void longTap(int x, int y) {
- mDevice.drag(x, y, x, y, 0);
- }
-
-
void swipe(int startX, int startY, int endX, int endY) {
executeAndWaitForEvent(
() -> mDevice.swipe(startX, startY, endX, endY, 60),
@@ -419,4 +417,11 @@
void waitForIdle() {
mDevice.waitForIdle();
}
+
+ void sendPointer(int action, Point point) {
+ final MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(),
+ SystemClock.uptimeMillis(), action, point.x, point.y, 0);
+ mInstrumentation.sendPointerSync(event);
+ event.recycle();
+ }
}
\ No newline at end of file