Loading quickstep/robolectric_tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java→quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java +64 −64 Original line number Diff line number Diff line Loading @@ -15,24 +15,31 @@ */ package com.android.launcher3.model; import static android.os.Process.myUserHandle; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.robolectric.Shadows.shadowOf; import static org.mockito.Mockito.doReturn; import android.app.prediction.AppTarget; import android.app.prediction.AppTargetId; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Context; import android.os.Process; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.launcher3.LauncherAppState; import com.android.launcher3.config.FeatureFlags; Loading @@ -40,38 +47,34 @@ import com.android.launcher3.icons.ComponentWithLabel; import com.android.launcher3.icons.IconCache; import com.android.launcher3.model.BgDataModel.FixedContainerItems; import com.android.launcher3.model.QuickstepModelDelegate.PredictorState; import com.android.launcher3.shadows.ShadowDeviceFlag; import com.android.launcher3.util.LauncherModelHelper; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.shadow.api.Shadow; import org.robolectric.shadows.ShadowAppWidgetManager; import org.robolectric.shadows.ShadowPackageManager; import org.robolectric.util.ReflectionHelpers; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @RunWith(RobolectricTestRunner.class) @SmallTest @RunWith(AndroidJUnit4.class) public final class WidgetsPredicationUpdateTaskTest { private AppWidgetProviderInfo mApp1Provider1 = new AppWidgetProviderInfo(); private AppWidgetProviderInfo mApp1Provider2 = new AppWidgetProviderInfo(); private AppWidgetProviderInfo mApp2Provider1 = new AppWidgetProviderInfo(); private AppWidgetProviderInfo mApp4Provider1 = new AppWidgetProviderInfo(); private AppWidgetProviderInfo mApp4Provider2 = new AppWidgetProviderInfo(); private AppWidgetProviderInfo mApp5Provider1 = new AppWidgetProviderInfo(); private AppWidgetProviderInfo mApp1Provider1; private AppWidgetProviderInfo mApp1Provider2; private AppWidgetProviderInfo mApp2Provider1; private AppWidgetProviderInfo mApp4Provider1; private AppWidgetProviderInfo mApp4Provider2; private AppWidgetProviderInfo mApp5Provider1; private List<AppWidgetProviderInfo> allWidgets; private FakeBgDataModelCallback mCallback = new FakeBgDataModelCallback(); private Context mContext; private LauncherModelHelper mModelHelper; private UserHandle mUserHandle; Loading @@ -80,54 +83,56 @@ public final class WidgetsPredicationUpdateTaskTest { @Before public void setup() throws Exception { mModelHelper = new LauncherModelHelper(); MockitoAnnotations.initMocks(this); doAnswer(invocation -> { ComponentWithLabel componentWithLabel = invocation.getArgument(0); return componentWithLabel.getComponent().getShortClassName(); }).when(mIconCache).getTitleNoCache(any()); mContext = RuntimeEnvironment.application; mModelHelper = new LauncherModelHelper(); mUserHandle = Process.myUserHandle(); mUserHandle = myUserHandle(); mApp1Provider1 = createAppWidgetProviderInfo( ComponentName.createRelative("app1", "provider1")); mApp1Provider2 = createAppWidgetProviderInfo( ComponentName.createRelative("app1", "provider2")); mApp2Provider1 = createAppWidgetProviderInfo( ComponentName.createRelative("app2", "provider1")); mApp4Provider1 = createAppWidgetProviderInfo( ComponentName.createRelative("app4", "provider1")); mApp4Provider2 = createAppWidgetProviderInfo( ComponentName.createRelative("app4", ".provider2")); mApp5Provider1 = createAppWidgetProviderInfo( ComponentName.createRelative("app5", "provider1")); allWidgets = Arrays.asList(mApp1Provider1, mApp1Provider2, mApp2Provider1, mApp4Provider1, mApp4Provider2, mApp5Provider1); AppWidgetManager manager = mModelHelper.sandboxContext.spyService(AppWidgetManager.class); doReturn(allWidgets).when(manager).getInstalledProviders(); doReturn(allWidgets).when(manager).getInstalledProvidersForProfile(eq(myUserHandle())); doAnswer(i -> { String pkg = i.getArgument(0); Log.e("Hello", "Getting v " + pkg); return TextUtils.isEmpty(pkg) ? allWidgets : allWidgets.stream() .filter(a -> pkg.equals(a.provider.getPackageName())) .collect(Collectors.toList()); }).when(manager).getInstalledProvidersForPackage(any(), eq(myUserHandle())); // 2 widgets, app4/provider1 & app5/provider1, have already been added to the workspace. mModelHelper.initializeData("/widgets_predication_update_task_data.txt"); ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager()); mApp1Provider1.provider = ComponentName.createRelative("app1", "provider1"); ReflectionHelpers.setField(mApp1Provider1, "providerInfo", packageManager.addReceiverIfNotPresent(mApp1Provider1.provider)); mApp1Provider2.provider = ComponentName.createRelative("app1", "provider2"); ReflectionHelpers.setField(mApp1Provider2, "providerInfo", packageManager.addReceiverIfNotPresent(mApp1Provider2.provider)); mApp2Provider1.provider = ComponentName.createRelative("app2", "provider1"); ReflectionHelpers.setField(mApp2Provider1, "providerInfo", packageManager.addReceiverIfNotPresent(mApp2Provider1.provider)); mApp4Provider1.provider = ComponentName.createRelative("app4", "provider1"); ReflectionHelpers.setField(mApp4Provider1, "providerInfo", packageManager.addReceiverIfNotPresent(mApp4Provider1.provider)); mApp4Provider2.provider = ComponentName.createRelative("app4", ".provider2"); ReflectionHelpers.setField(mApp4Provider2, "providerInfo", packageManager.addReceiverIfNotPresent(mApp4Provider2.provider)); mApp5Provider1.provider = ComponentName.createRelative("app5", "provider1"); ReflectionHelpers.setField(mApp5Provider1, "providerInfo", packageManager.addReceiverIfNotPresent(mApp5Provider1.provider)); ShadowAppWidgetManager shadowAppWidgetManager = shadowOf(mContext.getSystemService(AppWidgetManager.class)); shadowAppWidgetManager.addInstalledProvider(mApp1Provider1); shadowAppWidgetManager.addInstalledProvider(mApp1Provider2); shadowAppWidgetManager.addInstalledProvider(mApp2Provider1); shadowAppWidgetManager.addInstalledProvider(mApp4Provider1); shadowAppWidgetManager.addInstalledProvider(mApp4Provider2); shadowAppWidgetManager.addInstalledProvider(mApp5Provider1); mModelHelper.getModel().addCallbacks(mCallback); mModelHelper.initializeData("widgets_predication_update_task_data"); MAIN_EXECUTOR.submit(() -> mModelHelper.getModel().addCallbacks(mCallback)).get(); MODEL_EXECUTOR.post(() -> mModelHelper.getBgDataModel().widgetsModel.update( LauncherAppState.getInstance(mContext), /* packageUser= */ null)); waitUntilIdle(); LauncherAppState.getInstance(mModelHelper.sandboxContext), /* packageUser= */ null)); MODEL_EXECUTOR.submit(() -> { }).get(); MAIN_EXECUTOR.submit(() -> { }).get(); } @After public void tearDown() { mModelHelper.destroy(); } @Test public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder() Loading Loading @@ -165,9 +170,9 @@ public final class WidgetsPredicationUpdateTaskTest { @Test public void widgetsRecommendationRan_localFilterDisabled_shouldReturnWidgetsInPredicationOrder() throws Exception { ShadowDeviceFlag shadowDeviceFlag = Shadow.extract( FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER); shadowDeviceFlag.setValue(false); if (FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER.get()) { return; } // WHEN newPredicationTask is executed with 5 predicated widgets. AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1", Loading Loading @@ -203,11 +208,6 @@ public final class WidgetsPredicationUpdateTaskTest { assertThat(actual.getUser()).isEqualTo(expected.getProfile()); } private void waitUntilIdle() { shadowOf(MODEL_EXECUTOR.getLooper()).idle(); shadowOf(MAIN_EXECUTOR.getLooper()).idle(); } private WidgetsPredictionUpdateTask newWidgetsPredicationTask(List<AppTarget> appTargets) { return new WidgetsPredictionUpdateTask( new PredictorState(CONTAINER_WIDGETS_PREDICTION, "test_widgets_prediction"), Loading robolectric_tests/src/com/android/launcher3/secondarydisplay/SDWorkModeTest.javadeleted 100644 → 0 +0 −125 Original line number Diff line number Diff line /* * Copyright (C) 2020 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.secondarydisplay; import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE; import static com.android.launcher3.util.LauncherUIHelper.doLayout; import static com.android.launcher3.util.Preconditions.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; import android.content.Context; import android.os.UserManager; import android.provider.Settings; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.allapps.AllAppsPagedView; import com.android.launcher3.allapps.AllAppsRecyclerView; import com.android.launcher3.util.LauncherLayoutBuilder; import com.android.launcher3.util.LauncherModelHelper; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.LooperMode; import org.robolectric.annotation.LooperMode.Mode; import org.robolectric.shadow.api.Shadow; import org.robolectric.shadows.ShadowUserManager; /** * Tests for {@link SecondaryDisplayLauncher} with work profile */ @RunWith(RobolectricTestRunner.class) @LooperMode(Mode.PAUSED) public class SDWorkModeTest { private static final int SYSTEM_USER = 0; private static final int FLAG_SYSTEM = 0x00000800; private static final int WORK_PROFILE_ID = 10; private static final int FLAG_PROFILE = 0x00001000; private Context mTargetContext; private InvariantDeviceProfile mIdp; private LauncherModelHelper mModelHelper; @Before public void setup() throws Exception { mModelHelper = new LauncherModelHelper(); mTargetContext = RuntimeEnvironment.application; mIdp = InvariantDeviceProfile.INSTANCE.get(mTargetContext); Settings.Global.putFloat(mTargetContext.getContentResolver(), Settings.Global.WINDOW_ANIMATION_SCALE, 0); mModelHelper.installApp(TEST_PACKAGE); } @Test public void testAllAppsList_noWorkProfile() throws Exception { SecondaryDisplayLauncher launcher = loadLauncher(); launcher.showAppDrawer(true); doLayout(launcher); verifyRecyclerViewCount(launcher.getAppsView().getActiveRecyclerView()); } @Test public void testAllAppsList_workProfile() throws Exception { ShadowUserManager sum = Shadow.extract(mTargetContext.getSystemService(UserManager.class)); sum.addUser(SYSTEM_USER, "me", FLAG_SYSTEM); sum.addProfile(SYSTEM_USER, WORK_PROFILE_ID, "work", FLAG_PROFILE); SecondaryDisplayLauncher launcher = loadLauncher(); launcher.showAppDrawer(true); doLayout(launcher); AllAppsRecyclerView rv1 = launcher.getAppsView().getActiveRecyclerView(); verifyRecyclerViewCount(rv1); assertNotNull(launcher.getAppsView().getWorkModeSwitch()); assertTrue(launcher.getAppsView().getRecyclerViewContainer() instanceof AllAppsPagedView); AllAppsPagedView pagedView = (AllAppsPagedView) launcher.getAppsView().getRecyclerViewContainer(); pagedView.snapToPageImmediately(1); doLayout(launcher); AllAppsRecyclerView rv2 = launcher.getAppsView().getActiveRecyclerView(); verifyRecyclerViewCount(rv2); assertNotSame(rv1, rv2); } private SecondaryDisplayLauncher loadLauncher() throws Exception { // Install 100 apps for (int i = 0; i < 100; i++) { mModelHelper.installApp(TEST_PACKAGE + i); } mModelHelper.setupDefaultLayoutProvider(new LauncherLayoutBuilder()).loadModelSync(); SecondaryDisplayLauncher launcher = Robolectric.buildActivity(SecondaryDisplayLauncher.class).setup().get(); doLayout(launcher); return launcher; } private void verifyRecyclerViewCount(AllAppsRecyclerView rv) { int childCount = rv.getChildCount(); assertTrue(childCount > 0); assertTrue(childCount < 100); } } robolectric_tests/src/com/android/launcher3/util/LauncherUIHelper.javadeleted 100644 → 0 +0 −101 Original line number Diff line number Diff line /* * Copyright (C) 2020 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.util; import static android.view.View.MeasureSpec.EXACTLY; import static android.view.View.MeasureSpec.makeMeasureSpec; import static com.android.launcher3.Utilities.createHomeIntent; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; import android.graphics.Point; import android.view.View; import android.view.WindowManager; import com.android.launcher3.Launcher; import org.robolectric.Robolectric; import org.robolectric.RuntimeEnvironment; import org.robolectric.android.controller.ActivityController; import org.robolectric.shadows.ShadowLooper; import org.robolectric.util.ReflectionHelpers; import java.util.List; /** * Utility class to help manage Launcher UI and related objects for test. */ public class LauncherUIHelper { /** * Returns the class name for the Launcher activity as defined in the manifest */ public static String getLauncherClassName() { Context context = RuntimeEnvironment.application; Intent homeIntent = createHomeIntent().setPackage(context.getPackageName()); List<ResolveInfo> launchers = context.getPackageManager() .queryIntentActivities(homeIntent, 0); if (launchers.size() != 1) { return null; } return launchers.get(0).activityInfo.name; } /** * Returns an activity controller for Launcher activity defined in the manifest */ public static <T extends Launcher> ActivityController<T> buildLauncher() { try { Class<T> tClass = (Class<T>) Class.forName(getLauncherClassName()); return Robolectric.buildActivity(tClass); } catch (Exception e) { throw new RuntimeException(e); } } /** * Creates and binds a Launcher activity defined in the manifest. * Note that the model must be bound before calling this */ public static <T extends Launcher> T buildAndBindLauncher() { ActivityController<T> controller = buildLauncher(); T launcher = controller.setup().get(); doLayout(launcher); ViewOnDrawExecutor executor = ReflectionHelpers.getField(launcher, "mPendingExecutor"); if (executor != null) { executor.markCompleted(); } return launcher; } /** * Performs a measure and layout pass for the given activity */ public static void doLayout(Activity activity) { Point size = new Point(); RuntimeEnvironment.application.getSystemService(WindowManager.class) .getDefaultDisplay().getSize(size); View view = activity.getWindow().getDecorView(); view.measure(makeMeasureSpec(size.x, EXACTLY), makeMeasureSpec(size.y, EXACTLY)); view.layout(0, 0, size.x, size.y); ShadowLooper.idleMainLooper(); } } src/com/android/launcher3/LauncherAppState.java +3 −2 Original line number Diff line number Diff line Loading @@ -50,7 +50,7 @@ import com.android.launcher3.util.SimpleBroadcastReceiver; import com.android.launcher3.util.Themes; import com.android.launcher3.widget.custom.CustomWidgetManager; public class LauncherAppState { public class LauncherAppState implements SafeCloseable { public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher"; private static final String KEY_ICON_STATE = "pref_icon_shape_path"; Loading Loading @@ -162,7 +162,8 @@ public class LauncherAppState { /** * Call from Application.onTerminate(), which is not guaranteed to ever be called. */ public void onTerminate() { @Override public void close() { mModel.destroy(); mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel); CustomWidgetManager.INSTANCE.get(mContext).setWidgetRefreshCallback(null); Loading src/com/android/launcher3/LauncherModel.java +6 −1 Original line number Diff line number Diff line Loading @@ -96,9 +96,10 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi // our monitoring of the package manager provides all updates and we never // need to do a requery. This is only ever touched from the loader thread. private boolean mModelLoaded; private boolean mModelDestroyed = false; public boolean isModelLoaded() { synchronized (mLock) { return mModelLoaded && mLoaderTask == null; return mModelLoaded && mLoaderTask == null && !mModelDestroyed; } } Loading Loading @@ -245,6 +246,7 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi * Called when the model is destroyed */ public void destroy() { mModelDestroyed = true; MODEL_EXECUTOR.execute(mModelDelegate::destroy); } Loading Loading @@ -557,6 +559,9 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi } public void enqueueModelUpdateTask(ModelUpdateTask task) { if (mModelDestroyed) { return; } task.init(mApp, this, mBgDataModel, mBgAllAppsList, MAIN_EXECUTOR); MODEL_EXECUTOR.execute(task); } Loading Loading
quickstep/robolectric_tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java→quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java +64 −64 Original line number Diff line number Diff line Loading @@ -15,24 +15,31 @@ */ package com.android.launcher3.model; import static android.os.Process.myUserHandle; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.robolectric.Shadows.shadowOf; import static org.mockito.Mockito.doReturn; import android.app.prediction.AppTarget; import android.app.prediction.AppTargetId; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Context; import android.os.Process; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.launcher3.LauncherAppState; import com.android.launcher3.config.FeatureFlags; Loading @@ -40,38 +47,34 @@ import com.android.launcher3.icons.ComponentWithLabel; import com.android.launcher3.icons.IconCache; import com.android.launcher3.model.BgDataModel.FixedContainerItems; import com.android.launcher3.model.QuickstepModelDelegate.PredictorState; import com.android.launcher3.shadows.ShadowDeviceFlag; import com.android.launcher3.util.LauncherModelHelper; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.shadow.api.Shadow; import org.robolectric.shadows.ShadowAppWidgetManager; import org.robolectric.shadows.ShadowPackageManager; import org.robolectric.util.ReflectionHelpers; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @RunWith(RobolectricTestRunner.class) @SmallTest @RunWith(AndroidJUnit4.class) public final class WidgetsPredicationUpdateTaskTest { private AppWidgetProviderInfo mApp1Provider1 = new AppWidgetProviderInfo(); private AppWidgetProviderInfo mApp1Provider2 = new AppWidgetProviderInfo(); private AppWidgetProviderInfo mApp2Provider1 = new AppWidgetProviderInfo(); private AppWidgetProviderInfo mApp4Provider1 = new AppWidgetProviderInfo(); private AppWidgetProviderInfo mApp4Provider2 = new AppWidgetProviderInfo(); private AppWidgetProviderInfo mApp5Provider1 = new AppWidgetProviderInfo(); private AppWidgetProviderInfo mApp1Provider1; private AppWidgetProviderInfo mApp1Provider2; private AppWidgetProviderInfo mApp2Provider1; private AppWidgetProviderInfo mApp4Provider1; private AppWidgetProviderInfo mApp4Provider2; private AppWidgetProviderInfo mApp5Provider1; private List<AppWidgetProviderInfo> allWidgets; private FakeBgDataModelCallback mCallback = new FakeBgDataModelCallback(); private Context mContext; private LauncherModelHelper mModelHelper; private UserHandle mUserHandle; Loading @@ -80,54 +83,56 @@ public final class WidgetsPredicationUpdateTaskTest { @Before public void setup() throws Exception { mModelHelper = new LauncherModelHelper(); MockitoAnnotations.initMocks(this); doAnswer(invocation -> { ComponentWithLabel componentWithLabel = invocation.getArgument(0); return componentWithLabel.getComponent().getShortClassName(); }).when(mIconCache).getTitleNoCache(any()); mContext = RuntimeEnvironment.application; mModelHelper = new LauncherModelHelper(); mUserHandle = Process.myUserHandle(); mUserHandle = myUserHandle(); mApp1Provider1 = createAppWidgetProviderInfo( ComponentName.createRelative("app1", "provider1")); mApp1Provider2 = createAppWidgetProviderInfo( ComponentName.createRelative("app1", "provider2")); mApp2Provider1 = createAppWidgetProviderInfo( ComponentName.createRelative("app2", "provider1")); mApp4Provider1 = createAppWidgetProviderInfo( ComponentName.createRelative("app4", "provider1")); mApp4Provider2 = createAppWidgetProviderInfo( ComponentName.createRelative("app4", ".provider2")); mApp5Provider1 = createAppWidgetProviderInfo( ComponentName.createRelative("app5", "provider1")); allWidgets = Arrays.asList(mApp1Provider1, mApp1Provider2, mApp2Provider1, mApp4Provider1, mApp4Provider2, mApp5Provider1); AppWidgetManager manager = mModelHelper.sandboxContext.spyService(AppWidgetManager.class); doReturn(allWidgets).when(manager).getInstalledProviders(); doReturn(allWidgets).when(manager).getInstalledProvidersForProfile(eq(myUserHandle())); doAnswer(i -> { String pkg = i.getArgument(0); Log.e("Hello", "Getting v " + pkg); return TextUtils.isEmpty(pkg) ? allWidgets : allWidgets.stream() .filter(a -> pkg.equals(a.provider.getPackageName())) .collect(Collectors.toList()); }).when(manager).getInstalledProvidersForPackage(any(), eq(myUserHandle())); // 2 widgets, app4/provider1 & app5/provider1, have already been added to the workspace. mModelHelper.initializeData("/widgets_predication_update_task_data.txt"); ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager()); mApp1Provider1.provider = ComponentName.createRelative("app1", "provider1"); ReflectionHelpers.setField(mApp1Provider1, "providerInfo", packageManager.addReceiverIfNotPresent(mApp1Provider1.provider)); mApp1Provider2.provider = ComponentName.createRelative("app1", "provider2"); ReflectionHelpers.setField(mApp1Provider2, "providerInfo", packageManager.addReceiverIfNotPresent(mApp1Provider2.provider)); mApp2Provider1.provider = ComponentName.createRelative("app2", "provider1"); ReflectionHelpers.setField(mApp2Provider1, "providerInfo", packageManager.addReceiverIfNotPresent(mApp2Provider1.provider)); mApp4Provider1.provider = ComponentName.createRelative("app4", "provider1"); ReflectionHelpers.setField(mApp4Provider1, "providerInfo", packageManager.addReceiverIfNotPresent(mApp4Provider1.provider)); mApp4Provider2.provider = ComponentName.createRelative("app4", ".provider2"); ReflectionHelpers.setField(mApp4Provider2, "providerInfo", packageManager.addReceiverIfNotPresent(mApp4Provider2.provider)); mApp5Provider1.provider = ComponentName.createRelative("app5", "provider1"); ReflectionHelpers.setField(mApp5Provider1, "providerInfo", packageManager.addReceiverIfNotPresent(mApp5Provider1.provider)); ShadowAppWidgetManager shadowAppWidgetManager = shadowOf(mContext.getSystemService(AppWidgetManager.class)); shadowAppWidgetManager.addInstalledProvider(mApp1Provider1); shadowAppWidgetManager.addInstalledProvider(mApp1Provider2); shadowAppWidgetManager.addInstalledProvider(mApp2Provider1); shadowAppWidgetManager.addInstalledProvider(mApp4Provider1); shadowAppWidgetManager.addInstalledProvider(mApp4Provider2); shadowAppWidgetManager.addInstalledProvider(mApp5Provider1); mModelHelper.getModel().addCallbacks(mCallback); mModelHelper.initializeData("widgets_predication_update_task_data"); MAIN_EXECUTOR.submit(() -> mModelHelper.getModel().addCallbacks(mCallback)).get(); MODEL_EXECUTOR.post(() -> mModelHelper.getBgDataModel().widgetsModel.update( LauncherAppState.getInstance(mContext), /* packageUser= */ null)); waitUntilIdle(); LauncherAppState.getInstance(mModelHelper.sandboxContext), /* packageUser= */ null)); MODEL_EXECUTOR.submit(() -> { }).get(); MAIN_EXECUTOR.submit(() -> { }).get(); } @After public void tearDown() { mModelHelper.destroy(); } @Test public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder() Loading Loading @@ -165,9 +170,9 @@ public final class WidgetsPredicationUpdateTaskTest { @Test public void widgetsRecommendationRan_localFilterDisabled_shouldReturnWidgetsInPredicationOrder() throws Exception { ShadowDeviceFlag shadowDeviceFlag = Shadow.extract( FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER); shadowDeviceFlag.setValue(false); if (FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER.get()) { return; } // WHEN newPredicationTask is executed with 5 predicated widgets. AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1", Loading Loading @@ -203,11 +208,6 @@ public final class WidgetsPredicationUpdateTaskTest { assertThat(actual.getUser()).isEqualTo(expected.getProfile()); } private void waitUntilIdle() { shadowOf(MODEL_EXECUTOR.getLooper()).idle(); shadowOf(MAIN_EXECUTOR.getLooper()).idle(); } private WidgetsPredictionUpdateTask newWidgetsPredicationTask(List<AppTarget> appTargets) { return new WidgetsPredictionUpdateTask( new PredictorState(CONTAINER_WIDGETS_PREDICTION, "test_widgets_prediction"), Loading
robolectric_tests/src/com/android/launcher3/secondarydisplay/SDWorkModeTest.javadeleted 100644 → 0 +0 −125 Original line number Diff line number Diff line /* * Copyright (C) 2020 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.secondarydisplay; import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE; import static com.android.launcher3.util.LauncherUIHelper.doLayout; import static com.android.launcher3.util.Preconditions.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; import android.content.Context; import android.os.UserManager; import android.provider.Settings; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.allapps.AllAppsPagedView; import com.android.launcher3.allapps.AllAppsRecyclerView; import com.android.launcher3.util.LauncherLayoutBuilder; import com.android.launcher3.util.LauncherModelHelper; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.LooperMode; import org.robolectric.annotation.LooperMode.Mode; import org.robolectric.shadow.api.Shadow; import org.robolectric.shadows.ShadowUserManager; /** * Tests for {@link SecondaryDisplayLauncher} with work profile */ @RunWith(RobolectricTestRunner.class) @LooperMode(Mode.PAUSED) public class SDWorkModeTest { private static final int SYSTEM_USER = 0; private static final int FLAG_SYSTEM = 0x00000800; private static final int WORK_PROFILE_ID = 10; private static final int FLAG_PROFILE = 0x00001000; private Context mTargetContext; private InvariantDeviceProfile mIdp; private LauncherModelHelper mModelHelper; @Before public void setup() throws Exception { mModelHelper = new LauncherModelHelper(); mTargetContext = RuntimeEnvironment.application; mIdp = InvariantDeviceProfile.INSTANCE.get(mTargetContext); Settings.Global.putFloat(mTargetContext.getContentResolver(), Settings.Global.WINDOW_ANIMATION_SCALE, 0); mModelHelper.installApp(TEST_PACKAGE); } @Test public void testAllAppsList_noWorkProfile() throws Exception { SecondaryDisplayLauncher launcher = loadLauncher(); launcher.showAppDrawer(true); doLayout(launcher); verifyRecyclerViewCount(launcher.getAppsView().getActiveRecyclerView()); } @Test public void testAllAppsList_workProfile() throws Exception { ShadowUserManager sum = Shadow.extract(mTargetContext.getSystemService(UserManager.class)); sum.addUser(SYSTEM_USER, "me", FLAG_SYSTEM); sum.addProfile(SYSTEM_USER, WORK_PROFILE_ID, "work", FLAG_PROFILE); SecondaryDisplayLauncher launcher = loadLauncher(); launcher.showAppDrawer(true); doLayout(launcher); AllAppsRecyclerView rv1 = launcher.getAppsView().getActiveRecyclerView(); verifyRecyclerViewCount(rv1); assertNotNull(launcher.getAppsView().getWorkModeSwitch()); assertTrue(launcher.getAppsView().getRecyclerViewContainer() instanceof AllAppsPagedView); AllAppsPagedView pagedView = (AllAppsPagedView) launcher.getAppsView().getRecyclerViewContainer(); pagedView.snapToPageImmediately(1); doLayout(launcher); AllAppsRecyclerView rv2 = launcher.getAppsView().getActiveRecyclerView(); verifyRecyclerViewCount(rv2); assertNotSame(rv1, rv2); } private SecondaryDisplayLauncher loadLauncher() throws Exception { // Install 100 apps for (int i = 0; i < 100; i++) { mModelHelper.installApp(TEST_PACKAGE + i); } mModelHelper.setupDefaultLayoutProvider(new LauncherLayoutBuilder()).loadModelSync(); SecondaryDisplayLauncher launcher = Robolectric.buildActivity(SecondaryDisplayLauncher.class).setup().get(); doLayout(launcher); return launcher; } private void verifyRecyclerViewCount(AllAppsRecyclerView rv) { int childCount = rv.getChildCount(); assertTrue(childCount > 0); assertTrue(childCount < 100); } }
robolectric_tests/src/com/android/launcher3/util/LauncherUIHelper.javadeleted 100644 → 0 +0 −101 Original line number Diff line number Diff line /* * Copyright (C) 2020 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.util; import static android.view.View.MeasureSpec.EXACTLY; import static android.view.View.MeasureSpec.makeMeasureSpec; import static com.android.launcher3.Utilities.createHomeIntent; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; import android.graphics.Point; import android.view.View; import android.view.WindowManager; import com.android.launcher3.Launcher; import org.robolectric.Robolectric; import org.robolectric.RuntimeEnvironment; import org.robolectric.android.controller.ActivityController; import org.robolectric.shadows.ShadowLooper; import org.robolectric.util.ReflectionHelpers; import java.util.List; /** * Utility class to help manage Launcher UI and related objects for test. */ public class LauncherUIHelper { /** * Returns the class name for the Launcher activity as defined in the manifest */ public static String getLauncherClassName() { Context context = RuntimeEnvironment.application; Intent homeIntent = createHomeIntent().setPackage(context.getPackageName()); List<ResolveInfo> launchers = context.getPackageManager() .queryIntentActivities(homeIntent, 0); if (launchers.size() != 1) { return null; } return launchers.get(0).activityInfo.name; } /** * Returns an activity controller for Launcher activity defined in the manifest */ public static <T extends Launcher> ActivityController<T> buildLauncher() { try { Class<T> tClass = (Class<T>) Class.forName(getLauncherClassName()); return Robolectric.buildActivity(tClass); } catch (Exception e) { throw new RuntimeException(e); } } /** * Creates and binds a Launcher activity defined in the manifest. * Note that the model must be bound before calling this */ public static <T extends Launcher> T buildAndBindLauncher() { ActivityController<T> controller = buildLauncher(); T launcher = controller.setup().get(); doLayout(launcher); ViewOnDrawExecutor executor = ReflectionHelpers.getField(launcher, "mPendingExecutor"); if (executor != null) { executor.markCompleted(); } return launcher; } /** * Performs a measure and layout pass for the given activity */ public static void doLayout(Activity activity) { Point size = new Point(); RuntimeEnvironment.application.getSystemService(WindowManager.class) .getDefaultDisplay().getSize(size); View view = activity.getWindow().getDecorView(); view.measure(makeMeasureSpec(size.x, EXACTLY), makeMeasureSpec(size.y, EXACTLY)); view.layout(0, 0, size.x, size.y); ShadowLooper.idleMainLooper(); } }
src/com/android/launcher3/LauncherAppState.java +3 −2 Original line number Diff line number Diff line Loading @@ -50,7 +50,7 @@ import com.android.launcher3.util.SimpleBroadcastReceiver; import com.android.launcher3.util.Themes; import com.android.launcher3.widget.custom.CustomWidgetManager; public class LauncherAppState { public class LauncherAppState implements SafeCloseable { public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher"; private static final String KEY_ICON_STATE = "pref_icon_shape_path"; Loading Loading @@ -162,7 +162,8 @@ public class LauncherAppState { /** * Call from Application.onTerminate(), which is not guaranteed to ever be called. */ public void onTerminate() { @Override public void close() { mModel.destroy(); mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel); CustomWidgetManager.INSTANCE.get(mContext).setWidgetRefreshCallback(null); Loading
src/com/android/launcher3/LauncherModel.java +6 −1 Original line number Diff line number Diff line Loading @@ -96,9 +96,10 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi // our monitoring of the package manager provides all updates and we never // need to do a requery. This is only ever touched from the loader thread. private boolean mModelLoaded; private boolean mModelDestroyed = false; public boolean isModelLoaded() { synchronized (mLock) { return mModelLoaded && mLoaderTask == null; return mModelLoaded && mLoaderTask == null && !mModelDestroyed; } } Loading Loading @@ -245,6 +246,7 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi * Called when the model is destroyed */ public void destroy() { mModelDestroyed = true; MODEL_EXECUTOR.execute(mModelDelegate::destroy); } Loading Loading @@ -557,6 +559,9 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi } public void enqueueModelUpdateTask(ModelUpdateTask task) { if (mModelDestroyed) { return; } task.init(mApp, this, mBgDataModel, mBgAllAppsList, MAIN_EXECUTOR); MODEL_EXECUTOR.execute(task); } Loading