Adding test to check view inflation during swipe up
Bug: 137851409
Change-Id: Ic8e6f0b3c667051b921d1d4fad03c94122ee92e9
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 0000000..6726179
--- /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<ViewConfiguration> 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<ViewConfiguration>) 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 ("<init>".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 c6f55a7..0b74dc4 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -73,8 +73,12 @@
</intent-filter>
</activity>
+ <service
+ android:name="com.android.launcher3.testcomponent.ListViewService"
+ android:permission="android.permission.BIND_REMOTEVIEWS" />
+
<provider
- android:name="com.android.launcher3.testcomponent.TestCommandReceiver"
+ android:name="com.android.launcher3.testcomponent.TestCommandProvider"
android:authorities="${packageName}.commands"
android:exported="true"/>
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 0000000..0152040
--- /dev/null
+++ b/tests/res/layout/test_layout_widget_list.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#FFFFFF">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="#FF0000FF"
+ android:id="@android:id/text1"
+ android:padding="10dp" />
+
+ <ListView
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:id="@android:id/list" />
+</LinearLayout>
\ 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 0000000..3da20e0
--- /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 0000000..7682e07
--- /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 6a6916e..eb6c3ed 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) {
- Instrumentation inst = InstrumentationRegistry.getInstrumentation();
- Uri uri = Uri.parse("content://" + inst.getContext().getPackageName() + ".commands");
- return inst.getTargetContext().getContentResolver().call(uri, command, arg, null);
+ return callCommand(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);
+ 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, extras);
}
}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 1fac708..54d81bb 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -17,6 +17,7 @@
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 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.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.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;
@@ -231,6 +235,35 @@
}
/**
+ * 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.
*/
protected <T> T getOnUiThread(final Callable<T> callback) {
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index e6348d9..ac87148 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.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.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 java.util.HashSet;
import java.util.Set;
-import java.util.function.Consumer;
/**
* Tests for bind widget flow.
@@ -72,7 +71,6 @@
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 @@
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 @@
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, true);
LauncherAppWidgetInfo item = createWidgetInfo(info, true);
- setupContents(item);
+ addItemToScreen(item);
verifyWidgetPresent(info);
}
@@ -117,7 +114,7 @@
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
LauncherAppWidgetInfo item = createWidgetInfo(info, true);
- setupContents(item);
+ addItemToScreen(item);
verifyWidgetPresent(info);
}
@@ -127,7 +124,7 @@
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 @@
LauncherAppWidgetInfo item = createWidgetInfo(info, false);
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
- setupContents(item);
+ addItemToScreen(item);
verifyWidgetPresent(info);
}
@@ -161,7 +158,7 @@
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 @@
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 @@
| LauncherAppWidgetInfo.FLAG_RESTORE_STARTED
| LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
- setupContents(item);
+ addItemToScreen(item);
verifyPendingWidgetPresent();
// Verify item still exists in db
@@ -230,7 +227,7 @@
PackageInstaller installer = mTargetContext.getPackageManager().getPackageInstaller();
mSessionId = installer.createSession(params);
- setupContents(item);
+ addItemToScreen(item);
verifyPendingWidgetPresent();
// Verify item still exists in db
@@ -245,35 +242,6 @@
& 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 @@
* @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 @@
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");
}