1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 package com.android.launcher3.ui.widget; 17 18 import android.app.Activity; 19 import android.app.Application; 20 import android.app.PendingIntent; 21 import android.appwidget.AppWidgetManager; 22 import android.content.Intent; 23 import android.graphics.Color; 24 import android.support.test.uiautomator.By; 25 import android.support.test.uiautomator.UiObject2; 26 import android.support.test.uiautomator.Until; 27 import android.test.suitebuilder.annotation.LargeTest; 28 import android.view.View; 29 30 import com.android.launcher3.ItemInfo; 31 import com.android.launcher3.Launcher; 32 import com.android.launcher3.LauncherAppWidgetInfo; 33 import com.android.launcher3.LauncherSettings.Favorites; 34 import com.android.launcher3.MainThreadExecutor; 35 import com.android.launcher3.R; 36 import com.android.launcher3.ShortcutInfo; 37 import com.android.launcher3.Utilities; 38 import com.android.launcher3.Workspace.ItemOperator; 39 import com.android.launcher3.shortcuts.ShortcutKey; 40 import com.android.launcher3.testcomponent.AppWidgetNoConfig; 41 import com.android.launcher3.testcomponent.AppWidgetWithConfig; 42 import com.android.launcher3.testcomponent.RequestPinItemActivity; 43 import com.android.launcher3.ui.LauncherInstrumentationTestCase; 44 import com.android.launcher3.util.Condition; 45 import com.android.launcher3.util.SimpleActivityMonitor; 46 import com.android.launcher3.util.Wait; 47 import com.android.launcher3.widget.WidgetCell; 48 49 import java.util.UUID; 50 import java.util.concurrent.Callable; 51 52 /** 53 * Test to verify pin item request flow. 54 */ 55 @LargeTest 56 public class RequestPinItemTest extends LauncherInstrumentationTestCase { 57 58 private SimpleActivityMonitor mActivityMonitor; 59 private MainThreadExecutor mMainThreadExecutor; 60 61 private String mCallbackAction; 62 private String mShortcutId; 63 private int mAppWidgetId; 64 65 @Override setUp()66 protected void setUp() throws Exception { 67 super.setUp(); 68 grantWidgetPermission(); 69 setDefaultLauncher(); 70 71 mActivityMonitor = new SimpleActivityMonitor(); 72 ((Application) getInstrumentation().getTargetContext().getApplicationContext()) 73 .registerActivityLifecycleCallbacks(mActivityMonitor); 74 mMainThreadExecutor = new MainThreadExecutor(); 75 76 mCallbackAction = UUID.randomUUID().toString(); 77 mShortcutId = UUID.randomUUID().toString(); 78 } 79 80 @Override tearDown()81 protected void tearDown() throws Exception { 82 ((Application) getInstrumentation().getTargetContext().getApplicationContext()) 83 .unregisterActivityLifecycleCallbacks(mActivityMonitor); 84 super.tearDown(); 85 } 86 testPinWidgetNoConfig()87 public void testPinWidgetNoConfig() throws Throwable { 88 runTest("pinWidgetNoConfig", true, new ItemOperator() { 89 @Override 90 public boolean evaluate(ItemInfo info, View view) { 91 return info instanceof LauncherAppWidgetInfo && 92 ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId && 93 ((LauncherAppWidgetInfo) info).providerName.getClassName() 94 .equals(AppWidgetNoConfig.class.getName()); 95 } 96 }); 97 } 98 testPinWidgetNoConfig_customPreview()99 public void testPinWidgetNoConfig_customPreview() throws Throwable { 100 // Command to set custom preview 101 Intent command = RequestPinItemActivity.getCommandIntent( 102 RequestPinItemActivity.class, "setRemoteViewColor").putExtra( 103 RequestPinItemActivity.EXTRA_PARAM + "0", Color.RED); 104 105 runTest("pinWidgetNoConfig", true, new ItemOperator() { 106 @Override 107 public boolean evaluate(ItemInfo info, View view) { 108 return info instanceof LauncherAppWidgetInfo && 109 ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId && 110 ((LauncherAppWidgetInfo) info).providerName.getClassName() 111 .equals(AppWidgetNoConfig.class.getName()); 112 } 113 }, command); 114 } 115 testPinWidgetWithConfig()116 public void testPinWidgetWithConfig() throws Throwable { 117 runTest("pinWidgetWithConfig", true, new ItemOperator() { 118 @Override 119 public boolean evaluate(ItemInfo info, View view) { 120 return info instanceof LauncherAppWidgetInfo && 121 ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId && 122 ((LauncherAppWidgetInfo) info).providerName.getClassName() 123 .equals(AppWidgetWithConfig.class.getName()); 124 } 125 }); 126 } 127 testPinShortcut()128 public void testPinShortcut() throws Throwable { 129 // Command to set the shortcut id 130 Intent command = RequestPinItemActivity.getCommandIntent( 131 RequestPinItemActivity.class, "setShortcutId").putExtra( 132 RequestPinItemActivity.EXTRA_PARAM + "0", mShortcutId); 133 134 runTest("pinShortcut", false, new ItemOperator() { 135 @Override 136 public boolean evaluate(ItemInfo info, View view) { 137 return info instanceof ShortcutInfo && 138 info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT && 139 ShortcutKey.fromItemInfo(info).getId().equals(mShortcutId); 140 } 141 }, command); 142 } 143 runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher, Intent... commandIntents)144 private void runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher, 145 Intent... commandIntents) throws Throwable { 146 if (!Utilities.isAtLeastO()) { 147 return; 148 } 149 lockRotation(true); 150 151 clearHomescreen(); 152 startLauncher(); 153 154 // Open all apps and wait for load complete 155 final UiObject2 appsContainer = openAllApps(); 156 assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT)); 157 158 // Open Pin item activity 159 BlockingBroadcastReceiver openMonitor = new BlockingBroadcastReceiver( 160 RequestPinItemActivity.class.getName()); 161 scrollAndFind(appsContainer, By.text("Test Pin Item")).click(); 162 assertNotNull(openMonitor.blockingGetExtraIntent()); 163 164 // Set callback 165 PendingIntent callback = PendingIntent.getBroadcast(mTargetContext, 0, 166 new Intent(mCallbackAction), PendingIntent.FLAG_ONE_SHOT); 167 mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent( 168 RequestPinItemActivity.class, "setCallback").putExtra( 169 RequestPinItemActivity.EXTRA_PARAM + "0", callback)); 170 171 for (Intent command : commandIntents) { 172 mTargetContext.sendBroadcast(command); 173 } 174 175 // call the requested method to start the flow 176 mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent( 177 RequestPinItemActivity.class, activityMethod)); 178 UiObject2 widgetCell = mDevice.wait( 179 Until.findObject(By.clazz(WidgetCell.class)), DEFAULT_ACTIVITY_TIMEOUT); 180 assertNotNull(widgetCell); 181 182 // Accept confirmation: 183 BlockingBroadcastReceiver resultReceiver = new BlockingBroadcastReceiver(mCallbackAction); 184 mDevice.wait(Until.findObject(By.text(mTargetContext.getString( 185 R.string.place_automatically).toUpperCase())), DEFAULT_UI_TIMEOUT).click(); 186 Intent result = resultReceiver.blockingGetIntent(); 187 assertNotNull(result); 188 mAppWidgetId = result.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); 189 if (isWidget) { 190 assertNotSame(-1, mAppWidgetId); 191 } 192 193 // Go back to home 194 mTargetContext.startActivity(getHomeIntent()); 195 assertTrue(Wait.atMost(new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT)); 196 } 197 198 /** 199 * Condition for for an item 200 */ 201 private class ItemSearchCondition extends Condition implements Callable<Boolean> { 202 203 private final ItemOperator mOp; 204 ItemSearchCondition(ItemOperator op)205 ItemSearchCondition(ItemOperator op) { 206 mOp = op; 207 } 208 209 @Override isTrue()210 public boolean isTrue() throws Throwable { 211 return mMainThreadExecutor.submit(this).get(); 212 } 213 214 @Override call()215 public Boolean call() throws Exception { 216 // Find the resumed launcher 217 Launcher launcher = null; 218 for (Activity a : mActivityMonitor.resumed) { 219 if (a instanceof Launcher) { 220 launcher = (Launcher) a; 221 } 222 } 223 if (launcher == null) { 224 return false; 225 } 226 return launcher.getWorkspace().getFirstMatch(mOp) != null; 227 } 228 } 229 } 230