From d9843357d0fa8e4bbe0d42007bfdfebe37db7451 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 25 Jan 2017 11:19:59 -0800 Subject: [PATCH] Adding some tests for request pin shortcut/widget flow Bug: 33584624 Change-Id: I49df36f60d2ae071b9d2c77c9c3300e010cd3bb9 --- tests/AndroidManifest-common.xml | 18 +- tests/res/drawable/test_drawable_pin_item.xml | 38 ++++ .../testcomponent/BaseTestingActivity.java | 126 +++++++++++++ .../testcomponent/RequestPinItemActivity.java | 89 +++++++++ .../testcomponent/WidgetConfigActivity.java | 38 +--- .../launcher3/ui/AllAppsIconToHomeTest.java | 1 + .../ui/LauncherInstrumentationTestCase.java | 77 +++++++- .../android/launcher3/ui/ShortcutsLaunchTest.java | 1 + .../android/launcher3/ui/ShortcutsToHomeTest.java | 1 + .../launcher3/ui/widget/AddConfigWidgetTest.java | 45 ++--- .../android/launcher3/ui/widget/AddWidgetTest.java | 1 + .../launcher3/ui/widget/RequestPinItemTest.java | 210 +++++++++++++++++++++ 12 files changed, 575 insertions(+), 70 deletions(-) create mode 100644 tests/res/drawable/test_drawable_pin_item.xml create mode 100644 tests/src/com/android/launcher3/testcomponent/BaseTestingActivity.java create mode 100644 tests/src/com/android/launcher3/testcomponent/RequestPinItemActivity.java create mode 100644 tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml index 24882aa89..0a2914772 100644 --- a/tests/AndroidManifest-common.xml +++ b/tests/AndroidManifest-common.xml @@ -23,7 +23,9 @@ - + @@ -31,7 +33,9 @@ android:resource="@xml/appwidget_no_config" /> - + @@ -45,5 +49,15 @@ + + + + + + + diff --git a/tests/res/drawable/test_drawable_pin_item.xml b/tests/res/drawable/test_drawable_pin_item.xml new file mode 100644 index 000000000..1d0725690 --- /dev/null +++ b/tests/res/drawable/test_drawable_pin_item.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/tests/src/com/android/launcher3/testcomponent/BaseTestingActivity.java b/tests/src/com/android/launcher3/testcomponent/BaseTestingActivity.java new file mode 100644 index 000000000..904590cb8 --- /dev/null +++ b/tests/src/com/android/launcher3/testcomponent/BaseTestingActivity.java @@ -0,0 +1,126 @@ +/* + * 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 + * + * 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.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.util.TypedValue; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.LinearLayout.LayoutParams; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * Base activity with utility methods to help automate testing. + */ +public class BaseTestingActivity extends Activity implements View.OnClickListener { + + public static final String SUFFIX_COMMAND = "-command"; + public static final String EXTRA_METHOD = "method"; + public static final String EXTRA_PARAM = "param_"; + + private static final int MARGIN_DP = 20; + + private final String mAction = this.getClass().getName(); + + private LinearLayout mView; + private int mMargin; + + private final BroadcastReceiver mCommandReceiver = new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + handleCommand(intent); + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mMargin = Math.round(TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, MARGIN_DP, getResources().getDisplayMetrics())); + mView = new LinearLayout(this); + mView.setPadding(mMargin, mMargin, mMargin, mMargin); + mView.setOrientation(LinearLayout.VERTICAL); + setContentView(mView); + + registerReceiver(mCommandReceiver, new IntentFilter(mAction + SUFFIX_COMMAND)); + } + + protected void addButton(String title, String method) { + Button button = new Button(this); + button.setText(title); + button.setTag(method); + button.setOnClickListener(this); + + LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + lp.bottomMargin = mMargin; + mView.addView(button, lp); + } + + @Override + protected void onResume() { + super.onResume(); + sendBroadcast(new Intent(mAction).putExtra(Intent.EXTRA_INTENT, getIntent())); + } + + @Override + protected void onDestroy() { + unregisterReceiver(mCommandReceiver); + super.onDestroy(); + } + + @Override + public void onClick(View view) { + handleCommand(new Intent().putExtra(EXTRA_METHOD, (String) view.getTag())); + } + + private void handleCommand(Intent cmd) { + String methodName = cmd.getStringExtra(EXTRA_METHOD); + try { + Method method = null; + for (Method m : this.getClass().getDeclaredMethods()) { + if (methodName.equals(m.getName()) && + !Modifier.isStatic(m.getModifiers()) && + Modifier.isPublic(m.getModifiers())) { + method = m; + break; + } + } + Object[] args = new Object[method.getParameterTypes().length]; + Bundle extras = cmd.getExtras(); + for (int i = 0; i < args.length; i++) { + args[i] = extras.get(EXTRA_PARAM + i); + } + method.invoke(this, args); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static Intent getCommandIntent(Class clazz, String method) { + return new Intent(clazz.getName() + SUFFIX_COMMAND) + .putExtra(EXTRA_METHOD, method); + } +} diff --git a/tests/src/com/android/launcher3/testcomponent/RequestPinItemActivity.java b/tests/src/com/android/launcher3/testcomponent/RequestPinItemActivity.java new file mode 100644 index 000000000..c2dd2259d --- /dev/null +++ b/tests/src/com/android/launcher3/testcomponent/RequestPinItemActivity.java @@ -0,0 +1,89 @@ +/* + * 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 + * + * 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.annotation.TargetApi; +import android.app.PendingIntent; +import android.appwidget.AppWidgetManager; +import android.content.ComponentName; +import android.content.IntentSender; +import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.drawable.Icon; +import android.os.Bundle; + +/** + * Sample activity to request pinning an item. + */ +@TargetApi(26) +public class RequestPinItemActivity extends BaseTestingActivity { + + private PendingIntent mCallback = null; + private String mShortcutId; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addButton("Pin Shortcut", "pinShortcut"); + addButton("Pin Widget without config ", "pinWidgetNoConfig"); + addButton("Pin Widget with config", "pinWidgetWithConfig"); + } + + public void setCallback(PendingIntent callback) { + mCallback = callback; + } + + public void setShortcutId(String id) { + mShortcutId = id; + } + + public void pinShortcut() { + ShortcutManager sm = getSystemService(ShortcutManager.class); + + // Generate icon + int r = sm.getIconMaxWidth() / 2; + Bitmap icon = Bitmap.createBitmap(r * 2, r * 2, Bitmap.Config.ARGB_8888); + Paint p = new Paint(); + p.setColor(Color.RED); + new Canvas(icon).drawCircle(r, r, r, p); + + ShortcutInfo info = new ShortcutInfo.Builder(this, mShortcutId) + .setIntent(getPackageManager().getLaunchIntentForPackage(getPackageName())) + .setIcon(Icon.createWithBitmap(icon)) + .setShortLabel("Test shortcut") + .build(); + + IntentSender callback = mCallback == null ? null : mCallback.getIntentSender(); + sm.requestPinShortcut(info, callback); + } + + public void pinWidgetNoConfig() { + requestWidget(new ComponentName(this, AppWidgetNoConfig.class)); + } + + public void pinWidgetWithConfig() { + requestWidget(new ComponentName(this, AppWidgetWithConfig.class)); + } + + private void requestWidget(ComponentName cn) { + AppWidgetManager.getInstance(this).requestPinAppWidget(cn, mCallback); + } +} diff --git a/tests/src/com/android/launcher3/testcomponent/WidgetConfigActivity.java b/tests/src/com/android/launcher3/testcomponent/WidgetConfigActivity.java index c0509bc24..d76ad0499 100644 --- a/tests/src/com/android/launcher3/testcomponent/WidgetConfigActivity.java +++ b/tests/src/com/android/launcher3/testcomponent/WidgetConfigActivity.java @@ -15,50 +15,30 @@ */ package com.android.launcher3.testcomponent; -import android.app.Activity; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.os.Bundle; /** * Simple activity for widget configuration */ -public class WidgetConfigActivity extends Activity { +public class WidgetConfigActivity extends BaseTestingActivity { public static final String SUFFIX_FINISH = "-finish"; public static final String EXTRA_CODE = "code"; - public static final String EXTRA_INTENT = "intent"; - - private final BroadcastReceiver mFinishReceiver = new BroadcastReceiver() { - - @Override - public void onReceive(Context context, Intent intent) { - WidgetConfigActivity.this.setResult( - intent.getIntExtra(EXTRA_CODE, RESULT_CANCELED), - (Intent) intent.getParcelableExtra(EXTRA_INTENT)); - WidgetConfigActivity.this.finish(); - } - }; - - private final String mAction = this.getClass().getName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - registerReceiver(mFinishReceiver, new IntentFilter(mAction + SUFFIX_FINISH)); + addButton("Cancel", "clickCancel"); + addButton("OK", "clickOK"); } - @Override - protected void onResume() { - super.onResume(); - sendBroadcast(new Intent(mAction).putExtra(Intent.EXTRA_INTENT, getIntent())); + public void clickCancel() { + setResult(RESULT_CANCELED); + finish(); } - @Override - protected void onDestroy() { - unregisterReceiver(mFinishReceiver); - super.onDestroy(); + public void clickOK() { + setResult(RESULT_OK); + finish(); } } diff --git a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java index 3f77bfdcd..936175087 100644 --- a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java +++ b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java @@ -22,6 +22,7 @@ public class AllAppsIconToHomeTest extends LauncherInstrumentationTestCase { @Override protected void setUp() throws Exception { super.setUp(); + setDefaultLauncher(); mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext) .getActivityList("com.android.settings", Process.myUserHandle()).get(0); diff --git a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java b/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java index 4bc40c68f..1ed4a240e 100644 --- a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java +++ b/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java @@ -15,10 +15,14 @@ */ package com.android.launcher3.ui; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.graphics.Point; import android.os.ParcelFileDescriptor; import android.os.Process; @@ -46,16 +50,24 @@ import com.android.launcher3.testcomponent.AppWidgetNoConfig; import com.android.launcher3.testcomponent.AppWidgetWithConfig; import com.android.launcher3.util.ManagedProfileHeuristic; +import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; import java.util.Locale; import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * Base class for all instrumentation tests providing various utility methods. */ public class LauncherInstrumentationTestCase extends InstrumentationTestCase { + public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10); + public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5; + public static final long DEFAULT_UI_TIMEOUT = 3000; public static final long DEFAULT_WORKER_TIMEOUT_SECS = 5; @@ -89,11 +101,14 @@ public class LauncherInstrumentationTestCase extends InstrumentationTestCase { * Starts the launcher activity in the target package and returns the Launcher instance. */ protected Launcher startLauncher() { - Intent homeIntent = new Intent(Intent.ACTION_MAIN) + return (Launcher) getInstrumentation().startActivitySync(getHomeIntent()); + } + + protected Intent getHomeIntent() { + return new Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_HOME) .setPackage(mTargetPackage) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - return (Launcher) getInstrumentation().startActivitySync(homeIntent); } /** @@ -104,16 +119,31 @@ public class LauncherInstrumentationTestCase extends InstrumentationTestCase { if (mTargetContext.getPackageManager().checkPermission( mTargetPackage, android.Manifest.permission.BIND_APPWIDGET) != PackageManager.PERMISSION_GRANTED) { - ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation().executeShellCommand( - "appwidget grantbind --package " + mTargetPackage); - // Read the input stream fully. - FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd); - while (fis.read() != -1); - fis.close(); + runShellCommand("appwidget grantbind --package " + mTargetPackage); } } /** + * Sets the target launcher as default launcher. + */ + protected void setDefaultLauncher() throws IOException { + ActivityInfo launcher = mTargetContext.getPackageManager() + .queryIntentActivities(getHomeIntent(), 0).get(0).activityInfo; + runShellCommand("cmd package set-home-activity " + + new ComponentName(launcher.packageName, launcher.name).flattenToString()); + } + + protected void runShellCommand(String command) throws IOException { + ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation() + .executeShellCommand(command); + + // Read the input stream fully. + FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd); + while (fis.read() != -1); + fis.close(); + } + + /** * Opens all apps and returns the recycler view */ protected UiObject2 openAllApps() { @@ -285,4 +315,35 @@ public class LauncherInstrumentationTestCase extends InstrumentationTestCase { String name = mTargetContext.getResources().getResourceEntryName(id); return By.res(mTargetPackage, name); } + + + /** + * 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)); + } + + @Override + public void onReceive(Context context, Intent intent) { + mIntent = intent; + latch.countDown(); + } + + public Intent blockingGetIntent() throws InterruptedException { + 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); + } + } } diff --git a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java index c6828f064..ee3a62803 100644 --- a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java +++ b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java @@ -25,6 +25,7 @@ public class ShortcutsLaunchTest extends LauncherInstrumentationTestCase { @Override protected void setUp() throws Exception { super.setUp(); + setDefaultLauncher(); mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext) .getActivityList("com.android.settings", Process.myUserHandle()).get(0); diff --git a/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java b/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java index d573eeaec..061e86530 100644 --- a/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java +++ b/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java @@ -25,6 +25,7 @@ public class ShortcutsToHomeTest extends LauncherInstrumentationTestCase { @Override protected void setUp() throws Exception { super.setUp(); + setDefaultLauncher(); mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext) .getActivityList("com.android.settings", Process.myUserHandle()).get(0); diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java index 7cbd29283..0b4e34f94 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java @@ -18,10 +18,7 @@ package com.android.launcher3.ui.widget; import android.app.Activity; import android.app.Application; import android.appwidget.AppWidgetManager; -import android.content.BroadcastReceiver; -import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.support.test.uiautomator.By; import android.support.test.uiautomator.UiObject2; import android.test.suitebuilder.annotation.LargeTest; @@ -41,8 +38,6 @@ import com.android.launcher3.util.Wait; import com.android.launcher3.widget.WidgetCell; import java.util.concurrent.Callable; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; /** * Test to verify widget configuration is properly shown. @@ -50,9 +45,6 @@ import java.util.concurrent.TimeUnit; @LargeTest public class AddConfigWidgetTest extends LauncherInstrumentationTestCase { - public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10); - public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5; - private LauncherAppWidgetProviderInfo mWidgetInfo; private SimpleActivityMonitor mActivityMonitor; private MainThreadExecutor mMainThreadExecutor; @@ -69,6 +61,8 @@ public class AddConfigWidgetTest extends LauncherInstrumentationTestCase { .registerActivityLifecycleCallbacks(mActivityMonitor); mMainThreadExecutor = new MainThreadExecutor(); mAppWidgetManager = AppWidgetManager.getInstance(mTargetContext); + + grantWidgetPermission(); } @Override @@ -126,12 +120,11 @@ public class AddConfigWidgetTest extends LauncherInstrumentationTestCase { // Verify that the widget id is valid and bound assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId)); + setResult(acceptConfig); if (acceptConfig) { - setResult(Activity.RESULT_OK); assertTrue(Wait.atMost(new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT)); assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId)); } else { - setResult(Activity.RESULT_CANCELED); // Verify that the widget id is deleted. assertTrue(Wait.atMost(new Condition() { @Override @@ -142,10 +135,11 @@ public class AddConfigWidgetTest extends LauncherInstrumentationTestCase { } } - private void setResult(int resultCode) { - String action = WidgetConfigActivity.class.getName() + WidgetConfigActivity.SUFFIX_FINISH; + private void setResult(boolean success) { + getInstrumentation().getTargetContext().sendBroadcast( - new Intent(action).putExtra(WidgetConfigActivity.EXTRA_CODE, resultCode)); + WidgetConfigActivity.getCommandIntent(WidgetConfigActivity.class, + success ? "clickOK" : "clickCancel")); } /** @@ -185,28 +179,17 @@ public class AddConfigWidgetTest extends LauncherInstrumentationTestCase { /** * Broadcast receiver for receiving widget config activity status. */ - private class WidgetConfigStartupMonitor extends BroadcastReceiver { - - private final CountDownLatch latch = new CountDownLatch(1); - private Intent mIntent; + private class WidgetConfigStartupMonitor extends BlockingBroadcastReceiver { - WidgetConfigStartupMonitor() { - getInstrumentation().getTargetContext().registerReceiver(this, - new IntentFilter(WidgetConfigActivity.class.getName())); - } - - @Override - public void onReceive(Context context, Intent intent) { - mIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT); - latch.countDown(); + public WidgetConfigStartupMonitor() { + super(WidgetConfigActivity.class.getName()); } public int getWidgetId() throws InterruptedException { - latch.await(DEFAULT_BROADCAST_TIMEOUT_SECS, TimeUnit.SECONDS); - getInstrumentation().getTargetContext().unregisterReceiver(this); - assertNotNull(mIntent); - assertEquals(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE, mIntent.getAction()); - int widgetId = mIntent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, + Intent intent = blockingGetExtraIntent(); + assertNotNull(intent); + assertEquals(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE, intent.getAction()); + int widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, LauncherAppWidgetInfo.NO_ID); assertNotSame(widgetId, LauncherAppWidgetInfo.NO_ID); return widgetId; diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java index b7e1ca903..3c92c578d 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java @@ -41,6 +41,7 @@ public class AddWidgetTest extends LauncherInstrumentationTestCase { @Override protected void setUp() throws Exception { super.setUp(); + grantWidgetPermission(); widgetInfo = findWidgetProvider(false /* hasConfigureScreen */); } diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java new file mode 100644 index 000000000..5ef5ec1c3 --- /dev/null +++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java @@ -0,0 +1,210 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.android.launcher3.ui.widget; + +import android.app.Activity; +import android.app.Application; +import android.app.PendingIntent; +import android.appwidget.AppWidgetManager; +import android.content.Intent; +import android.support.test.uiautomator.By; +import android.support.test.uiautomator.UiObject2; +import android.support.test.uiautomator.Until; +import android.test.suitebuilder.annotation.LargeTest; +import android.view.View; + +import com.android.launcher3.ItemInfo; +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppWidgetInfo; +import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.MainThreadExecutor; +import com.android.launcher3.R; +import com.android.launcher3.ShortcutInfo; +import com.android.launcher3.Utilities; +import com.android.launcher3.Workspace.ItemOperator; +import com.android.launcher3.shortcuts.ShortcutKey; +import com.android.launcher3.testcomponent.AppWidgetNoConfig; +import com.android.launcher3.testcomponent.AppWidgetWithConfig; +import com.android.launcher3.testcomponent.RequestPinItemActivity; +import com.android.launcher3.ui.LauncherInstrumentationTestCase; +import com.android.launcher3.util.Condition; +import com.android.launcher3.util.SimpleActivityMonitor; +import com.android.launcher3.util.Wait; +import com.android.launcher3.widget.WidgetCell; + +import java.util.UUID; +import java.util.concurrent.Callable; + +/** + * Test to verify pin item request flow. + */ +@LargeTest +public class RequestPinItemTest extends LauncherInstrumentationTestCase { + + private SimpleActivityMonitor mActivityMonitor; + private MainThreadExecutor mMainThreadExecutor; + + private String mCallbackAction; + private String mShortcutId; + private int mAppWidgetId; + + @Override + protected void setUp() throws Exception { + super.setUp(); + grantWidgetPermission(); + setDefaultLauncher(); + + mActivityMonitor = new SimpleActivityMonitor(); + ((Application) getInstrumentation().getTargetContext().getApplicationContext()) + .registerActivityLifecycleCallbacks(mActivityMonitor); + mMainThreadExecutor = new MainThreadExecutor(); + + mCallbackAction = UUID.randomUUID().toString(); + mShortcutId = UUID.randomUUID().toString(); + } + + @Override + protected void tearDown() throws Exception { + ((Application) getInstrumentation().getTargetContext().getApplicationContext()) + .unregisterActivityLifecycleCallbacks(mActivityMonitor); + super.tearDown(); + } + + public void testPinWidgetNoConfig() throws Throwable { + runTest("pinWidgetNoConfig", true, new ItemOperator() { + @Override + public boolean evaluate(ItemInfo info, View view) { + return info instanceof LauncherAppWidgetInfo && + ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId && + ((LauncherAppWidgetInfo) info).providerName.getClassName() + .equals(AppWidgetNoConfig.class.getName()); + } + }); + } + + public void testPinWidgetWithConfig() throws Throwable { + runTest("pinWidgetWithConfig", true, new ItemOperator() { + @Override + public boolean evaluate(ItemInfo info, View view) { + return info instanceof LauncherAppWidgetInfo && + ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId && + ((LauncherAppWidgetInfo) info).providerName.getClassName() + .equals(AppWidgetWithConfig.class.getName()); + } + }); + } + + + public void testPinWidgetShortcut() throws Throwable { + runTest("pinShortcut", false, new ItemOperator() { + @Override + public boolean evaluate(ItemInfo info, View view) { + return info instanceof ShortcutInfo && + info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT && + ShortcutKey.fromItemInfo(info).getId().equals(mShortcutId); + } + }); + } + + private void runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher) + throws Throwable { + if (!Utilities.isAtLeastO()) { + return; + } + lockRotation(true); + + clearHomescreen(); + startLauncher(); + + // Open all apps and wait for load complete + final UiObject2 appsContainer = openAllApps(); + assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT)); + + // Open Pin item activity + BlockingBroadcastReceiver openMonitor = new BlockingBroadcastReceiver( + RequestPinItemActivity.class.getName()); + scrollAndFind(appsContainer, By.text("Test Pin Item")).click(); + assertNotNull(openMonitor.blockingGetExtraIntent()); + + // Set callback + PendingIntent callback = PendingIntent.getBroadcast(mTargetContext, 0, + new Intent(mCallbackAction), PendingIntent.FLAG_ONE_SHOT); + mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent( + RequestPinItemActivity.class, "setCallback").putExtra( + RequestPinItemActivity.EXTRA_PARAM + "0", callback)); + + if (!isWidget) { + // Set shortcut id + mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent( + RequestPinItemActivity.class, "setShortcutId").putExtra( + RequestPinItemActivity.EXTRA_PARAM + "0", mShortcutId)); + } + + // call the requested method to start the flow + mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent( + RequestPinItemActivity.class, activityMethod)); + UiObject2 widgetCell = mDevice.wait( + Until.findObject(By.clazz(WidgetCell.class)), DEFAULT_ACTIVITY_TIMEOUT); + assertNotNull(widgetCell); + + // Accept confirmation: + BlockingBroadcastReceiver resultReceiver = new BlockingBroadcastReceiver(mCallbackAction); + mDevice.wait(Until.findObject(By.text(mTargetContext.getString( + R.string.place_automatically).toUpperCase())), DEFAULT_UI_TIMEOUT).click(); + Intent result = resultReceiver.blockingGetIntent(); + assertNotNull(result); + mAppWidgetId = result.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); + if (isWidget) { + assertNotSame(-1, mAppWidgetId); + } + + // Go back to home + mTargetContext.startActivity(getHomeIntent()); + assertTrue(Wait.atMost(new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT)); + } + + /** + * Condition for for an item + */ + private class ItemSearchCondition extends Condition implements Callable { + + private final ItemOperator mOp; + + ItemSearchCondition(ItemOperator op) { + mOp = op; + } + + @Override + public boolean isTrue() throws Throwable { + return mMainThreadExecutor.submit(this).get(); + } + + @Override + public Boolean call() throws Exception { + // Find the resumed launcher + Launcher launcher = null; + for (Activity a : mActivityMonitor.resumed) { + if (a instanceof Launcher) { + launcher = (Launcher) a; + } + } + if (launcher == null) { + return false; + } + return launcher.getWorkspace().getFirstMatch(mOp) != null; + } + } +} -- 2.11.0