diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java index 5eac01ef62..c8455b8a2b 100644 --- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java +++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java @@ -38,12 +38,14 @@ import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.launcher3.popup.PopupDataProvider; +import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.util.ShortcutUtil; import com.android.launcher3.util.TouchController; import com.android.launcher3.views.BaseDragLayer; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; /** * DragLayer for Secondary launcher @@ -194,15 +196,21 @@ public class SecondaryDragLayer extends BaseDragLayer return false; } + List systemShortcuts = new ArrayList<>(); + + // Hide redundant pin shortcut for app drawer icons if drag-n-drop is enabled. + if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) { + systemShortcuts.add(mPinnedAppsAdapter.getSystemShortcut(item, v)); + } + systemShortcuts.add(APP_INFO.getShortcut(mActivity, item, v)); + final PopupContainerWithArrow container = (PopupContainerWithArrow) mActivity.getLayoutInflater().inflate( R.layout.popup_container, mActivity.getDragLayer(), false); container.populateAndShow((BubbleTextView) v, popupDataProvider.getShortcutCountForItem(item), - Collections.emptyList(), - Arrays.asList(mPinnedAppsAdapter.getSystemShortcut(item, v), - APP_INFO.getShortcut(mActivity, item, v))); + Collections.emptyList(), systemShortcuts); container.requestFocus(); if (!FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.get() || !mActivity.isAppDrawerShown()) { diff --git a/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java b/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java index 93fa705880..082e243694 100644 --- a/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java +++ b/tests/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncherTest.java @@ -15,39 +15,273 @@ */ package com.android.launcher3.secondarydisplay; -import static androidx.test.core.app.ActivityScenario.launch; +import static android.content.Context.MODE_PRIVATE; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static android.view.MotionEvent.ACTION_DOWN; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Point; +import android.os.SystemClock; +import android.view.MotionEvent; +import android.widget.TextView; -import androidx.test.core.app.ActivityScenario; -import androidx.test.espresso.intent.Intents; import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.MediumTest; +import androidx.test.filters.LargeTest; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiObject2; +import androidx.test.uiautomator.Until; + +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.tapl.LauncherInstrumentation; +import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.util.LauncherModelHelper; import org.junit.After; -import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; /** - * Tests for {@link SecondaryDisplayLauncher} + * Tests for {@link SecondaryDisplayLauncher}. + * TODO (b/242776943): Remove anti-patterns & migrate prediction row tests to Quickstep directory */ -@MediumTest +@LargeTest @RunWith(AndroidJUnit4.class) -public class SecondaryDisplayLauncherTest { +public final class SecondaryDisplayLauncherTest extends AbstractLauncherUiTest { + private static final int WAIT_TIME_MS = 5000; + private static final int LONG_PRESS_DURATION_MS = 1000; + private static final int DRAG_TIME_MS = 160; - @Before - public void setUp() { - Intents.init(); + private static final String PINNED_APPS_KEY = "pinned_apps"; + + // Variables required to coordinate drag steps. + private Point mStartPoint; + private Point mEndPoint; + private long mDownTime; + + @Override + public void setUp() throws Exception { + super.setUp(); + setDragNDropFlag(true); } @After public void tearDown() { - Intents.release(); + mTargetContext.getSharedPreferences(PINNED_APPS_KEY, MODE_PRIVATE) + .edit().clear().commit(); } @Test - public void testAllAppsListOpens() { - ActivityScenario launcher = - launch(SecondaryDisplayLauncher.class); - launcher.onActivity(l -> l.showAppDrawer(true)); + @Ignore + public void initializeSecondaryDisplayLauncher_allAppsButtonVisible() { + assertThat(findObjectByResourceName("all_apps_button")).isNotNull(); + } + + @Test + @Ignore + public void allAppsButtonTap_opensAppDrawer() { + openAppDrawer(); + assertThat(findObjectByResourceName("search_container_all_apps")).isNotNull(); + } + + @Test + @Ignore("Launcher3 without quickstep doesn't have a predictions row.") + public void appDrawerOpened_predictionRowAppDividerVisible() { + openAppDrawer(); + assertThat(findObjectByResourceName("apps_divider_view")).isNotNull(); + } + + @Test + @Ignore + public void dragNDropDisabled_pinIconAddsToWorkspace() { + setDragNDropFlag(false); + openAppDrawer(); + UiObject2 app = findDescendantByResourceName( + findObjectByResourceName("apps_list_view"), "icon"); + app.click(LONG_PRESS_DURATION_MS); + UiObject2 popupContainer = findObjectByResourceName("popup_container"); + assertThat(popupContainer).isNotNull(); + UiObject2 pinIcon = findDescendantByTextOrDesc(popupContainer, "Add to home screen"); + assertThat(pinIcon).isNotNull(); + pinIcon.click(); + String appName = app.getContentDescription(); + assertThat(findAppInWorkspace(appName)).isNotNull(); + } + + @Test + @Ignore + public void pressBackFromAllApps_popupMenuOpen_returnsToWorkspace() { + openAppDrawer(); + assertThat(findObjectByResourceName("search_container_all_apps")).isNotNull(); + + findDescendantByResourceName(findObjectByResourceName("apps_list_view"), "icon") + .click(LONG_PRESS_DURATION_MS); + assertThat(findObjectByResourceName("popup_container")).isNotNull(); + + // First back press should close only popup menu. + mDevice.pressBack(); + assertThat(findObjectByResourceName("search_container_all_apps")).isNotNull(); + assertThat(findObjectByResourceName("popup_container")).isNull(); + + // Second back press should close app drawer. + mDevice.pressBack(); + assertThat(findObjectByResourceName("popup_container")).isNull(); + assertThat(findObjectByResourceName("search_container_all_apps")).isNull(); + } + + @Test + @Ignore("Launcher3 without quickstep doesn't have a predictions row.") + public void dragNDropFromPredictionsRow_pinToGrid() { + openAppDrawer(); + assertThat(findObjectByResourceName("prediction_row")).isNotNull(); + String appName = startDragFromPredictionRow(); + moveAppToCenterOfScreen(); + dropApp(); + + // Ensure app was added. + assertThat(findAppInWorkspace(appName)).isNotNull(); + } + + @Test + @Ignore + public void dragNDropFromAppDrawer_pinToGrid() { + openAppDrawer(); + String draggedAppName = startDragFromAllApps(); + moveAppToCenterOfScreen(); + dropApp(); + + // Ensure app was added. + assertThat(findAppInWorkspace(draggedAppName)).isNotNull(); + } + + @Test + @Ignore + public void tapRemoveButton_unpinApp() { + openAppDrawer(); + String draggedAppName = startDragFromAllApps(); + moveAppToCenterOfScreen(); + dropApp(); + removeAppByName(draggedAppName); + assertThat(findAppInWorkspace(draggedAppName)).isNull(); + } + + private void openAppDrawer() { + UiObject2 allAppsButton = findObjectByResourceName("all_apps_button"); + assertThat(allAppsButton).isNotNull(); + allAppsButton.click(); + } + + private String startDragFromAllApps() { + // Find app from app drawer. + UiObject2 allApps = findObjectByResourceName("apps_list_view"); + assertThat(allApps).isNotNull(); + UiObject2 icon = findDescendantByResourceName(allApps, "icon"); + assertThat(icon).isNotNull(); + String appName = icon.getContentDescription(); + + // Start drag action. + mDownTime = SystemClock.uptimeMillis(); + mStartPoint = icon.getVisibleCenter(); + mEndPoint = new Point(mStartPoint.x, mStartPoint.y); + mLauncher.sendPointer(mDownTime, mDownTime, ACTION_DOWN, mStartPoint, + LauncherInstrumentation.GestureScope.INSIDE); + assertThat(findObjectByResourceName("popup_container")).isNotNull(); + return appName; + } + + private String startDragFromPredictionRow() { + // Find app from predictions. + UiObject2 predictionRow = findObjectByResourceName("prediction_row"); + assertThat(predictionRow).isNotNull(); + + UiObject2 icon = findDescendantByResourceName(predictionRow, "icon"); + assertThat(icon).isNotNull(); + + String appName = icon.getContentDescription(); + UiObject2 app = findDescendantByAppName(predictionRow, appName); + assertThat(app).isNotNull(); + + // Start drag action. + mDownTime = SystemClock.uptimeMillis(); + mStartPoint = icon.getVisibleCenter(); + mEndPoint = new Point(mStartPoint.x, mStartPoint.y); + mLauncher.sendPointer(mDownTime, mDownTime, ACTION_DOWN, mStartPoint, + LauncherInstrumentation.GestureScope.INSIDE); + assertThat(findObjectByResourceName("popup_container")).isNotNull(); + return appName; + } + + private void moveAppToCenterOfScreen() { + mEndPoint.set(mDevice.getDisplayWidth() / 2, mDevice.getDisplayHeight() / 2); + mLauncher.movePointer(mDownTime, SystemClock.uptimeMillis(), DRAG_TIME_MS, true, + mStartPoint, mEndPoint, LauncherInstrumentation.GestureScope.INSIDE); + } + + private void dropApp() { + mLauncher.sendPointer(mDownTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, + mEndPoint, LauncherInstrumentation.GestureScope.INSIDE); + } + + private void removeAppByName(String appName) { + // Find app within home screen. + UiObject2 app = findDescendantByAppName(findObjectByResourceName("workspace_grid"), + appName); + if (app == null) return; + + // Open app's popup container. + app.click(LONG_PRESS_DURATION_MS); + UiObject2 popupContainer = findObjectByResourceName("popup_container"); + assertThat(popupContainer).isNotNull(); + + // Grab & click remove button. + UiObject2 removeButton = findDescendantByTextOrDesc(popupContainer, "Remove"); + assertThat(removeButton).isNotNull(); + removeButton.click(); + } + + private UiObject2 findAppInWorkspace(String appName) { + UiObject2 workspace = findObjectByResourceName("workspace_grid"); + return findDescendantByAppName(workspace, appName); + } + + private UiObject2 findObjectByResourceName(String resourceName) { + return mDevice.wait(Until.findObject(By.res(mTargetPackage, resourceName)), WAIT_TIME_MS); + } + + private UiObject2 findDescendantByResourceName(UiObject2 outerObject, + String resourceName) { + assertThat(outerObject).isNotNull(); + return outerObject.findObject(By.res(mTargetPackage, resourceName)); + } + + private UiObject2 findDescendantByAppName(UiObject2 outerObject, String appName) { + assertThat(outerObject).isNotNull(); + return outerObject.findObject(By.clazz(TextView.class).text(appName) + .pkg(mDevice.getLauncherPackageName())); + } + + private UiObject2 findDescendantByTextOrDesc(UiObject2 outerObject, String content) { + assertThat(outerObject).isNotNull(); + UiObject2 innerObject = outerObject.findObject(By.desc(content)); + if (innerObject == null) innerObject = outerObject.findObject(By.text(content)); + return innerObject; + } + + private void startSecondaryDisplayActivity() { + mTargetContext.startActivity(( + new Intent(mTargetContext, SecondaryDisplayLauncher.class).addFlags( + FLAG_ACTIVITY_NEW_TASK))); + } + + private void setDragNDropFlag(Boolean status) { + Context context = new LauncherModelHelper().sandboxContext; + context.getSharedPreferences(FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE).edit() + .putBoolean(FeatureFlags.SECONDARY_DRAG_N_DROP_TO_PIN.key, status) + .commit(); + FeatureFlags.initialize(context); + startSecondaryDisplayActivity(); } }