1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.launcher3.popup; 18 19 import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; 20 21 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 22 import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR; 23 import static com.android.launcher3.Flags.FLAG_ENABLE_DISMISS_PREDICTION_UNDO; 24 import static com.android.launcher3.Flags.FLAG_ENABLE_PRIVATE_SPACE; 25 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS; 26 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; 27 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; 28 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DISMISS_PREDICTION_UNDO; 29 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP; 30 import static com.android.launcher3.model.data.WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI; 31 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; 32 33 import static org.junit.Assert.assertFalse; 34 import static org.junit.Assert.assertNotNull; 35 import static org.junit.Assert.assertNull; 36 import static org.junit.Assert.assertTrue; 37 import static org.mockito.ArgumentMatchers.any; 38 import static org.mockito.ArgumentMatchers.eq; 39 import static org.mockito.Mockito.atLeast; 40 import static org.mockito.Mockito.doReturn; 41 import static org.mockito.Mockito.reset; 42 import static org.mockito.Mockito.times; 43 import static org.mockito.Mockito.verify; 44 import static org.mockito.Mockito.when; 45 46 import android.content.ComponentName; 47 import android.content.Intent; 48 import android.content.pm.ApplicationInfo; 49 import android.content.pm.LauncherActivityInfo; 50 import android.content.pm.LauncherApps; 51 import android.os.Process; 52 import android.os.UserHandle; 53 import android.platform.test.annotations.DisableFlags; 54 import android.platform.test.annotations.EnableFlags; 55 import android.platform.test.flag.junit.SetFlagsRule; 56 import android.view.View; 57 58 import androidx.test.annotation.UiThreadTest; 59 import androidx.test.filters.SmallTest; 60 import androidx.test.platform.app.InstrumentationRegistry; 61 62 import com.android.launcher3.AbstractFloatingView; 63 import com.android.launcher3.R; 64 import com.android.launcher3.allapps.PrivateProfileManager; 65 import com.android.launcher3.dagger.LauncherAppComponent; 66 import com.android.launcher3.dagger.LauncherAppSingleton; 67 import com.android.launcher3.logging.StatsLogManager; 68 import com.android.launcher3.logging.StatsLogManager.StatsLogger; 69 import com.android.launcher3.model.data.AppInfo; 70 import com.android.launcher3.model.data.ItemInfo; 71 import com.android.launcher3.model.data.WorkspaceItemInfo; 72 import com.android.launcher3.pm.UserCache; 73 import com.android.launcher3.util.AllModulesForTest; 74 import com.android.launcher3.util.ComponentKey; 75 import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext; 76 import com.android.launcher3.util.LauncherMultivalentJUnit; 77 import com.android.launcher3.util.TestSandboxModelContextWrapper; 78 import com.android.launcher3.util.TestUtil; 79 import com.android.launcher3.util.UserIconInfo; 80 import com.android.launcher3.views.Snackbar; 81 import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider; 82 import com.android.launcher3.widget.picker.model.data.WidgetPickerData; 83 84 import org.junit.After; 85 import org.junit.Assert; 86 import org.junit.Before; 87 import org.junit.Rule; 88 import org.junit.Test; 89 import org.junit.runner.RunWith; 90 import org.mockito.Answers; 91 import org.mockito.Mock; 92 import org.mockito.MockitoAnnotations; 93 94 import dagger.BindsInstance; 95 import dagger.Component; 96 97 @SmallTest 98 @RunWith(LauncherMultivalentJUnit.class) 99 public class SystemShortcutTest { 100 @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); 101 private static final UserHandle PRIVATE_HANDLE = new UserHandle(11); 102 private static final UserHandle MAIN_HANDLE = Process.myUserHandle(); 103 private View mView; 104 private ItemInfo mItemInfo; 105 private TestSandboxModelContextWrapper mTestContext; 106 private final SandboxModelContext mSandboxContext = new SandboxModelContext(); 107 private PrivateProfileManager mPrivateProfileManager; 108 private WidgetPickerDataProvider mWidgetPickerDataProvider; 109 private AppInfo mAppInfo; 110 111 @Mock UserCache mUserCache; 112 @Mock UserIconInfo mUserIconInfo; 113 @Mock LauncherActivityInfo mLauncherActivityInfo; 114 @Mock ApplicationInfo mApplicationInfo; 115 @Mock Intent mIntent; 116 @Mock StatsLogManager mStatsLogManager; 117 @Mock(answer = Answers.RETURNS_SELF) StatsLogger mStatsLogger; 118 119 @Before setUp()120 public void setUp() { 121 MockitoAnnotations.initMocks(this); 122 mSandboxContext.initDaggerComponent( 123 DaggerSystemShortcutTest_TestComponent.builder().bindUserCache(mUserCache) 124 ); 125 mTestContext = new TestSandboxModelContextWrapper(mSandboxContext) { 126 @Override 127 public StatsLogManager getStatsLogManager() { 128 return mStatsLogManager; 129 } 130 }; 131 spyOn(mSandboxContext); 132 doReturn(mStatsLogger).when(mStatsLogManager).logger(); 133 134 mView = new View(mTestContext); 135 mItemInfo = new ItemInfo(); 136 137 LauncherApps mLauncherApps = mSandboxContext.spyService(LauncherApps.class); 138 doReturn(mLauncherActivityInfo).when(mLauncherApps).resolveActivity(any(), any()); 139 when(mLauncherActivityInfo.getApplicationInfo()).thenReturn(mApplicationInfo); 140 141 when(mUserCache.getUserInfo(any())).thenReturn(mUserIconInfo); 142 mPrivateProfileManager = mTestContext.getAppsView().getPrivateProfileManager(); 143 spyOn(mPrivateProfileManager); 144 when(mPrivateProfileManager.getProfileUser()).thenReturn(PRIVATE_HANDLE); 145 146 mWidgetPickerDataProvider = mTestContext.getWidgetPickerDataProvider(); 147 spyOn(mWidgetPickerDataProvider); 148 } 149 150 @After tearDown()151 public void tearDown() { 152 mSandboxContext.onDestroy(); 153 } 154 155 @Test testWidgetsForNullComponentName()156 public void testWidgetsForNullComponentName() { 157 assertNull(mItemInfo.getTargetComponent()); 158 SystemShortcut systemShortcut = SystemShortcut.WIDGETS 159 .getShortcut(mTestContext, mItemInfo, mView); 160 assertNull(systemShortcut); 161 } 162 163 @Test testWidgetsForEmptyWidgetList()164 public void testWidgetsForEmptyWidgetList() { 165 mAppInfo = new AppInfo(); 166 mAppInfo.componentName = new ComponentName(mTestContext, getClass()); 167 assertNotNull(mAppInfo.getTargetComponent()); 168 doReturn(new WidgetPickerData()).when(mWidgetPickerDataProvider).get(); 169 spyOn(mAppInfo); 170 SystemShortcut systemShortcut = SystemShortcut.WIDGETS 171 .getShortcut(mTestContext, mAppInfo, mView); 172 verify(mAppInfo, times(2)).getTargetComponent(); 173 assertNull(systemShortcut); 174 } 175 176 @Test testAppInfoShortcut()177 public void testAppInfoShortcut() { 178 mAppInfo = new AppInfo(); 179 mAppInfo.componentName = new ComponentName(mTestContext, getClass()); 180 SystemShortcut systemShortcut = SystemShortcut.APP_INFO 181 .getShortcut(mTestContext, mAppInfo, mView); 182 assertNotNull(systemShortcut); 183 } 184 185 186 @Test testDontSuggestAppForNonPredictedItem()187 public void testDontSuggestAppForNonPredictedItem() { 188 assertFalse(mItemInfo.isPredictedItem()); 189 SystemShortcut systemShortcut = SystemShortcut.DONT_SUGGEST_APP 190 .getShortcut(mTestContext, mItemInfo, mView); 191 assertNull(systemShortcut); 192 } 193 194 @Test 195 @DisableFlags(FLAG_ENABLE_DISMISS_PREDICTION_UNDO) testDontSuggestAppForPredictedItem()196 public void testDontSuggestAppForPredictedItem() { 197 mAppInfo = new AppInfo(); 198 mAppInfo.componentName = new ComponentName(mTestContext, getClass()); 199 mAppInfo.container = CONTAINER_HOTSEAT_PREDICTION; 200 assertTrue(mAppInfo.isPredictedItem()); 201 SystemShortcut systemShortcut = SystemShortcut.DONT_SUGGEST_APP 202 .getShortcut(mTestContext, mAppInfo, mView); 203 assertNotNull(systemShortcut); 204 205 TestUtil.runOnExecutorSync(MAIN_EXECUTOR, () -> systemShortcut.onClick(mView)); 206 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 207 208 verify(mStatsLogger).log(eq(LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP)); 209 assertFalse(AbstractFloatingView.hasOpenView(mTestContext, TYPE_SNACKBAR)); 210 } 211 212 @Test 213 @EnableFlags(FLAG_ENABLE_DISMISS_PREDICTION_UNDO) testDontSuggestAppForPredictedItemWithUndo()214 public void testDontSuggestAppForPredictedItemWithUndo() { 215 mAppInfo = new AppInfo(); 216 mAppInfo.componentName = new ComponentName(mTestContext, getClass()); 217 mAppInfo.container = CONTAINER_HOTSEAT_PREDICTION; 218 assertTrue(mAppInfo.isPredictedItem()); 219 SystemShortcut systemShortcut = SystemShortcut.DONT_SUGGEST_APP 220 .getShortcut(mTestContext, mAppInfo, mView); 221 assertNotNull(systemShortcut); 222 223 TestUtil.runOnExecutorSync(MAIN_EXECUTOR, () -> systemShortcut.onClick(mView)); 224 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 225 verify(mStatsLogger).log(eq(LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP)); 226 227 // Undo bar shown 228 Snackbar snackbar = AbstractFloatingView.getOpenView(mTestContext, TYPE_SNACKBAR); 229 assertNotNull(snackbar); 230 reset(mStatsLogger); 231 TestUtil.runOnExecutorSync(MAIN_EXECUTOR, snackbar.findViewById( 232 R.id.action)::performClick); 233 verify(mStatsLogger).log(eq(LAUNCHER_DISMISS_PREDICTION_UNDO)); 234 } 235 236 @Test testPrivateProfileInstallwithTargetComponentNull()237 public void testPrivateProfileInstallwithTargetComponentNull() { 238 assertNull(mItemInfo.getTargetComponent()); 239 SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL 240 .getShortcut(mTestContext, mItemInfo, mView); 241 assertNull(systemShortcut); 242 } 243 244 @Test testPrivateProfileInstallNotAllAppsContainer()245 public void testPrivateProfileInstallNotAllAppsContainer() { 246 mAppInfo = new AppInfo(); 247 mAppInfo.componentName = new ComponentName(mTestContext, getClass()); 248 mAppInfo.container = CONTAINER_HOTSEAT_PREDICTION; 249 250 assertNotNull(mAppInfo.getTargetComponent()); 251 assertFalse(mAppInfo.getContainerInfo().hasAllAppsContainer()); 252 253 SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL 254 .getShortcut(mTestContext, mAppInfo, mView); 255 assertNull(systemShortcut); 256 } 257 258 @Test testPrivateProfileInstallNullPrivateProfileManager()259 public void testPrivateProfileInstallNullPrivateProfileManager() { 260 mAppInfo = new AppInfo(); 261 mAppInfo.componentName = new ComponentName(mTestContext, getClass()); 262 mAppInfo.container = CONTAINER_ALL_APPS; 263 mPrivateProfileManager = null; 264 265 assertNotNull(mAppInfo.getTargetComponent()); 266 assertTrue(mAppInfo.getContainerInfo().hasAllAppsContainer()); 267 assertNull(mPrivateProfileManager); 268 269 SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL 270 .getShortcut(mTestContext, mAppInfo, mView); 271 assertNull(systemShortcut); 272 } 273 274 @Test testPrivateProfileInstallPrivateProfileManagerDisabled()275 public void testPrivateProfileInstallPrivateProfileManagerDisabled() { 276 mAppInfo = new AppInfo(); 277 mAppInfo.componentName = new ComponentName(mTestContext, getClass()); 278 mAppInfo.container = CONTAINER_ALL_APPS; 279 280 assertNotNull(mPrivateProfileManager); 281 assertNotNull(mAppInfo.getTargetComponent()); 282 assertTrue(mAppInfo.getContainerInfo().hasAllAppsContainer()); 283 284 when(mPrivateProfileManager.isEnabled()).thenReturn(false); 285 SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL 286 .getShortcut(mTestContext, mAppInfo, mView); 287 assertNull(systemShortcut); 288 } 289 290 @Test testPrivateProfileInstallNullPrivateProfileUser()291 public void testPrivateProfileInstallNullPrivateProfileUser() { 292 mAppInfo = new AppInfo(); 293 mAppInfo.componentName = new ComponentName(mTestContext, getClass()); 294 mAppInfo.container = CONTAINER_ALL_APPS; 295 when(mPrivateProfileManager.getProfileUser()).thenReturn(null); 296 297 assertNotNull(mPrivateProfileManager); 298 assertNotNull(mAppInfo.getTargetComponent()); 299 assertTrue(mAppInfo.getContainerInfo().hasAllAppsContainer()); 300 assertNull(mPrivateProfileManager.getProfileUser()); 301 302 SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL 303 .getShortcut(mTestContext, mAppInfo, mView); 304 305 assertNull(systemShortcut); 306 } 307 308 @Test testPrivateProfileInstallNonNullPrivateProfileUser()309 public void testPrivateProfileInstallNonNullPrivateProfileUser() { 310 mAppInfo = new AppInfo(); 311 mAppInfo.componentName = new ComponentName(mTestContext, getClass()); 312 mAppInfo.container = CONTAINER_ALL_APPS; 313 when(mPrivateProfileManager.isEnabled()).thenReturn(true); 314 when(mPrivateProfileManager.getProfileUser()).thenReturn(PRIVATE_HANDLE); 315 316 assertNotNull(mAppInfo.getTargetComponent()); 317 assertTrue(mAppInfo.getContainerInfo().hasAllAppsContainer()); 318 assertNotNull(mPrivateProfileManager); 319 assertNotNull(mPrivateProfileManager.getProfileUser()); 320 assertNull(mTestContext.getAppsView().getAppsStore().getApp( 321 new ComponentKey(mAppInfo.getTargetComponent(), PRIVATE_HANDLE))); 322 323 SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL 324 .getShortcut(mTestContext, mAppInfo, mView); 325 326 verify(mPrivateProfileManager, atLeast(1)).isEnabled(); 327 assertNotNull(systemShortcut); 328 } 329 330 @Test testInstallGetShortcutWithNonWorkSpaceItemInfo()331 public void testInstallGetShortcutWithNonWorkSpaceItemInfo() { 332 SystemShortcut systemShortcut = SystemShortcut.INSTALL.getShortcut( 333 mTestContext, mItemInfo, mView); 334 Assert.assertNull(systemShortcut); 335 } 336 337 @Test 338 @UiThreadTest testInstallGetShortcutWithWorkSpaceItemInfo()339 public void testInstallGetShortcutWithWorkSpaceItemInfo() { 340 mAppInfo = new AppInfo(); 341 mAppInfo.componentName = new ComponentName(mTestContext, getClass()); 342 mAppInfo.intent = mIntent; 343 WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(mAppInfo); 344 workspaceItemInfo.status = FLAG_SUPPORTS_WEB_UI; 345 SystemShortcut systemShortcut = SystemShortcut.INSTALL.getShortcut( 346 mTestContext, workspaceItemInfo, mView); 347 Assert.assertNotNull(systemShortcut); 348 } 349 350 351 @Test 352 @DisableFlags(FLAG_ENABLE_PRIVATE_SPACE) testUninstallGetShortcutWithPrivateSpaceOff()353 public void testUninstallGetShortcutWithPrivateSpaceOff() { 354 SystemShortcut systemShortcut = SystemShortcut.UNINSTALL_APP.getShortcut( 355 mTestContext, null, mView); 356 Assert.assertNull(systemShortcut); 357 } 358 359 @Test 360 @EnableFlags(FLAG_ENABLE_PRIVATE_SPACE) testUninstallGetShortcutWithNonPrivateItemInfo()361 public void testUninstallGetShortcutWithNonPrivateItemInfo() { 362 mAppInfo = new AppInfo(); 363 mAppInfo.user = MAIN_HANDLE; 364 when(mUserIconInfo.isPrivate()).thenReturn(false); 365 366 SystemShortcut systemShortcut = SystemShortcut.UNINSTALL_APP.getShortcut( 367 mTestContext, mAppInfo, mView); 368 verify(mUserIconInfo).isPrivate(); 369 Assert.assertNull(systemShortcut); 370 } 371 372 @Test 373 @EnableFlags(FLAG_ENABLE_PRIVATE_SPACE) testUninstallGetShortcutWithSystemItemInfo()374 public void testUninstallGetShortcutWithSystemItemInfo() { 375 mAppInfo = new AppInfo(); 376 mAppInfo.user = PRIVATE_HANDLE; 377 mAppInfo.itemType = ITEM_TYPE_APPLICATION; 378 mAppInfo.intent = mIntent; 379 mAppInfo.componentName = new ComponentName(mTestContext, getClass()); 380 when(mLauncherActivityInfo.getComponentName()).thenReturn(mAppInfo.componentName); 381 when(mUserIconInfo.isPrivate()).thenReturn(true); 382 // System App 383 mApplicationInfo.flags = 1; 384 385 SystemShortcut systemShortcut = SystemShortcut.UNINSTALL_APP.getShortcut( 386 mTestContext, mAppInfo, mView); 387 verify(mLauncherActivityInfo, times(0)).getComponentName(); 388 Assert.assertNull(systemShortcut); 389 } 390 391 @Test 392 @EnableFlags(FLAG_ENABLE_PRIVATE_SPACE) testUninstallGetShortcutWithPrivateItemInfo()393 public void testUninstallGetShortcutWithPrivateItemInfo() { 394 mAppInfo = new AppInfo(); 395 mAppInfo.user = PRIVATE_HANDLE; 396 mAppInfo.itemType = ITEM_TYPE_APPLICATION; 397 mAppInfo.intent = mIntent; 398 mAppInfo.componentName = new ComponentName(mTestContext, getClass()); 399 when(mUserIconInfo.isPrivate()).thenReturn(true); 400 when(mLauncherActivityInfo.getComponentName()).thenReturn(mAppInfo.componentName); 401 // 3rd party app, not system app. 402 mApplicationInfo.flags = 0; 403 404 SystemShortcut systemShortcut = SystemShortcut.UNINSTALL_APP.getShortcut( 405 mTestContext, mAppInfo, mView); 406 407 verify(mLauncherActivityInfo).getComponentName(); 408 Assert.assertNotNull(systemShortcut); 409 410 systemShortcut.onClick(mView); 411 verify(mSandboxContext).startActivity(any()); 412 } 413 414 @LauncherAppSingleton 415 @Component(modules = { AllModulesForTest.class }) 416 interface TestComponent extends LauncherAppComponent { 417 @Component.Builder 418 interface Builder extends LauncherAppComponent.Builder { 419 @BindsInstance bindUserCache(UserCache userCache)420 SystemShortcutTest.TestComponent.Builder bindUserCache(UserCache userCache); build()421 @Override LauncherAppComponent build(); 422 } 423 } 424 } 425