diff --git a/tests/Android.bp b/tests/Android.bp index b1d4ef6703..e4fecc5ec8 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -63,7 +63,6 @@ filegroup { "src/com/android/launcher3/dragging/TaplDragTest.java", "src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java", "src/com/android/launcher3/ui/TaplTestsLauncher3Test.java", - "src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java", "src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java", ], } diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index ed5762d08c..8e4db5c6cf 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -21,11 +21,8 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static org.junit.Assert.assertTrue; -import android.content.BroadcastReceiver; import android.content.ComponentName; -import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.os.Process; import android.system.OsConstants; import android.util.Log; @@ -53,7 +50,6 @@ import org.junit.rules.TestRule; import java.util.Objects; import java.util.concurrent.Callable; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; @@ -216,38 +212,6 @@ public abstract class AbstractLauncherUiTest }, mLauncher, timeout); } - /** - * Broadcast receiver which blocks until the result is received. - */ - public class BlockingBroadcastReceiver extends BroadcastReceiver { - - private final CountDownLatch latch = new CountDownLatch(1); - private Intent mIntent; - - public BlockingBroadcastReceiver(String action) { - mTargetContext.registerReceiver(this, new IntentFilter(action), - Context.RECEIVER_EXPORTED/*UNAUDITED*/); - } - - @Override - public void onReceive(Context context, Intent intent) { - mIntent = intent; - latch.countDown(); - } - - public Intent blockingGetIntent() throws InterruptedException { - assertTrue("Timed Out", latch.await(DEFAULT_BROADCAST_TIMEOUT_SECS, TimeUnit.SECONDS)); - mTargetContext.unregisterReceiver(this); - return mIntent; - } - - public Intent blockingGetExtraIntent() throws InterruptedException { - Intent intent = blockingGetIntent(); - return intent == null ? null : (Intent) intent.getParcelableExtra( - Intent.EXTRA_INTENT); - } - } - public static void startAppFast(String packageName) { startIntent( getInstrumentation().getContext().getPackageManager().getLaunchIntentForPackage( diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java similarity index 54% rename from tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java rename to tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java index 7845222b6e..bb645d714f 100644 --- a/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java @@ -1,17 +1,17 @@ /* * Copyright (C) 2017 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 + * 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 + * 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. + * 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.widget; @@ -24,23 +24,34 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import android.appwidget.AppWidgetManager; +import android.content.Context; import android.content.Intent; +import android.os.Process; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; -import androidx.test.runner.AndroidJUnit4; +import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; import com.android.launcher3.celllayout.FavoriteItemsTransaction; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.testcomponent.WidgetConfigActivity; -import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; import com.android.launcher3.ui.TestViewHelpers; +import com.android.launcher3.util.BaseLauncherActivityTest; +import com.android.launcher3.util.BlockingBroadcastReceiver; +import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.Wait; import com.android.launcher3.util.rule.ShellCommandRule; +import com.android.launcher3.views.OptionsPopupView; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; +import com.android.launcher3.widget.PendingAddWidgetInfo; +import com.android.launcher3.widget.WidgetCell; +import com.android.launcher3.widget.picker.WidgetsFullSheet; +import com.android.launcher3.widget.picker.WidgetsListAdapter; +import com.android.launcher3.widget.picker.WidgetsRecyclerView; import org.junit.Before; import org.junit.Rule; @@ -52,7 +63,7 @@ import org.junit.runner.RunWith; */ @LargeTest @RunWith(AndroidJUnit4.class) -public class TaplAddConfigWidgetTest extends AbstractLauncherUiTest { +public class AddConfigWidgetTest extends BaseLauncherActivityTest { @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind(); @@ -62,12 +73,10 @@ public class TaplAddConfigWidgetTest extends AbstractLauncherUiTest { private int mWidgetId; - @Override @Before public void setUp() throws Exception { - super.setUp(); mWidgetInfo = TestViewHelpers.findWidgetProvider(true /* hasConfigureScreen */); - mAppWidgetManager = AppWidgetManager.getInstance(mTargetContext); + mAppWidgetManager = AppWidgetManager.getInstance(targetContext()); } @Test @@ -82,80 +91,86 @@ public class TaplAddConfigWidgetTest extends AbstractLauncherUiTest { runTest(false); } - /** * @param acceptConfig accept the config activity */ private void runTest(boolean acceptConfig) throws Throwable { - commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext)); + new FavoriteItemsTransaction(targetContext()).commit(); + loadLauncherSync(); - // Drag widget to homescreen + // Add widget to homescreen WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor(); - mLauncher.getWorkspace() - .openAllWidgets() - .getWidget(mWidgetInfo.getLabel()) - .dragToWorkspace(true, false); + executeOnLauncher(OptionsPopupView::openWidgets); + uiDevice.waitForIdle(); + + // Select the widget header + Context testContext = getInstrumentation().getContext(); + String packageName = testContext.getPackageName(); + executeOnLauncher(l -> { + WidgetsRecyclerView wrv = WidgetsFullSheet.getWidgetsView(l); + WidgetsListAdapter adapter = (WidgetsListAdapter) wrv.getAdapter(); + int pos = adapter.getItems().indexOf( + adapter.getItems().stream() + .filter(entry -> packageName.equals(entry.mPkgItem.packageName)) + .findFirst() + .get()); + wrv.getLayoutManager().scrollToPosition(pos); + adapter.onHeaderClicked(true, new PackageUserKey(packageName, Process.myUserHandle())); + }); + uiDevice.waitForIdle(); + + View widgetView = getOnceNotNull("Widget not found", l -> searchView(l.getDragLayer(), v -> + v instanceof WidgetCell + && v.getTag() instanceof PendingAddWidgetInfo pawi + && mWidgetInfo.provider.equals(pawi.componentName))); + addToWorkspace(widgetView); + // Widget id for which the config activity was opened mWidgetId = monitor.getWidgetId(); // Verify that the widget id is valid and bound assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId)); + setResult(acceptConfig); - setResultAndWaitForAnimation(acceptConfig); if (acceptConfig) { - Wait.atMost("", new WidgetSearchCondition(), mLauncher); + getOnceNotNull("Widget was not added", l -> { + // Close the resize frame before searching for widget + AbstractFloatingView.closeAllOpenViews(l); + return l.getWorkspace().getFirstMatch(new WidgetSearchCondition()); + }); assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId)); } else { // Verify that the widget id is deleted. - Wait.atMost("", () -> mAppWidgetManager.getAppWidgetInfo(mWidgetId) == null, - mLauncher); + Wait.atMost("", () -> mAppWidgetManager.getAppWidgetInfo(mWidgetId) == null); } } - private static void setResult(boolean success) { + private void setResult(boolean success) { getInstrumentation().getTargetContext().sendBroadcast( WidgetConfigActivity.getCommandIntent(WidgetConfigActivity.class, success ? "clickOK" : "clickCancel")); - } - - private void setResultAndWaitForAnimation(boolean success) { - if (mLauncher.isLauncher3()) { - setResult(success); - } else { - mLauncher.executeAndWaitForWallpaperAnimation( - () -> setResult(success), - "setting widget coinfig result"); - } + uiDevice.waitForIdle(); } /** * Condition for searching widget id */ - private class WidgetSearchCondition implements Wait.Condition, ItemOperator { - - @Override - public boolean isTrue() throws Throwable { - return mMainThreadExecutor.submit(() -> { - Launcher l = Launcher.ACTIVITY_TRACKER.getCreatedContext(); - return l != null && l.getWorkspace().getFirstMatch(this) != null; - }).get(); - } + private class WidgetSearchCondition implements ItemOperator { @Override public boolean evaluate(ItemInfo info, View view) { - return info instanceof LauncherAppWidgetInfo - && ((LauncherAppWidgetInfo) info).providerName.getClassName().equals( - mWidgetInfo.provider.getClassName()) - && ((LauncherAppWidgetInfo) info).appWidgetId == mWidgetId; + return info instanceof LauncherAppWidgetInfo lawi + && lawi.providerName.equals(mWidgetInfo.provider) + && lawi.appWidgetId == mWidgetId; } } /** * Broadcast receiver for receiving widget config activity status. */ - private class WidgetConfigStartupMonitor extends BlockingBroadcastReceiver { + private static class WidgetConfigStartupMonitor extends BlockingBroadcastReceiver { - public WidgetConfigStartupMonitor() { + WidgetConfigStartupMonitor() { super(WidgetConfigActivity.class.getName()); } diff --git a/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java similarity index 77% rename from tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java rename to tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java index 4cdbd96641..8846d6593a 100644 --- a/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java @@ -1,17 +1,17 @@ /* * Copyright (C) 2017 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 + * 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 + * 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. + * 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.widget; @@ -22,12 +22,12 @@ import static com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_PROVID import static com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; import static com.android.launcher3.provider.LauncherDbUtils.itemIdMatch; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; +import static com.android.launcher3.util.TestUtil.getOnUiThread; +import static com.android.launcher3.util.Wait.atMost; import static com.android.launcher3.util.WidgetUtils.createWidgetInfo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; import android.appwidget.AppWidgetManager; import android.content.ComponentName; @@ -36,6 +36,7 @@ import android.content.pm.PackageInstaller.SessionParams; import android.content.pm.PackageManager; import android.database.Cursor; import android.os.Bundle; +import android.text.TextUtils; import android.widget.RemoteViews; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -49,13 +50,12 @@ import com.android.launcher3.R; import com.android.launcher3.celllayout.FavoriteItemsTransaction; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.pm.InstallSessionHelper; -import com.android.launcher3.tapl.Widget; -import com.android.launcher3.tapl.Workspace; -import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.TestViewHelpers; -import com.android.launcher3.util.TestUtil; +import com.android.launcher3.util.BaseLauncherActivityTest; import com.android.launcher3.util.rule.ShellCommandRule; +import com.android.launcher3.widget.LauncherAppWidgetHostView; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; +import com.android.launcher3.widget.PendingAppWidgetHostView; import com.android.launcher3.widget.WidgetManagerHelper; import org.junit.After; @@ -67,6 +67,7 @@ import org.junit.runner.RunWith; import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; +import java.util.function.Function; /** * Tests for bind widget flow. @@ -75,7 +76,7 @@ import java.util.function.Consumer; */ @LargeTest @RunWith(AndroidJUnit4.class) -public class TaplBindWidgetTest extends AbstractLauncherUiTest { +public class BindWidgetTest extends BaseLauncherActivityTest { @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind(); @@ -87,11 +88,9 @@ public class TaplBindWidgetTest extends AbstractLauncherUiTest { private LauncherModel mModel; - @Override @Before public void setUp() throws Exception { - super.setUp(); - mModel = LauncherAppState.getInstance(mTargetContext).getModel(); + mModel = LauncherAppState.getInstance(targetContext()).getModel(); } @After @@ -101,7 +100,7 @@ public class TaplBindWidgetTest extends AbstractLauncherUiTest { } if (mSessionId > -1) { - mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId); + targetContext().getPackageManager().getPackageInstaller().abandonSession(mSessionId); } } @@ -122,13 +121,12 @@ public class TaplBindWidgetTest extends AbstractLauncherUiTest { LauncherAppWidgetProviderInfo info = addWidgetToScreen(false, false, item -> item.appWidgetId = -33); - final Workspace workspace = mLauncher.getWorkspace(); // Item deleted from db mCursor = queryItem(); assertEquals(0, mCursor.getCount()); // The view does not exist - assertTrue("Widget exists", workspace.tryGetWidget(info.label, 0) == null); + verifyItemEventuallyNull("Widget exists", widgetProvider(info)); } @Test @@ -154,18 +152,19 @@ public class TaplBindWidgetTest extends AbstractLauncherUiTest { // Widget has a valid Id now. assertEquals(0, mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED)) & FLAG_ID_NOT_VALID); - assertNotNull(AppWidgetManager.getInstance(mTargetContext) + assertNotNull(AppWidgetManager.getInstance(targetContext()) .getAppWidgetInfo(mCursor.getInt(mCursor.getColumnIndex( LauncherSettings.Favorites.APPWIDGET_ID)))); // send OPTION_APPWIDGET_RESTORE_COMPLETED int appWidgetId = mCursor.getInt( mCursor.getColumnIndex(LauncherSettings.Favorites.APPWIDGET_ID)); - AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mTargetContext); + AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(targetContext()); Bundle b = new Bundle(); b.putBoolean(WidgetManagerHelper.WIDGET_OPTION_RESTORE_COMPLETED, true); - RemoteViews remoteViews = new RemoteViews(mTargetPackage, R.layout.appwidget_not_ready); + RemoteViews remoteViews = new RemoteViews( + targetContext().getPackageName(), R.layout.appwidget_not_ready); appWidgetManager.updateAppWidgetOptions(appWidgetId, b); appWidgetManager.updateAppWidget(appWidgetId, remoteViews); @@ -175,15 +174,14 @@ public class TaplBindWidgetTest extends AbstractLauncherUiTest { WidgetManagerHelper.WIDGET_OPTION_RESTORE_COMPLETED)); executeOnLauncher(l -> l.getAppWidgetHolder().startListening()); verifyWidgetPresent(info); - assertNull(mLauncher.getWorkspace().tryGetPendingWidget(100)); + verifyItemEventuallyNull("Pending widget exists", pendingWidgetProvider()); } @Test public void testPendingWidget_notRestored_removed() { addPendingItemToScreen(getInvalidWidgetInfo(), FLAG_ID_NOT_VALID | FLAG_PROVIDER_NOT_READY); - assertTrue("Pending widget exists", - mLauncher.getWorkspace().tryGetPendingWidget(0) == null); + verifyItemEventuallyNull("Pending widget exists", pendingWidgetProvider()); // Item deleted from db mCursor = queryItem(); assertEquals(0, mCursor.getCount()); @@ -216,7 +214,7 @@ public class TaplBindWidgetTest extends AbstractLauncherUiTest { // Create an active installer session SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL); params.setAppPackageName(item.providerName.getPackageName()); - PackageInstaller installer = mTargetContext.getPackageManager().getPackageInstaller(); + PackageInstaller installer = targetContext().getPackageManager().getPackageInstaller(); mSessionId = installer.createSession(params); addPendingItemToScreen(item, FLAG_ID_NOT_VALID | FLAG_PROVIDER_NOT_READY); @@ -234,36 +232,47 @@ public class TaplBindWidgetTest extends AbstractLauncherUiTest { } private void verifyWidgetPresent(LauncherAppWidgetProviderInfo info) { - final Widget widget = mLauncher.getWorkspace().tryGetWidget(info.label, - TestUtil.DEFAULT_UI_TIMEOUT); - assertTrue("Widget is not present", - widget != null); + getOnceNotNull("Widget is not present", widgetProvider(info)); } private void verifyPendingWidgetPresent() { - final Widget widget = mLauncher.getWorkspace().tryGetPendingWidget( - TestUtil.DEFAULT_UI_TIMEOUT); - assertTrue("Pending widget is not present", - widget != null); + getOnceNotNull("Widget is not present", pendingWidgetProvider()); + } + + private Function pendingWidgetProvider() { + return l -> l.getWorkspace().getFirstMatch( + (item, view) -> view instanceof PendingAppWidgetHostView); + } + + private Function widgetProvider(LauncherAppWidgetProviderInfo info) { + return l -> l.getWorkspace().getFirstMatch((item, view) -> + view instanceof LauncherAppWidgetHostView + && TextUtils.equals(info.label, view.getContentDescription())); + } + + private void verifyItemEventuallyNull(String message, Function provider) { + atMost(message, () -> getFromLauncher(provider) == null); } private void addPendingItemToScreen(LauncherAppWidgetInfo item, int restoreStatus) { item.restoreStatus = restoreStatus; item.screenId = FIRST_SCREEN_ID; - commitTransactionAndLoadHome( - new FavoriteItemsTransaction(mTargetContext).addItem(() -> item)); + new FavoriteItemsTransaction(targetContext()).addItem(() -> item).commit(); + loadLauncherSync(); } private LauncherAppWidgetProviderInfo addWidgetToScreen(boolean hasConfigureScreen, boolean bindWidget, Consumer itemOverride) { LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(hasConfigureScreen); - commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext) + new FavoriteItemsTransaction(targetContext()) .addItem(() -> { - LauncherAppWidgetInfo item = createWidgetInfo(info, mTargetContext, bindWidget); + LauncherAppWidgetInfo item = + createWidgetInfo(info, targetContext(), bindWidget); item.screenId = FIRST_SCREEN_ID; itemOverride.accept(item); return item; - })); + }).commit(); + loadLauncherSync(); return info; } @@ -277,13 +286,13 @@ public class TaplBindWidgetTest extends AbstractLauncherUiTest { Set activePackage = getOnUiThread(() -> { Set packages = new HashSet<>(); - InstallSessionHelper.INSTANCE.get(mTargetContext).getActiveSessions() + InstallSessionHelper.INSTANCE.get(targetContext()).getActiveSessions() .keySet().forEach(packageUserKey -> packages.add(packageUserKey.mPackageName)); return packages; }); while (true) { try { - mTargetContext.getPackageManager().getPackageInfo( + targetContext().getPackageManager().getPackageInfo( pkg, PackageManager.GET_UNINSTALLED_PACKAGES); } catch (Exception e) { if (!activePackage.contains(pkg)) { diff --git a/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java similarity index 61% rename from tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java rename to tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java index fe3b2eea6c..2fb7987f05 100644 --- a/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java +++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java @@ -1,34 +1,41 @@ /* * Copyright (C) 2017 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 + * 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 + * 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. + * 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.widget; import static android.app.PendingIntent.FLAG_MUTABLE; import static android.app.PendingIntent.FLAG_ONE_SHOT; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; +import static java.util.regex.Pattern.CASE_INSENSITIVE; + import android.app.PendingIntent; import android.appwidget.AppWidgetManager; +import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.view.View; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.LargeTest; -import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.BySelector; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherSettings.Favorites; @@ -37,14 +44,13 @@ import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.shortcuts.ShortcutKey; -import com.android.launcher3.tapl.AddToHomeScreenPrompt; import com.android.launcher3.testcomponent.AppWidgetNoConfig; import com.android.launcher3.testcomponent.AppWidgetWithConfig; import com.android.launcher3.testcomponent.RequestPinItemActivity; -import com.android.launcher3.ui.AbstractLauncherUiTest; +import com.android.launcher3.util.BaseLauncherActivityTest; +import com.android.launcher3.util.BlockingBroadcastReceiver; import com.android.launcher3.util.LauncherBindableItemsContainer.ItemOperator; -import com.android.launcher3.util.Wait; -import com.android.launcher3.util.Wait.Condition; +import com.android.launcher3.util.TestUtil; import com.android.launcher3.util.rule.ShellCommandRule; import org.junit.Before; @@ -53,25 +59,27 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.UUID; +import java.util.regex.Pattern; /** * Test to verify pin item request flow. */ @LargeTest @RunWith(AndroidJUnit4.class) -public class TaplRequestPinItemTest extends AbstractLauncherUiTest { +public class RequestPinItemTest extends BaseLauncherActivityTest { @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind(); + @Rule + public ShellCommandRule mDefaultLauncherRule = ShellCommandRule.setDefaultLauncher(); + private String mCallbackAction; private String mShortcutId; private int mAppWidgetId; - @Override @Before public void setUp() throws Exception { - super.setUp(); mCallbackAction = UUID.randomUUID().toString(); mShortcutId = UUID.randomUUID().toString(); } @@ -81,9 +89,9 @@ public class TaplRequestPinItemTest extends AbstractLauncherUiTest { @Test public void testPinWidgetNoConfig() throws Throwable { - runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo && - ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId && - ((LauncherAppWidgetInfo) info).providerName.getClassName() + runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo + && ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId + && ((LauncherAppWidgetInfo) info).providerName.getClassName() .equals(AppWidgetNoConfig.class.getName())); } @@ -94,18 +102,18 @@ public class TaplRequestPinItemTest extends AbstractLauncherUiTest { RequestPinItemActivity.class, "setRemoteViewColor").putExtra( RequestPinItemActivity.EXTRA_PARAM + "0", Color.RED); - runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo && - ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId && - ((LauncherAppWidgetInfo) info).providerName.getClassName() + runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo + && ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId + && ((LauncherAppWidgetInfo) info).providerName.getClassName() .equals(AppWidgetNoConfig.class.getName()), command); } @Test public void testPinWidgetWithConfig() throws Throwable { runTest("pinWidgetWithConfig", true, - (info, view) -> info instanceof LauncherAppWidgetInfo && - ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId && - ((LauncherAppWidgetInfo) info).providerName.getClassName() + (info, view) -> info instanceof LauncherAppWidgetInfo + && ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId + && ((LauncherAppWidgetInfo) info).providerName.getClassName() .equals(AppWidgetWithConfig.class.getName())); } @@ -119,47 +127,48 @@ public class TaplRequestPinItemTest extends AbstractLauncherUiTest { runTest("pinShortcut", false, new ItemOperator() { @Override public boolean evaluate(ItemInfo info, View view) { - return info instanceof WorkspaceItemInfo && - info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT && - ShortcutKey.fromItemInfo(info).getId().equals(mShortcutId); + return info instanceof WorkspaceItemInfo + && info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT + && ShortcutKey.fromItemInfo(info).getId().equals(mShortcutId); } }, command); } private void runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher, Intent... commandIntents) throws Throwable { - commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext)); + new FavoriteItemsTransaction(targetContext()).commit(); + loadLauncherSync(); // Open Pin item activity BlockingBroadcastReceiver openMonitor = new BlockingBroadcastReceiver( RequestPinItemActivity.class.getName()); - mLauncher. - getWorkspace(). - switchToAllApps(). - getAppIcon("Test Pin Item"). - launch(getAppPackageName()); + Context testContext = getInstrumentation().getContext(); + startAppFast( + testContext.getPackageName(), + new Intent(testContext, RequestPinItemActivity.class)); assertNotNull(openMonitor.blockingGetExtraIntent()); // Set callback - PendingIntent callback = PendingIntent.getBroadcast(mTargetContext, 0, - new Intent(mCallbackAction).setPackage(mTargetContext.getPackageName()), + PendingIntent callback = PendingIntent.getBroadcast(targetContext(), 0, + new Intent(mCallbackAction).setPackage(targetContext().getPackageName()), FLAG_ONE_SHOT | FLAG_MUTABLE); - mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent( + targetContext().sendBroadcast(RequestPinItemActivity.getCommandIntent( RequestPinItemActivity.class, "setCallback").putExtra( RequestPinItemActivity.EXTRA_PARAM + "0", callback)); for (Intent command : commandIntents) { - mTargetContext.sendBroadcast(command); + targetContext().sendBroadcast(command); } // call the requested method to start the flow - mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent( + targetContext().sendBroadcast(RequestPinItemActivity.getCommandIntent( RequestPinItemActivity.class, activityMethod)); - final AddToHomeScreenPrompt addToHomeScreenPrompt = mLauncher.getAddToHomeScreenPrompt(); // Accept confirmation: BlockingBroadcastReceiver resultReceiver = new BlockingBroadcastReceiver(mCallbackAction); - addToHomeScreenPrompt.addAutomatically(); + BySelector selector = By.text(Pattern.compile("^Add to home screen$", CASE_INSENSITIVE)) + .pkg(targetContext().getPackageName()); + uiDevice.wait(device -> device.findObject(selector), TestUtil.DEFAULT_UI_TIMEOUT).click(); Intent result = resultReceiver.blockingGetIntent(); assertNotNull(result); mAppWidgetId = result.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); @@ -167,28 +176,9 @@ public class TaplRequestPinItemTest extends AbstractLauncherUiTest { assertNotSame(-1, mAppWidgetId); } - // Go back to home - mLauncher.goHome(); - Wait.atMost("", new ItemSearchCondition(itemMatcher), mLauncher); - } - - /** - * Condition for for an item - */ - private class ItemSearchCondition implements Condition { - - private final ItemOperator mOp; - - ItemSearchCondition(ItemOperator op) { - mOp = op; - } - - @Override - public boolean isTrue() throws Throwable { - return mMainThreadExecutor.submit(() -> { - Launcher l = Launcher.ACTIVITY_TRACKER.getCreatedContext(); - return l != null && l.getWorkspace().getFirstMatch(mOp) != null; - }).get(); - } + // Reload activity, so that the activity is focused + closeCurrentActivity(); + loadLauncherSync(); + getOnceNotNull("", l -> l.getWorkspace().getFirstMatch(itemMatcher)); } } diff --git a/tests/src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java b/tests/src/com/android/launcher3/ui/widget/WidgetPickerTest.java similarity index 68% rename from tests/src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java rename to tests/src/com/android/launcher3/ui/widget/WidgetPickerTest.java index 19c585085b..caad1d98b7 100644 --- a/tests/src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java +++ b/tests/src/com/android/launcher3/ui/widget/WidgetPickerTest.java @@ -22,24 +22,30 @@ import static org.junit.Assert.assertTrue; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.MediumTest; +import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; -import com.android.launcher3.tapl.Widgets; -import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape; +import com.android.launcher3.util.BaseLauncherActivityTest; +import com.android.launcher3.util.rule.ScreenRecordRule; import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord; +import com.android.launcher3.views.OptionsPopupView; import com.android.launcher3.widget.picker.WidgetsFullSheet; import com.android.launcher3.widget.picker.WidgetsRecyclerView; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestRule; import org.junit.runner.RunWith; /** - * This test run in both Out of process (Oop) and in-process (Ipc). * Make sure the basic interactions with the WidgetPicker works. */ @MediumTest @RunWith(AndroidJUnit4.class) -public class TaplWidgetPickerTest extends AbstractLauncherUiTest { +public class WidgetPickerTest extends BaseLauncherActivityTest { + + @Rule + public TestRule screenRecordRule = new ScreenRecordRule(); private WidgetsRecyclerView getWidgetsView(Launcher launcher) { return WidgetsFullSheet.getWidgetsView(launcher); @@ -56,30 +62,21 @@ public class TaplWidgetPickerTest extends AbstractLauncherUiTest { @ScreenRecord @PortraitLandscape public void testWidgets() { - mLauncher.goHome(); + loadLauncherSync(); // 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("openAllWidgets() returned null", + getFromLauncher(OptionsPopupView::openWidgets)); + WidgetsRecyclerView widgets = getFromLauncher(this::getWidgetsView); assertNotNull("getAllWidgets() returned null", widgets); - executeOnLauncher(launcher -> - assertTrue("Widgets is not shown", getWidgetsView(launcher).isShown())); + executeOnLauncher(launcher -> assertTrue("Widgets is not shown", widgets.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)); + executeOnLauncher(AbstractFloatingView::closeAllOpenViews); + uiDevice.waitForIdle(); - widgets.flingBackward(); - executeOnLauncher(launcher -> assertTrue("Flinging backward didn't scroll widgets", - getWidgetsScroll(launcher) < flingForwardY)); - - mLauncher.goHome(); waitForLauncherCondition("Widgets were not closed", launcher -> getWidgetsView(launcher) == null); } diff --git a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java index cfc0a6b286..c623513f07 100644 --- a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java +++ b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java @@ -15,8 +15,6 @@ */ package com.android.launcher3.ui.workspace; -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - import static com.android.launcher3.AbstractFloatingView.TYPE_ACTION_POPUP; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.TestConstants.AppNames.TEST_APP_NAME; @@ -29,11 +27,9 @@ import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.ContentValues; import android.net.Uri; -import android.view.View; import android.view.ViewGroup; import androidx.test.filters.LargeTest; -import androidx.test.uiautomator.UiDevice; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BubbleTextView; @@ -49,9 +45,6 @@ import com.android.launcher3.util.TestUtil; import org.junit.Test; -import java.util.ArrayDeque; -import java.util.Queue; - /** * Tests for theme icon support in Launcher * @@ -137,27 +130,10 @@ public class ThemeIconsTest extends BaseLauncherActivityTest { } catch (Exception e) { throw new RuntimeException(e); } - - // Find the app icon - Queue viewQueue = new ArrayDeque<>(); - viewQueue.add(parent); - BubbleTextView icon = null; - while (!viewQueue.isEmpty()) { - View view = viewQueue.poll(); - if (view instanceof ViewGroup) { - parent = (ViewGroup) view; - for (int i = parent.getChildCount() - 1; i >= 0; i--) { - viewQueue.add(parent.getChildAt(i)); - } - } else if (view instanceof BubbleTextView btv) { - if (btv.getContentDescription() != null - && title.equals(btv.getContentDescription().toString())) { - icon = btv; - break; - } - } - } - return icon; + return (BubbleTextView) searchView(parent, v -> + v instanceof BubbleTextView btv + && btv.getContentDescription() != null + && title.equals(btv.getContentDescription().toString())); } private BubbleTextView verifyIconTheme(String title, ViewGroup parent, boolean isThemed) { @@ -193,11 +169,4 @@ public class ThemeIconsTest extends BaseLauncherActivityTest { rv.getLayoutManager().scrollToPosition(pos); }); } - - private void addToWorkspace(View btv) { - TestUtil.runOnExecutorSync(MAIN_EXECUTOR, () -> - btv.getAccessibilityDelegate().performAccessibilityAction( - btv, com.android.launcher3.R.id.action_add_to_workspace, null)); - UiDevice.getInstance(getInstrumentation()).waitForIdle(); - } } diff --git a/tests/src/com/android/launcher3/util/BaseLauncherActivityTest.kt b/tests/src/com/android/launcher3/util/BaseLauncherActivityTest.kt index 476e497807..644659296d 100644 --- a/tests/src/com/android/launcher3/util/BaseLauncherActivityTest.kt +++ b/tests/src/com/android/launcher3/util/BaseLauncherActivityTest.kt @@ -22,6 +22,9 @@ import android.view.InputDevice import android.view.KeyCharacterMap import android.view.KeyEvent import android.view.MotionEvent +import android.view.View +import android.view.ViewGroup +import androidx.core.view.children import androidx.lifecycle.Lifecycle.State.RESUMED import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ActivityScenario.ActivityAction @@ -30,11 +33,13 @@ import androidx.test.uiautomator.UiDevice import com.android.launcher3.Launcher import com.android.launcher3.LauncherAppState import com.android.launcher3.LauncherState +import com.android.launcher3.R import com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST import com.android.launcher3.tapl.TestHelpers import com.android.launcher3.util.ModelTestExtensions.loadModelSync import com.android.launcher3.util.Wait.atMost import java.util.function.Function +import java.util.function.Predicate import java.util.function.Supplier import org.junit.After @@ -56,6 +61,8 @@ open class BaseLauncherActivityTest { ) .also { currentScenario = it } + @JvmField val uiDevice = UiDevice.getInstance(getInstrumentation()) + @After fun closeCurrentActivity() { currentScenario?.close() @@ -136,18 +143,43 @@ open class BaseLauncherActivityTest { event.recycle() } - fun startAppFast(packageName: String) { - val intent = targetContext().packageManager.getLaunchIntentForPackage(packageName)!! + @JvmOverloads + fun startAppFast( + packageName: String, + intent: Intent = targetContext().packageManager.getLaunchIntentForPackage(packageName)!!, + ) { intent.addCategory(Intent.CATEGORY_LAUNCHER) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) targetContext().startActivity(intent) - UiDevice.getInstance(getInstrumentation()).waitForIdle() + uiDevice.waitForIdle() } fun freezeAllApps() = executeOnLauncher { it.appsView.appsStore.enableDeferUpdates(DEFER_UPDATES_TEST) } - fun executeShellCommand(cmd: String) = - UiDevice.getInstance(getInstrumentation()).executeShellCommand(cmd) + fun executeShellCommand(cmd: String) = uiDevice.executeShellCommand(cmd) + + fun addToWorkspace(view: View) { + TestUtil.runOnExecutorSync(Executors.MAIN_EXECUTOR) { + view.accessibilityDelegate.performAccessibilityAction( + view, + R.id.action_add_to_workspace, + null, + ) + } + UiDevice.getInstance(getInstrumentation()).waitForIdle() + } + + fun ViewGroup.searchView(filter: Predicate): View? { + if (filter.test(this)) return this + for (child in children) { + if (filter.test(child)) return child + if (child is ViewGroup) + child.searchView(filter)?.let { + return it + } + } + return null + } } diff --git a/tests/src/com/android/launcher3/util/BlockingBroadcastReceiver.kt b/tests/src/com/android/launcher3/util/BlockingBroadcastReceiver.kt new file mode 100644 index 0000000000..20881d19fb --- /dev/null +++ b/tests/src/com/android/launcher3/util/BlockingBroadcastReceiver.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 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.util + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.Parcelable +import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation +import java.util.concurrent.CompletableFuture +import java.util.concurrent.TimeUnit.SECONDS + +private const val DEFAULT_BROADCAST_TIMEOUT_SECS: Long = 10 + +/** Broadcast receiver which blocks until the result is received. */ +open class BlockingBroadcastReceiver(action: String) : BroadcastReceiver() { + + val value = CompletableFuture() + + init { + getInstrumentation() + .targetContext + .registerReceiver(this, IntentFilter(action), Context.RECEIVER_EXPORTED) + } + + override fun onReceive(context: Context, intent: Intent) { + value.complete(intent) + } + + @Throws(InterruptedException::class) + fun blockingGetIntent(): Intent = + value.get(DEFAULT_BROADCAST_TIMEOUT_SECS, SECONDS).also { + getInstrumentation().targetContext.unregisterReceiver(this) + } + + @Throws(InterruptedException::class) + fun blockingGetExtraIntent(): Intent? = + blockingGetIntent().getParcelableExtra(Intent.EXTRA_INTENT) as Intent? +}