diff --git a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java new file mode 100644 index 0000000000..67261796f8 --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep; + +import static androidx.test.InstrumentationRegistry.getContext; +import static androidx.test.InstrumentationRegistry.getInstrumentation; + +import static com.android.launcher3.testcomponent.TestCommandReceiver.EXTRA_VALUE; +import static com.android.launcher3.testcomponent.TestCommandReceiver.SET_LIST_VIEW_SERVICE_BINDER; +import static com.android.launcher3.ui.widget.BindWidgetTest.createWidgetInfo; +import static com.android.quickstep.NavigationModeSwitchRule.Mode.ZERO_BUTTON; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.spy; + +import android.appwidget.AppWidgetManager; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.util.SparseArray; +import android.view.View; +import android.view.ViewConfiguration; +import android.widget.RemoteViews; + +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.Until; + +import com.android.launcher3.LauncherAppWidgetInfo; +import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.tapl.Background; +import com.android.launcher3.testcomponent.ListViewService; +import com.android.launcher3.testcomponent.ListViewService.SimpleViewsFactory; +import com.android.launcher3.testcomponent.TestCommandReceiver; +import com.android.launcher3.ui.TaplTestsLauncher3; +import com.android.launcher3.ui.TestViewHelpers; +import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.lang.reflect.Field; +import java.util.function.IntConsumer; + +/** + * Test to verify view inflation does not happen during swipe up. + * To verify view inflation, we setup a dummy ViewConfiguration and check if any call to that class + * does from a View.init method or not. + * + * Alternative approaches considered: + * Overriding LayoutInflater: This does not cover views initialized + * directly (ex: new LinearLayout) + * Using ExtendedMockito: Mocking static methods from platform classes (loaded in zygote) makes + * the main thread extremely slow and untestable + */ +@LargeTest +@RunWith(AndroidJUnit4.class) +public class ViewInflationDuringSwipeUp extends AbstractQuickStepTest { + + private ContentResolver mResolver; + private SparseArray mConfigMap; + private InitTracker mInitTracker; + + @Before + public void setUp() throws Exception { + super.setUp(); + TaplTestsLauncher3.initialize(this); + + mResolver = mTargetContext.getContentResolver(); + LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); + + // Get static configuration map + Field field = ViewConfiguration.class.getDeclaredField("sConfigurations"); + field.setAccessible(true); + mConfigMap = (SparseArray) field.get(null); + + mInitTracker = new InitTracker(); + } + + @Test + @NavigationModeSwitch(mode = ZERO_BUTTON) + public void testSwipeUpFromApp() throws Exception { + try { + // Go to overview once so that all views are initialized and cached + startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); + mLauncher.getBackground().switchToOverview().dismissAllTasks(); + + // Track view creations + mInitTracker.startTracking(); + + startTestActivity(2); + mLauncher.getBackground().switchToOverview(); + + assertEquals("Views inflated during swipe up", 0, mInitTracker.viewInitCount); + } finally { + mConfigMap.clear(); + } + } + + @Test + @NavigationModeSwitch(mode = ZERO_BUTTON) + public void testSwipeUpFromApp_widget_update() { + String dummyText = "Some random dummy text"; + + executeSwipeUpTestWithWidget( + widgetId -> { }, + widgetId -> AppWidgetManager.getInstance(getContext()) + .updateAppWidget(widgetId, createMainWidgetViews(dummyText)), + dummyText); + } + + @Test + @NavigationModeSwitch(mode = ZERO_BUTTON) + public void testSwipeUp_with_list_widgets() { + SimpleViewsFactory viewFactory = new SimpleViewsFactory(); + viewFactory.viewCount = 1; + Bundle args = new Bundle(); + args.putBinder(EXTRA_VALUE, viewFactory.toBinder()); + TestCommandReceiver.callCommand(SET_LIST_VIEW_SERVICE_BINDER, null, args); + + try { + executeSwipeUpTestWithWidget( + widgetId -> { + // Initialize widget + RemoteViews views = createMainWidgetViews("List widget title"); + views.setRemoteAdapter(android.R.id.list, + new Intent(getContext(), ListViewService.class)); + AppWidgetManager.getInstance(getContext()).updateAppWidget(widgetId, views); + verifyWidget(viewFactory.getLabel(0)); + }, + widgetId -> { + // Update widget + viewFactory.viewCount = 2; + AppWidgetManager.getInstance(getContext()) + .notifyAppWidgetViewDataChanged(widgetId, android.R.id.list); + }, + viewFactory.getLabel(1) + ); + } finally { + TestCommandReceiver.callCommand(SET_LIST_VIEW_SERVICE_BINDER, null, new Bundle()); + } + } + + private void executeSwipeUpTestWithWidget(IntConsumer widgetIdCreationCallback, + IntConsumer updateBeforeSwipeUp, String finalWidgetText) { + try { + // Clear all existing data + LauncherSettings.Settings.call(mResolver, + LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); + LauncherSettings.Settings.call(mResolver, + LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG); + LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false); + LauncherAppWidgetInfo item = createWidgetInfo(info, true); + + addItemToScreen(item); + assertTrue("Widget is not present", + mLauncher.pressHome().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null); + int widgetId = item.appWidgetId; + + // Verify widget id + widgetIdCreationCallback.accept(widgetId); + + // Go to overview once so that all views are initialized and cached + startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); + mLauncher.getBackground().switchToOverview().dismissAllTasks(); + + // Track view creations + mInitTracker.startTracking(); + + startTestActivity(2); + Background background = mLauncher.getBackground(); + + // Update widget + updateBeforeSwipeUp.accept(widgetId); + + background.switchToOverview(); + assertEquals("Views inflated during swipe up", 0, mInitTracker.viewInitCount); + + // Widget is updated when going home + mInitTracker.disableLog(); + mLauncher.pressHome(); + verifyWidget(finalWidgetText); + assertNotEquals(1, mInitTracker.viewInitCount); + } finally { + mConfigMap.clear(); + } + } + + private void verifyWidget(String text) { + assertNotNull("Widget not updated", + UiDevice.getInstance(getInstrumentation()) + .wait(Until.findObject(By.text(text)), DEFAULT_UI_TIMEOUT)); + } + + private RemoteViews createMainWidgetViews(String title) { + Context c = getContext(); + int layoutId = c.getResources().getIdentifier( + "test_layout_widget_list", "layout", c.getPackageName()); + RemoteViews views = new RemoteViews(c.getPackageName(), layoutId); + views.setTextViewText(android.R.id.text1, title); + return views; + } + + private class InitTracker implements Answer { + + public int viewInitCount = 0; + + public boolean log = true; + + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + Exception ex = new Exception(); + + boolean found = false; + for (StackTraceElement ste : ex.getStackTrace()) { + if ("".equals(ste.getMethodName()) + && View.class.getName().equals(ste.getClassName())) { + found = true; + break; + } + } + if (found) { + viewInitCount++; + if (log) { + Log.d("InitTracker", "New view inflated", ex); + } + + } + return invocation.callRealMethod(); + } + + public void disableLog() { + log = false; + } + + public void startTracking() { + ViewConfiguration vc = ViewConfiguration.get(mTargetContext); + ViewConfiguration spyVC = spy(vc); + mConfigMap.put(mConfigMap.keyAt(mConfigMap.indexOfValue(vc)), spyVC); + doAnswer(this).when(spyVC).getScaledTouchSlop(); + } + } +} diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml index c6f55a7178..0b74dc4096 100644 --- a/tests/AndroidManifest-common.xml +++ b/tests/AndroidManifest-common.xml @@ -73,8 +73,12 @@ + + diff --git a/tests/res/layout/test_layout_widget_list.xml b/tests/res/layout/test_layout_widget_list.xml new file mode 100644 index 0000000000..0152040f7e --- /dev/null +++ b/tests/res/layout/test_layout_widget_list.xml @@ -0,0 +1,20 @@ + + + + + + + \ No newline at end of file diff --git a/tests/src/com/android/launcher3/testcomponent/ListViewService.java b/tests/src/com/android/launcher3/testcomponent/ListViewService.java new file mode 100644 index 0000000000..3da20e0991 --- /dev/null +++ b/tests/src/com/android/launcher3/testcomponent/ListViewService.java @@ -0,0 +1,95 @@ +/* + * 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.testcomponent; + +import android.content.Intent; +import android.os.IBinder; +import android.widget.RemoteViews; +import android.widget.RemoteViewsService; + +public class ListViewService extends RemoteViewsService { + + public static IBinder sBinderForTest; + + @Override + public RemoteViewsFactory onGetViewFactory(Intent intent) { + return new SimpleViewsFactory(); + } + + @Override + public IBinder onBind(Intent intent) { + return sBinderForTest != null ? sBinderForTest : super.onBind(intent); + } + + public static class SimpleViewsFactory implements RemoteViewsFactory { + + public int viewCount = 0; + + @Override + public void onCreate() { } + + @Override + public void onDataSetChanged() { } + + @Override + public void onDestroy() { } + + @Override + public int getCount() { + return viewCount; + } + + @Override + public RemoteViews getViewAt(int i) { + RemoteViews views = new RemoteViews("android", android.R.layout.simple_list_item_1); + views.setTextViewText(android.R.id.text1, getLabel(i)); + return views; + } + + public String getLabel(int i) { + return "Item " + i; + } + + @Override + public RemoteViews getLoadingView() { + return null; + } + + @Override + public int getViewTypeCount() { + return 1; + } + + @Override + public long getItemId(int i) { + return i; + } + + @Override + public boolean hasStableIds() { + return false; + } + + public IBinder toBinder() { + return new RemoteViewsService() { + @Override + public RemoteViewsFactory onGetViewFactory(Intent intent) { + return SimpleViewsFactory.this; + } + }.onBind(new Intent("dummy_intent")); + } + } +} diff --git a/tests/src/com/android/launcher3/testcomponent/TestCommandProvider.java b/tests/src/com/android/launcher3/testcomponent/TestCommandProvider.java new file mode 100644 index 0000000000..7682e0771c --- /dev/null +++ b/tests/src/com/android/launcher3/testcomponent/TestCommandProvider.java @@ -0,0 +1,130 @@ +/* + * 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.testcomponent; + +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; +import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; +import static android.content.pm.PackageManager.DONT_KILL_APP; +import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; + +import static com.android.launcher3.testcomponent.TestCommandReceiver.DISABLE_TEST_LAUNCHER; +import static com.android.launcher3.testcomponent.TestCommandReceiver.ENABLE_TEST_LAUNCHER; +import static com.android.launcher3.testcomponent.TestCommandReceiver.EXTRA_VALUE; +import static com.android.launcher3.testcomponent.TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE; +import static com.android.launcher3.testcomponent.TestCommandReceiver.KILL_PROCESS; +import static com.android.launcher3.testcomponent.TestCommandReceiver.SET_LIST_VIEW_SERVICE_BINDER; + +import android.app.Activity; +import android.app.ActivityManager; +import android.content.ComponentName; +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.util.Base64; + +import com.android.launcher3.tapl.TestHelpers; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; + +public class TestCommandProvider extends ContentProvider { + + @Override + public boolean onCreate() { + return true; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + throw new UnsupportedOperationException("unimplemented mock method"); + } + + @Override + public String getType(Uri uri) { + throw new UnsupportedOperationException("unimplemented mock method"); + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + throw new UnsupportedOperationException("unimplemented mock method"); + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + throw new UnsupportedOperationException("unimplemented mock method"); + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + throw new UnsupportedOperationException("unimplemented mock method"); + } + + @Override + public Bundle call(String method, String arg, Bundle extras) { + switch (method) { + case ENABLE_TEST_LAUNCHER: { + getContext().getPackageManager().setComponentEnabledSetting( + new ComponentName(getContext(), TestLauncherActivity.class), + COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP); + return null; + } + case DISABLE_TEST_LAUNCHER: { + getContext().getPackageManager().setComponentEnabledSetting( + new ComponentName(getContext(), TestLauncherActivity.class), + COMPONENT_ENABLED_STATE_DISABLED, DONT_KILL_APP); + return null; + } + case KILL_PROCESS: { + ((ActivityManager) getContext().getSystemService(Activity.ACTIVITY_SERVICE)) + .killBackgroundProcesses(arg); + return null; + } + + case GET_SYSTEM_HEALTH_MESSAGE: { + final Bundle response = new Bundle(); + response.putString("result", TestHelpers.getSystemHealthMessage(getContext())); + return response; + } + + case SET_LIST_VIEW_SERVICE_BINDER: { + ListViewService.sBinderForTest = extras.getBinder(EXTRA_VALUE); + return null; + } + } + return super.call(method, arg, extras); + } + + @Override + public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { + String path = Base64.encodeToString(uri.getPath().getBytes(), + Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP); + File file = new File(getContext().getCacheDir(), path); + if (!file.exists()) { + // Create an empty file so that we can pass its descriptor + try { + file.createNewFile(); + } catch (IOException e) { + } + } + + return ParcelFileDescriptor.open(file, MODE_READ_WRITE); + } +} diff --git a/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java b/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java index 6a6916eec3..eb6c3ed783 100644 --- a/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java +++ b/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java @@ -15,125 +15,36 @@ */ package com.android.launcher3.testcomponent; -import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; -import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; -import static android.content.pm.PackageManager.DONT_KILL_APP; -import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; - -import android.app.Activity; -import android.app.ActivityManager; import android.app.Instrumentation; -import android.content.ComponentName; -import android.content.ContentProvider; -import android.content.ContentValues; -import android.database.Cursor; import android.net.Uri; import android.os.Bundle; -import android.os.ParcelFileDescriptor; -import android.util.Base64; import androidx.test.InstrumentationRegistry; -import com.android.launcher3.tapl.TestHelpers; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; - /** * Content provider to receive commands from tests */ -public class TestCommandReceiver extends ContentProvider { +public class TestCommandReceiver { public static final String ENABLE_TEST_LAUNCHER = "enable-test-launcher"; public static final String DISABLE_TEST_LAUNCHER = "disable-test-launcher"; public static final String KILL_PROCESS = "kill-process"; public static final String GET_SYSTEM_HEALTH_MESSAGE = "get-system-health-message"; + public static final String SET_LIST_VIEW_SERVICE_BINDER = "set-list-view-service-binder"; - @Override - public boolean onCreate() { - return true; - } - - @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - public String getType(Uri uri) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - public Uri insert(Uri uri, ContentValues values) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, - String sortOrder) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - throw new UnsupportedOperationException("unimplemented mock method"); - } - - @Override - public Bundle call(String method, String arg, Bundle extras) { - switch (method) { - case ENABLE_TEST_LAUNCHER: { - getContext().getPackageManager().setComponentEnabledSetting( - new ComponentName(getContext(), TestLauncherActivity.class), - COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP); - return null; - } - case DISABLE_TEST_LAUNCHER: { - getContext().getPackageManager().setComponentEnabledSetting( - new ComponentName(getContext(), TestLauncherActivity.class), - COMPONENT_ENABLED_STATE_DISABLED, DONT_KILL_APP); - return null; - } - case KILL_PROCESS: { - ((ActivityManager) getContext().getSystemService(Activity.ACTIVITY_SERVICE)). - killBackgroundProcesses(arg); - return null; - } - - case GET_SYSTEM_HEALTH_MESSAGE: { - final Bundle response = new Bundle(); - response.putString("result", TestHelpers.getSystemHealthMessage(getContext())); - return response; - } - } - return super.call(method, arg, extras); - } + public static final String EXTRA_VALUE = "value"; public static Bundle callCommand(String command) { return callCommand(command, null); } public static Bundle callCommand(String command, String arg) { + return callCommand(command, arg, null); + } + + public static Bundle callCommand(String command, String arg, Bundle extras) { Instrumentation inst = InstrumentationRegistry.getInstrumentation(); Uri uri = Uri.parse("content://" + inst.getContext().getPackageName() + ".commands"); - return inst.getTargetContext().getContentResolver().call(uri, command, arg, null); - } - - @Override - public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { - String path = Base64.encodeToString(uri.getPath().getBytes(), - Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP); - File file = new File(getContext().getCacheDir(), path); - if (!file.exists()) { - // Create an empty file so that we can pass its descriptor - try { - file.createNewFile(); - } catch (IOException e) { - } - } - - return ParcelFileDescriptor.open(file, MODE_READ_WRITE); + return inst.getTargetContext().getContentResolver().call(uri, command, arg, extras); } } diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 1fac7081b7..54d81bb179 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -17,6 +17,7 @@ package com.android.launcher3.ui; import static androidx.test.InstrumentationRegistry.getInstrumentation; +import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID; import static com.android.launcher3.tapl.LauncherInstrumentation.ContainerType; import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; @@ -27,6 +28,7 @@ import static java.lang.System.exit; import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -43,6 +45,7 @@ import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.Until; +import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; @@ -56,6 +59,7 @@ import com.android.launcher3.tapl.LauncherInstrumentation; import com.android.launcher3.tapl.TestHelpers; import com.android.launcher3.testcomponent.TestCommandReceiver; import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.util.ContentWriter; import com.android.launcher3.util.LooperExecutor; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.Wait; @@ -230,6 +234,35 @@ public abstract class AbstractLauncherUiTest { }); } + /** + * Adds {@param item} on the homescreen on the 0th screen + */ + protected void addItemToScreen(ItemInfo item) { + ContentResolver resolver = mTargetContext.getContentResolver(); + int screenId = FIRST_SCREEN_ID; + // Update the screen id counter for the provider. + LauncherSettings.Settings.call(resolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID); + + if (screenId > FIRST_SCREEN_ID) { + screenId = FIRST_SCREEN_ID; + } + + // Insert the item + ContentWriter writer = new ContentWriter(mTargetContext); + item.id = LauncherSettings.Settings.call( + resolver, LauncherSettings.Settings.METHOD_NEW_ITEM_ID) + .getInt(LauncherSettings.Settings.EXTRA_VALUE); + item.screenId = screenId; + item.onAddToDatabase(writer); + writer.put(LauncherSettings.Favorites._ID, item.id); + resolver.insert(LauncherSettings.Favorites.CONTENT_URI, writer.getValues(mTargetContext)); + resetLoaderState(); + + // Launch the home activity + mDevice.pressHome(); + waitForModelLoaded(); + } + /** * Runs the callback on the UI thread and returns the result. */ diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java index e6348d9c02..ac87148633 100644 --- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java @@ -15,7 +15,9 @@ */ package com.android.launcher3.ui.widget; -import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID; +import static androidx.test.InstrumentationRegistry.getTargetContext; + +import static com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOptionsForWidget; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -25,6 +27,7 @@ import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.ContentResolver; +import android.content.Context; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionParams; import android.content.pm.PackageManager; @@ -43,11 +46,8 @@ import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.tapl.Workspace; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.TestViewHelpers; -import com.android.launcher3.util.ContentWriter; -import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.rule.ShellCommandRule; import com.android.launcher3.widget.PendingAddWidgetInfo; -import com.android.launcher3.widget.WidgetHostViewLoader; import org.junit.After; import org.junit.Before; @@ -57,7 +57,6 @@ import org.junit.runner.RunWith; import java.util.HashSet; import java.util.Set; -import java.util.function.Consumer; /** * Tests for bind widget flow. @@ -72,7 +71,6 @@ public class BindWidgetTest extends AbstractLauncherUiTest { public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind(); private ContentResolver mResolver; - private AppWidgetManagerCompat mWidgetManager; // Objects created during test, which should be cleaned up in the end. private Cursor mCursor; @@ -85,7 +83,6 @@ public class BindWidgetTest extends AbstractLauncherUiTest { super.setUp(); mResolver = mTargetContext.getContentResolver(); - mWidgetManager = AppWidgetManagerCompat.getInstance(mTargetContext); // Clear all existing data LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB); @@ -108,7 +105,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest { LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, true); LauncherAppWidgetInfo item = createWidgetInfo(info, true); - setupContents(item); + addItemToScreen(item); verifyWidgetPresent(info); } @@ -117,7 +114,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest { LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false); LauncherAppWidgetInfo item = createWidgetInfo(info, true); - setupContents(item); + addItemToScreen(item); verifyWidgetPresent(info); } @@ -127,7 +124,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest { LauncherAppWidgetInfo item = createWidgetInfo(info, false); item.appWidgetId = -33; - setupContents(item); + addItemToScreen(item); final Workspace workspace = mLauncher.getWorkspace(); // Item deleted from db @@ -148,7 +145,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest { LauncherAppWidgetInfo item = createWidgetInfo(info, false); item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID; - setupContents(item); + addItemToScreen(item); verifyWidgetPresent(info); } @@ -161,7 +158,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest { LauncherAppWidgetInfo item = createWidgetInfo(info, false); item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID; - setupContents(item); + addItemToScreen(item); verifyPendingWidgetPresent(); // Item deleted from db @@ -183,7 +180,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest { item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID | LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; - setupContents(item); + addItemToScreen(item); assertTrue("Pending widget exists", mLauncher.getWorkspace().tryGetPendingWidget(0) == null); @@ -202,7 +199,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest { | LauncherAppWidgetInfo.FLAG_RESTORE_STARTED | LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; - setupContents(item); + addItemToScreen(item); verifyPendingWidgetPresent(); // Verify item still exists in db @@ -230,7 +227,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest { PackageInstaller installer = mTargetContext.getPackageManager().getPackageInstaller(); mSessionId = installer.createSession(params); - setupContents(item); + addItemToScreen(item); verifyPendingWidgetPresent(); // Verify item still exists in db @@ -245,35 +242,6 @@ public class BindWidgetTest extends AbstractLauncherUiTest { & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID); } - /** - * Adds {@param item} on the homescreen on the 0th screen at 0,0, and verifies that the - * widget class is displayed on the homescreen. - */ - private void setupContents(LauncherAppWidgetInfo item) { - int screenId = FIRST_SCREEN_ID; - // Update the screen id counter for the provider. - LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID); - - if (screenId > FIRST_SCREEN_ID) { - screenId = FIRST_SCREEN_ID; - } - - // Insert the item - ContentWriter writer = new ContentWriter(mTargetContext); - item.id = LauncherSettings.Settings.call( - mResolver, LauncherSettings.Settings.METHOD_NEW_ITEM_ID) - .getInt(LauncherSettings.Settings.EXTRA_VALUE); - item.screenId = screenId; - item.onAddToDatabase(writer); - writer.put(LauncherSettings.Favorites._ID, item.id); - mResolver.insert(LauncherSettings.Favorites.CONTENT_URI, writer.getValues(mTargetContext)); - resetLoaderState(); - - // Launch the home activity - mDevice.pressHome(); - waitForModelLoaded(); - } - private void verifyWidgetPresent(LauncherAppWidgetProviderInfo info) { assertTrue("Widget is not present", mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null); @@ -289,8 +257,10 @@ public class BindWidgetTest extends AbstractLauncherUiTest { * @param bindWidget if true the info is bound and a valid widgetId is assigned to * the LauncherAppWidgetInfo */ - private LauncherAppWidgetInfo createWidgetInfo( + public static LauncherAppWidgetInfo createWidgetInfo( LauncherAppWidgetProviderInfo info, boolean bindWidget) { + Context targetContext = getTargetContext(); + LauncherAppWidgetInfo item = new LauncherAppWidgetInfo( LauncherAppWidgetInfo.NO_ID, info.provider); item.spanX = info.minSpanX; @@ -308,11 +278,12 @@ public class BindWidgetTest extends AbstractLauncherUiTest { pendingInfo.spanY = item.spanY; pendingInfo.minSpanX = item.minSpanX; pendingInfo.minSpanY = item.minSpanY; - Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(mTargetContext, pendingInfo); + Bundle options = getDefaultOptionsForWidget(targetContext, pendingInfo); - AppWidgetHost host = new LauncherAppWidgetHost(mTargetContext); + AppWidgetHost host = new LauncherAppWidgetHost(targetContext); int widgetId = host.allocateAppWidgetId(); - if (!mWidgetManager.bindAppWidgetIdIfAllowed(widgetId, info, options)) { + if (!AppWidgetManagerCompat.getInstance(targetContext) + .bindAppWidgetIdIfAllowed(widgetId, info, options)) { host.deleteAppWidgetId(widgetId); throw new IllegalArgumentException("Unable to bind widget id"); }