Merge "Fixing widget sheet not scrolled using external mouse" into ub-launcher3-master
diff --git a/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java b/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
index f8ac010..7bc34cf 100644
--- a/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
@@ -30,11 +30,8 @@
import com.android.launcher3.FolderInfo;
import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.model.BgDataModel.Callbacks;
-import com.android.launcher3.util.Executors;
import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.util.LauncherRoboTestRunner;
@@ -46,8 +43,6 @@
import org.robolectric.annotation.LooperMode;
import org.robolectric.annotation.LooperMode.Mode;
-import java.util.ArrayList;
-
/**
* Tests for layout parser for remote layout
*/
@@ -120,18 +115,6 @@
}
private void writeLayoutAndLoad(LauncherLayoutBuilder builder) throws Exception {
- mModelHelper.setupDefaultLayoutProvider(builder);
-
- LoaderResults results = new LoaderResults(
- LauncherAppState.getInstance(mTargetContext),
- mModelHelper.getBgDataModel(),
- mModelHelper.getAllAppsList(),
- new Callbacks[0]);
- LoaderTask task = new LoaderTask(
- LauncherAppState.getInstance(mTargetContext),
- mModelHelper.getAllAppsList(),
- mModelHelper.getBgDataModel(),
- results);
- Executors.MODEL_EXECUTOR.submit(() -> task.loadWorkspace(new ArrayList<>())).get();
+ mModelHelper.setupDefaultLayoutProvider(builder).loadModelSync();
}
}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java
index f16ed33..76cb747 100644
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java
+++ b/robolectric_tests/src/com/android/launcher3/shadows/LShadowLauncherApps.java
@@ -127,6 +127,10 @@
@Override
protected List<LauncherActivityInfo> getShortcutConfigActivityList(String packageName,
UserHandle user) {
- return Collections.emptyList();
+ Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setPackage(packageName);
+ return RuntimeEnvironment.application.getPackageManager().queryIntentActivities(intent, 0)
+ .stream()
+ .map(ri -> getLauncherActivityInfo(ri.activityInfo))
+ .collect(Collectors.toList());
}
}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowTypeface.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowTypeface.java
new file mode 100644
index 0000000..0e7c1de
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/shadows/LShadowTypeface.java
@@ -0,0 +1,38 @@
+/*
+ * 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.shadows;
+
+import android.graphics.Typeface;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowTypeface;
+
+/**
+ * Extension of {@link ShadowTypeface} with missing shadow methods
+ */
+@Implements(Typeface.class)
+public class LShadowTypeface extends ShadowTypeface {
+
+ @Implementation
+ public static Typeface create(Typeface family, int weight, boolean italic) {
+ int style = italic ? Typeface.ITALIC : Typeface.NORMAL;
+ if (weight >= 400) {
+ style |= Typeface.BOLD;
+ }
+ return create(family, style);
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowWallpaperManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowWallpaperManager.java
new file mode 100644
index 0000000..d60251c
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/shadows/LShadowWallpaperManager.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.shadows;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.app.WallpaperManager;
+import android.content.Context;
+
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.shadows.ShadowUserManager;
+import org.robolectric.shadows.ShadowWallpaperManager;
+
+/**
+ * Extension of {@link ShadowUserManager} with missing shadow methods
+ */
+@Implements(WallpaperManager.class)
+public class LShadowWallpaperManager extends ShadowWallpaperManager {
+
+ @Implementation
+ protected static WallpaperManager getInstance(Context context) {
+ return context.getSystemService(WallpaperManager.class);
+ }
+
+ /**
+ * Remove this once the fix for
+ * https://github.com/robolectric/robolectric/issues/5285
+ * is available
+ */
+ public static void initializeMock() {
+ WallpaperManager wm = mock(WallpaperManager.class);
+ ShadowApplication shadowApplication = Shadows.shadowOf(RuntimeEnvironment.application);
+ shadowApplication.setSystemService(Context.WALLPAPER_SERVICE, wm);
+ doReturn(0).when(wm).getDesiredMinimumWidth();
+ doReturn(0).when(wm).getDesiredMinimumHeight();
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/ShadowOverrides.java b/robolectric_tests/src/com/android/launcher3/shadows/ShadowOverrides.java
new file mode 100644
index 0000000..131f691
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/shadows/ShadowOverrides.java
@@ -0,0 +1,61 @@
+/*
+ * 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.shadows;
+
+import android.content.Context;
+
+import com.android.launcher3.util.MainThreadInitializedObject.ObjectProvider;
+import com.android.launcher3.util.ResourceBasedOverride;
+import com.android.launcher3.util.ResourceBasedOverride.Overrides;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.util.ReflectionHelpers.ClassParameter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Shadow for {@link Overrides} to provide custom overrides for test
+ */
+@Implements(value = Overrides.class, isInAndroidSdk = false)
+public class ShadowOverrides {
+
+ private static Map<Class, ObjectProvider> sProviderMap = new HashMap<>();
+
+ @Implementation
+ public static <T extends ResourceBasedOverride> T getObject(
+ Class<T> clazz, Context context, int resId) {
+ ObjectProvider<T> provider = sProviderMap.get(clazz);
+ if (provider != null) {
+ return provider.get(context);
+ }
+ return Shadow.directlyOn(Overrides.class, "getObject",
+ ClassParameter.from(Class.class, clazz),
+ ClassParameter.from(Context.class, context),
+ ClassParameter.from(int.class, resId));
+ }
+
+ public static <T> void setProvider(Class<T> clazz, ObjectProvider<T> provider) {
+ sProviderMap.put(clazz, provider);
+ }
+
+ public static void clearProvider() {
+ sProviderMap.clear();
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
new file mode 100644
index 0000000..209bae0
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
@@ -0,0 +1,207 @@
+/*
+ * 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.ui;
+
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+
+import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.Mockito.mock;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.MotionEvent.PointerProperties;
+import android.view.View;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.FolderPagedView;
+import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.shadows.ShadowOverrides;
+import com.android.launcher3.util.LauncherLayoutBuilder;
+import com.android.launcher3.util.LauncherLayoutBuilder.FolderBuilder;
+import com.android.launcher3.util.LauncherModelHelper;
+import com.android.launcher3.util.LauncherRoboTestRunner;
+import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.launcher3.widget.WidgetsFullSheet;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.LooperMode;
+import org.robolectric.annotation.LooperMode.Mode;
+import org.robolectric.shadows.ShadowLooper;
+import org.robolectric.util.ReflectionHelpers;
+
+/**
+ * Tests scroll behavior at various Launcher UI components
+ */
+@RunWith(LauncherRoboTestRunner.class)
+@LooperMode(Mode.PAUSED)
+public class LauncherUIScrollTest {
+
+ private Context mTargetContext;
+ private InvariantDeviceProfile mIdp;
+ private LauncherModelHelper mModelHelper;
+
+ private LauncherLayoutBuilder mLayoutBuilder;
+
+ @Before
+ public void setup() throws Exception {
+ mModelHelper = new LauncherModelHelper();
+ mTargetContext = RuntimeEnvironment.application;
+ mIdp = InvariantDeviceProfile.INSTANCE.get(mTargetContext);
+ ShadowOverrides.setProvider(UserEventDispatcher.class,
+ c -> mock(UserEventDispatcher.class));
+
+ Settings.Global.putFloat(mTargetContext.getContentResolver(),
+ Settings.Global.WINDOW_ANIMATION_SCALE, 0);
+
+ mModelHelper.installApp(TEST_PACKAGE);
+ // LayoutBuilder with 3 workspace pages
+ mLayoutBuilder = new LauncherLayoutBuilder()
+ .atWorkspace(0, mIdp.numRows - 1, 0).putApp(TEST_PACKAGE, TEST_PACKAGE)
+ .atWorkspace(0, mIdp.numRows - 1, 1).putApp(TEST_PACKAGE, TEST_PACKAGE)
+ .atWorkspace(0, mIdp.numRows - 1, 2).putApp(TEST_PACKAGE, TEST_PACKAGE);
+ }
+
+ @Test
+ public void testWorkspacePagesBound() throws Exception {
+ // Verify that the workspace if bound synchronously
+ Launcher launcher = loadLauncher();
+ assertEquals(3, launcher.getWorkspace().getPageCount());
+ assertEquals(0, launcher.getWorkspace().getCurrentPage());
+
+ launcher.dispatchGenericMotionEvent(createScrollEvent(-1));
+ assertNotEquals("Workspace was not scrolled",
+ 0, launcher.getWorkspace().getNextPage());
+ }
+
+ @Test
+ public void testAllAppsScroll() throws Exception {
+ // Install 100 apps
+ for (int i = 0; i < 100; i++) {
+ mModelHelper.installApp(TEST_PACKAGE + i);
+ }
+
+ // Bind and open all-apps
+ Launcher launcher = loadLauncher();
+ launcher.getStateManager().goToState(LauncherState.ALL_APPS, false);
+ doLayout(launcher);
+
+ int currentScroll = launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
+ launcher.dispatchGenericMotionEvent(createScrollEvent(-1));
+ int newScroll = launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
+
+ assertNotEquals("All Apps was not scrolled", currentScroll, newScroll);
+ assertEquals("Workspace was scrolled", 0, launcher.getWorkspace().getNextPage());
+ }
+
+ @Test
+ public void testWidgetsListScroll() throws Exception {
+ // Install 100 widgets
+ for (int i = 0; i < 100; i++) {
+ mModelHelper.installCustomShortcut(TEST_PACKAGE + i, "shortcutProvider");
+ }
+
+ // Bind and open widgets
+ Launcher launcher = loadLauncher();
+ WidgetsFullSheet widgets = WidgetsFullSheet.show(launcher, false);
+ doLayout(launcher);
+
+ int currentScroll = widgets.getRecyclerView().getCurrentScrollY();
+ launcher.dispatchGenericMotionEvent(createScrollEvent(-1));
+ int newScroll = widgets.getRecyclerView().getCurrentScrollY();
+ assertNotEquals("Widgets was not scrolled", currentScroll, newScroll);
+ assertEquals("Workspace was scrolled", 0, launcher.getWorkspace().getNextPage());
+ }
+
+ @Test
+ public void testFolderPageScroll() throws Exception {
+ // Add a folder with multiple icons
+ FolderBuilder fb = mLayoutBuilder.atWorkspace(mIdp.numColumns / 2, mIdp.numRows / 2, 0)
+ .putFolder(0);
+ for (int i = 0; i < 100; i++) {
+ fb.addApp(TEST_PACKAGE, TEST_PACKAGE);
+ }
+
+ // Bind and open folder
+ Launcher launcher = loadLauncher();
+ doLayout(launcher);
+ launcher.getWorkspace().getFirstMatch((i, v) -> v instanceof FolderIcon).performClick();
+ ShadowLooper.idleMainLooper();
+ doLayout(launcher);
+ FolderPagedView folderPages = Folder.getOpen(launcher).getContent();
+
+ assertEquals(0, folderPages.getNextPage());
+ launcher.dispatchGenericMotionEvent(createScrollEvent(-1));
+ assertNotEquals("Folder page was not scrolled", 0, folderPages.getNextPage());
+ assertEquals("Workspace was scrolled", 0, launcher.getWorkspace().getNextPage());
+ }
+
+ private Launcher loadLauncher() throws Exception {
+ mModelHelper.setupDefaultLayoutProvider(mLayoutBuilder).loadModelSync();
+
+ Launcher launcher = Robolectric.buildActivity(Launcher.class).setup().get();
+ doLayout(launcher);
+ ViewOnDrawExecutor executor = ReflectionHelpers.getField(launcher, "mPendingExecutor");
+ if (executor != null) {
+ executor.runAllTasks();
+ }
+ return launcher;
+ }
+
+ private static void doLayout(Activity activity) {
+ DeviceProfile dp = InvariantDeviceProfile.INSTANCE
+ .get(RuntimeEnvironment.application).portraitProfile;
+ View view = activity.getWindow().getDecorView();
+ view.measure(makeMeasureSpec(dp.widthPx, EXACTLY), makeMeasureSpec(dp.heightPx, EXACTLY));
+ view.layout(0, 0, dp.widthPx, dp.heightPx);
+ ShadowLooper.idleMainLooper();
+ }
+
+ private static MotionEvent createScrollEvent(int scroll) {
+ DeviceProfile dp = InvariantDeviceProfile.INSTANCE
+ .get(RuntimeEnvironment.application).portraitProfile;
+
+ final PointerProperties[] pointerProperties = new PointerProperties[1];
+ pointerProperties[0] = new PointerProperties();
+ pointerProperties[0].id = 0;
+ final MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[1];
+ coords[0] = new MotionEvent.PointerCoords();
+ coords[0].setAxisValue(MotionEvent.AXIS_VSCROLL, scroll);
+ coords[0].x = dp.widthPx / 2;
+ coords[0].y = dp.heightPx / 2;
+
+ final long time = SystemClock.uptimeMillis();
+ return MotionEvent.obtain(time, time, MotionEvent.ACTION_SCROLL, 1,
+ pointerProperties, coords, 0, 0, 1.0f, 1.0f, 0, 0,
+ InputDevice.SOURCE_CLASS_POINTER, 0);
+ }
+
+}
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java b/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
index 20b1453..d593d84 100644
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.util;
+import static android.content.Intent.ACTION_CREATE_SHORTCUT;
+
import static com.android.launcher3.LauncherSettings.Favorites.CONTENT_URI;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -44,6 +46,7 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.pm.UserCache;
import org.mockito.ArgumentCaptor;
@@ -61,6 +64,7 @@
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.function.Function;
@@ -345,7 +349,8 @@
/**
* Sets up a dummy provider to load the provided layout by default, next time the layout loads
*/
- public void setupDefaultLayoutProvider(LauncherLayoutBuilder builder) throws Exception {
+ public LauncherModelHelper setupDefaultLayoutProvider(LauncherLayoutBuilder builder)
+ throws Exception {
Context context = RuntimeEnvironment.application;
InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(context);
idp.numRows = idp.numColumns = idp.numHotseatIcons = DEFAULT_GRID_SIZE;
@@ -363,23 +368,53 @@
Uri layoutUri = LauncherProvider.getLayoutUri(TEST_PROVIDER_AUTHORITY, context);
shadowOf(context.getContentResolver()).registerInputStream(layoutUri,
new ByteArrayInputStream(bos.toByteArray()));
+ return this;
}
/**
* Simulates an apk install with a default main activity with same class and package name
*/
public void installApp(String component) throws NameNotFoundException {
- ShadowPackageManager spm = shadowOf(RuntimeEnvironment.application.getPackageManager());
- ComponentName cn = new ComponentName(component, component);
- spm.addActivityIfNotPresent(cn);
-
IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN);
filter.addCategory(Intent.CATEGORY_LAUNCHER);
+ installApp(component, component, filter);
+ }
+
+ /**
+ * Simulates a custom shortcut install
+ */
+ public void installCustomShortcut(String pkg, String clazz) throws NameNotFoundException {
+ installApp(pkg, clazz, new IntentFilter(ACTION_CREATE_SHORTCUT));
+ }
+
+ private void installApp(String pkg, String clazz, IntentFilter filter)
+ throws NameNotFoundException {
+ ShadowPackageManager spm = shadowOf(RuntimeEnvironment.application.getPackageManager());
+ ComponentName cn = new ComponentName(pkg, clazz);
+ spm.addActivityIfNotPresent(cn);
+
filter.addCategory(Intent.CATEGORY_DEFAULT);
spm.addIntentFilterForActivity(cn, filter);
}
/**
+ * Loads the model in memory synchronously
+ */
+ public void loadModelSync() throws ExecutionException, InterruptedException {
+ // Since robolectric tests run on main thread, we run the loader-UI calls on a temp thread,
+ // so that we can wait appropriately for the loader to complete.
+ ReflectionHelpers.setField(getModel(), "mMainExecutor", Executors.UI_HELPER_EXECUTOR);
+
+ Callbacks mockCb = mock(Callbacks.class);
+ getModel().addCallbacksAndLoad(mockCb);
+
+ Executors.MODEL_EXECUTOR.submit(() -> { }).get();
+ Executors.UI_HELPER_EXECUTOR.submit(() -> { }).get();
+ ReflectionHelpers.setField(getModel(), "mMainExecutor", Executors.MAIN_EXECUTOR);
+ getModel().removeCallbacks(mockCb);
+ }
+
+ /**
* An extension of LauncherProvider backed up by in-memory database.
*/
public static class TestLauncherProvider extends LauncherProvider {
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherRoboTestRunner.java b/robolectric_tests/src/com/android/launcher3/util/LauncherRoboTestRunner.java
index 6277c66..744b478b 100644
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherRoboTestRunner.java
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherRoboTestRunner.java
@@ -21,10 +21,13 @@
import com.android.launcher3.shadows.LShadowBackupManager;
import com.android.launcher3.shadows.LShadowBitmap;
import com.android.launcher3.shadows.LShadowLauncherApps;
+import com.android.launcher3.shadows.LShadowTypeface;
import com.android.launcher3.shadows.LShadowUserManager;
+import com.android.launcher3.shadows.LShadowWallpaperManager;
import com.android.launcher3.shadows.ShadowDeviceFlag;
import com.android.launcher3.shadows.ShadowLooperExecutor;
import com.android.launcher3.shadows.ShadowMainThreadInitializedObject;
+import com.android.launcher3.shadows.ShadowOverrides;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import org.junit.runners.model.InitializationError;
@@ -49,9 +52,12 @@
LShadowLauncherApps.class,
LShadowBitmap.class,
LShadowBackupManager.class,
+ LShadowTypeface.class,
+ LShadowWallpaperManager.class,
ShadowLooperExecutor.class,
ShadowMainThreadInitializedObject.class,
ShadowDeviceFlag.class,
+ ShadowOverrides.class
};
public LauncherRoboTestRunner(Class<?> testClass) throws InitializationError {
@@ -78,6 +84,9 @@
// Disable plugins
PluginManagerWrapper.INSTANCE.initializeForTesting(mock(PluginManagerWrapper.class));
+
+ // Initialize mock wallpaper manager
+ LShadowWallpaperManager.initializeMock();
}
@Override
@@ -86,6 +95,7 @@
ShadowLog.stream = null;
ShadowMainThreadInitializedObject.resetInitializedObjects();
+ ShadowOverrides.clearProvider();
}
}
}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 7d7739e..5e47e2f 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -16,6 +16,14 @@
package com.android.launcher3;
+import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
+import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.touch.OverScroll.OVERSCROLL_DAMP_FACTOR;
+import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE;
+import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_BY;
+import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_TO;
+
import android.animation.LayoutTransition;
import android.animation.TimeInterpolator;
import android.annotation.SuppressLint;
@@ -42,14 +50,6 @@
import android.view.animation.Interpolator;
import android.widget.ScrollView;
-import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
-import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
-import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
-import static com.android.launcher3.touch.OverScroll.OVERSCROLL_DAMP_FACTOR;
-import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE;
-import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_BY;
-import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_TO;
-
import androidx.annotation.Nullable;
import com.android.launcher3.anim.Interpolators;
@@ -63,6 +63,7 @@
import com.android.launcher3.touch.PortraitPagedViewHandler;
import com.android.launcher3.util.OverScroller;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.ActivityContext;
import java.util.ArrayList;
@@ -1369,10 +1370,6 @@
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
switch (event.getAction()) {
case MotionEvent.ACTION_SCROLL: {
- Launcher launcher = Launcher.getLauncher(getContext());
- if (launcher != null) {
- AbstractFloatingView.closeAllOpenViews(launcher);
- }
// Handle mouse (or ext. device) by shifting the page depending on the scroll
final float vscroll;
final float hscroll;
@@ -1383,8 +1380,8 @@
vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
}
- if (Math.abs(vscroll) > Math.abs(hscroll) && !isVerticalScrollable()) {
- return true;
+ if (!canScroll(Math.abs(vscroll), Math.abs(hscroll))) {
+ return false;
}
if (hscroll != 0 || vscroll != 0) {
boolean isForwardScroll = mIsRtl ? (hscroll < 0 || vscroll < 0)
@@ -1402,8 +1399,13 @@
return super.onGenericMotionEvent(event);
}
- protected boolean isVerticalScrollable() {
- return true;
+ /**
+ * Returns true if the paged view can scroll for the provided vertical and horizontal
+ * scroll values
+ */
+ protected boolean canScroll(float absVScroll, float absHScroll) {
+ ActivityContext ac = ActivityContext.lookupContext(getContext());
+ return (ac == null || AbstractFloatingView.getTopOpenView(ac) == null);
}
private void acquireVelocityTrackerAndAddMovement(MotionEvent ev) {
diff --git a/src/com/android/launcher3/allapps/AllAppsPagedView.java b/src/com/android/launcher3/allapps/AllAppsPagedView.java
index ab4cb6b..f640c3e 100644
--- a/src/com/android/launcher3/allapps/AllAppsPagedView.java
+++ b/src/com/android/launcher3/allapps/AllAppsPagedView.java
@@ -83,7 +83,7 @@
}
@Override
- protected boolean isVerticalScrollable() {
- return false;
+ protected boolean canScroll(float absVScroll, float absHScroll) {
+ return (absHScroll > absVScroll) && super.canScroll(absVScroll, absHScroll);
}
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 365e76f..c241dc2 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -1648,6 +1648,10 @@
}
}
+ public FolderPagedView getContent() {
+ return mContent;
+ }
+
private void logEditFolderLabel() {
LauncherEvent launcherEvent = LauncherEvent.newBuilder()
.setAction(Action.newBuilder().setType(Action.Type.SOFT_KEYBOARD))
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index b83609e..3d72b49 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -115,6 +115,7 @@
*/
public AnimatorSet getAnimator() {
final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) mFolder.getLayoutParams();
+ mFolderIcon.getPreviewItemManager().recomputePreviewDrawingParams();
ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule();
final List<BubbleTextView> itemsInPreview = getPreviewIconsOnPage(0);
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index c6d62f8..dcd0e14 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -16,6 +16,9 @@
package com.android.launcher3.folder;
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.launcher3.AbstractFloatingView.TYPE_FOLDER;
+
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
@@ -27,6 +30,7 @@
import android.view.View;
import android.view.ViewDebug;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
@@ -258,7 +262,7 @@
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
- mPageIndicator.setScroll(l, mMaxScroll);
+ if (mMaxScroll > 0) mPageIndicator.setScroll(l, mMaxScroll);
}
/**
@@ -614,6 +618,12 @@
}
}
+ @Override
+ protected boolean canScroll(float absVScroll, float absHScroll) {
+ return AbstractFloatingView.getTopOpenViewWithType(mFolder.mLauncher,
+ TYPE_ALL & ~TYPE_FOLDER) == null;
+ }
+
public int itemsPerPage() {
return mOrganizer.getMaxItemsPerPage();
}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 62904ae..fc0997b 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -45,8 +45,6 @@
import android.util.MutableInt;
import android.util.TimingLogger;
-import androidx.annotation.VisibleForTesting;
-
import com.android.launcher3.AppInfo;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.InstallShortcutReceiver;
@@ -282,8 +280,7 @@
this.notify();
}
- @VisibleForTesting
- void loadWorkspace(List<ShortcutInfo> allDeepShortcuts) {
+ private void loadWorkspace(List<ShortcutInfo> allDeepShortcuts) {
loadWorkspace(allDeepShortcuts, LauncherSettings.Favorites.CONTENT_URI);
}
diff --git a/src/com/android/launcher3/widget/WidgetsFullSheet.java b/src/com/android/launcher3/widget/WidgetsFullSheet.java
index aaebedd..b3e9734 100644
--- a/src/com/android/launcher3/widget/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsFullSheet.java
@@ -98,6 +98,11 @@
onWidgetsBound();
}
+ @VisibleForTesting
+ public WidgetsRecyclerView getRecyclerView() {
+ return mRecyclerView;
+ }
+
@Override
protected Pair<View, String> getAccessibilityTarget() {
return Pair.create(mRecyclerView, getContext().getString(