From 82877211149bbe1cf1be7c3b2b0136707c5ef986 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 27 Mar 2020 16:58:18 -0700 Subject: [PATCH 01/17] Allow delegate input consumer to clear consumer state - When we have multiple wrapped input consumers, mConsumer will be set to the wrapper while the caller can actually be the input consumer being delegated to (ie. other activity ic). So we should clear the consumer state if the caller is anywhere in the IC hierarchy. This results in a separate issue where mGesture could be cleared during onConsumerAboutToBeSwitched() before being passed to the new consumer, so make a copy of the previous state just for passing to the new consumer. Bug: 152318829 Change-Id: I4afcef5b18aa772889e9104f3977887893f174ea --- .../android/quickstep/TouchInteractionService.java | 7 +++++-- .../inputconsumers/DelegateInputConsumer.java | 5 +++++ quickstep/src/com/android/quickstep/GestureState.java | 11 +++++++++++ .../src/com/android/quickstep/InputConsumer.java | 7 +++++++ 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index 496a3d858a..61fe6cb09a 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -444,9 +444,12 @@ public class TouchInteractionService extends Service implements PluginListener Date: Tue, 31 Mar 2020 12:12:14 -0700 Subject: [PATCH 02/17] Fixing widget sheet not scrolled using external mouse > Fixing crash when some properties are not initialized if draw is not called > Adding robolectic support for UI testing > Adding robo tests to verify workspace, all-apps, widgets and folder scrolling Bug: 147312357 Change-Id: Id7756e07f06396359e441cdff2e4f992bdcb97bb --- .../model/DefaultLayoutProviderTest.java | 19 +- .../shadows/LShadowLauncherApps.java | 6 +- .../launcher3/shadows/LShadowTypeface.java | 38 ++++ .../shadows/LShadowWallpaperManager.java | 56 +++++ .../launcher3/shadows/ShadowOverrides.java | 61 ++++++ .../launcher3/ui/LauncherUIScrollTest.java | 207 ++++++++++++++++++ .../launcher3/util/LauncherModelHelper.java | 45 +++- .../util/LauncherRoboTestRunner.java | 10 + src/com/android/launcher3/PagedView.java | 34 +-- .../launcher3/allapps/AllAppsPagedView.java | 4 +- src/com/android/launcher3/folder/Folder.java | 4 + .../folder/FolderAnimationManager.java | 1 + .../launcher3/folder/FolderPagedView.java | 12 +- .../android/launcher3/model/LoaderTask.java | 5 +- .../launcher3/widget/WidgetsFullSheet.java | 5 + 15 files changed, 460 insertions(+), 47 deletions(-) create mode 100644 robolectric_tests/src/com/android/launcher3/shadows/LShadowTypeface.java create mode 100644 robolectric_tests/src/com/android/launcher3/shadows/LShadowWallpaperManager.java create mode 100644 robolectric_tests/src/com/android/launcher3/shadows/ShadowOverrides.java create mode 100644 robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java diff --git a/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java b/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java index f8ac010ee1..7bc34cf91f 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 android.content.pm.PackageInstaller.SessionParams; 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.RuntimeEnvironment; 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 @@ public class DefaultLayoutProviderTest { } 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 f16ed33cfe..76cb74707f 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 @@ public class LShadowLauncherApps extends ShadowLauncherApps { @Override protected List 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 0000000000..0e7c1deba0 --- /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 0000000000..d60251c901 --- /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 0000000000..131f69136c --- /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 sProviderMap = new HashMap<>(); + + @Implementation + public static T getObject( + Class clazz, Context context, int resId) { + ObjectProvider 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 void setProvider(Class clazz, ObjectProvider 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 0000000000..209bae06a8 --- /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 20b1453b6f..d593d84778 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.LauncherProvider; 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.io.OutputStreamWriter; 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 @@ public class LauncherModelHelper { /** * 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,22 +368,52 @@ public class LauncherModelHelper { 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. */ diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherRoboTestRunner.java b/robolectric_tests/src/com/android/launcher3/util/LauncherRoboTestRunner.java index 6277c66d2c..744b478b2a 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.LShadowAppWidgetManager; 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 @@ public class LauncherRoboTestRunner extends RobolectricTestRunner { 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 @@ public class LauncherRoboTestRunner extends RobolectricTestRunner { // Disable plugins PluginManagerWrapper.INSTANCE.initializeForTesting(mock(PluginManagerWrapper.class)); + + // Initialize mock wallpaper manager + LShadowWallpaperManager.initializeMock(); } @Override @@ -86,6 +95,7 @@ public class LauncherRoboTestRunner extends RobolectricTestRunner { ShadowLog.stream = null; ShadowMainThreadInitializedObject.resetInitializedObjects(); + ShadowOverrides.clearProvider(); } } } diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 7d7739eb12..5e47e2ff73 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.accessibility.AccessibilityNodeInfo; 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.PagedOrientationHandler.ChildBounds; 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 @@ public abstract class PagedView extends ViewGrou 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 @@ public abstract class PagedView extends ViewGrou 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 @@ public abstract class PagedView extends ViewGrou 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 ab4cb6b8db..f640c3e8d0 100644 --- a/src/com/android/launcher3/allapps/AllAppsPagedView.java +++ b/src/com/android/launcher3/allapps/AllAppsPagedView.java @@ -83,7 +83,7 @@ public class AllAppsPagedView extends PagedView { } @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 365e76fa2b..c241dc2148 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -1648,6 +1648,10 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } } + 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 b83609e680..3d72b49b44 100644 --- a/src/com/android/launcher3/folder/FolderAnimationManager.java +++ b/src/com/android/launcher3/folder/FolderAnimationManager.java @@ -115,6 +115,7 @@ public class FolderAnimationManager { */ public AnimatorSet getAnimator() { final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) mFolder.getLayoutParams(); + mFolderIcon.getPreviewItemManager().recomputePreviewDrawingParams(); ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule(); final List itemsInPreview = getPreviewIconsOnPage(0); diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java index c6d62f8bc7..dcd0e144bc 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.Gravity; 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 @@ public class FolderPagedView extends PagedView { @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 @@ public class FolderPagedView extends PagedView { } } + @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 62904ae4a4..fc0997b198 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -45,8 +45,6 @@ import android.util.LongSparseArray; 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 @@ public class LoaderTask implements Runnable { this.notify(); } - @VisibleForTesting - void loadWorkspace(List allDeepShortcuts) { + private void loadWorkspace(List 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 b07a4f461d..d7a814a439 100644 --- a/src/com/android/launcher3/widget/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/WidgetsFullSheet.java @@ -87,6 +87,11 @@ public class WidgetsFullSheet extends BaseWidgetSheet onWidgetsBound(); } + @VisibleForTesting + public WidgetsRecyclerView getRecyclerView() { + return mRecyclerView; + } + @Override protected Pair getAccessibilityTarget() { return Pair.create(mRecyclerView, getContext().getString( From bb3f515690f8699f5c75241b988288ffa3e700ba Mon Sep 17 00:00:00 2001 From: vadimt Date: Tue, 31 Mar 2020 14:03:00 -0700 Subject: [PATCH 03/17] Catching StaleObjectException (and everything else) from get bounds This will improve diags because fail() adds useful info to the message Change-Id: I0a27f80c71a900078c94d9dd44d431fc206a37a0 --- .../com/android/launcher3/tapl/AllApps.java | 15 ++++++++------- .../android/launcher3/tapl/BaseOverview.java | 4 ++-- .../android/launcher3/tapl/Launchable.java | 2 +- .../tapl/LauncherInstrumentation.java | 19 ++++++++++++++----- .../launcher3/tapl/OptionsPopupMenuItem.java | 2 +- .../android/launcher3/tapl/OverviewTask.java | 2 +- .../com/android/launcher3/tapl/Widgets.java | 10 +++++----- .../com/android/launcher3/tapl/Workspace.java | 2 +- 8 files changed, 33 insertions(+), 23 deletions(-) diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java index 4a2d6995f4..808be6644b 100644 --- a/tests/tapl/com/android/launcher3/tapl/AllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java @@ -43,7 +43,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { AllApps(LauncherInstrumentation launcher) { super(launcher); final UiObject2 allAppsContainer = verifyActiveContainer(); - mHeight = allAppsContainer.getVisibleBounds().height(); + mHeight = mLauncher.getVisibleBounds(allAppsContainer).height(); final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer, "apps_list_view"); // Wait for the recycler to populate. @@ -66,7 +66,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { LauncherInstrumentation.log("hasClickableIcon: icon not visible"); return false; } - final Rect iconBounds = icon.getVisibleBounds(); + final Rect iconBounds = mLauncher.getVisibleBounds(icon); LauncherInstrumentation.log("hasClickableIcon: icon bounds: " + iconBounds); if (iconBounds.height() < mIconHeight / 2) { LauncherInstrumentation.log("hasClickableIcon: icon has insufficient height"); @@ -86,7 +86,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { private boolean iconCenterInSearchBox(UiObject2 allAppsContainer, UiObject2 icon) { final Point iconCenter = icon.getVisibleCenter(); - return getSearchBox(allAppsContainer).getVisibleBounds().contains( + return mLauncher.getVisibleBounds(getSearchBox(allAppsContainer)).contains( iconCenter.x, iconCenter.y); } @@ -125,11 +125,11 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { mLauncher.getObjectsInContainer(allAppsContainer, "icon") .stream() .filter(icon -> - icon.getVisibleBounds().bottom + mLauncher.getVisibleBounds(icon).bottom <= displayBottom) .collect(Collectors.toList()), - searchBox.getVisibleBounds().bottom - - allAppsContainer.getVisibleBounds().top); + mLauncher.getVisibleBounds(searchBox).bottom + - mLauncher.getVisibleBounds(allAppsContainer).top); final int newScroll = getAllAppsScroll(); mLauncher.assertTrue( "Scrolled in a wrong direction in AllApps: from " + scroll + " to " @@ -166,7 +166,8 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { final UiObject2 searchBox = getSearchBox(allAppsContainer); int attempts = 0; - final Rect margins = new Rect(0, searchBox.getVisibleBounds().bottom + 1, 0, 5); + final Rect margins = + new Rect(0, mLauncher.getVisibleBounds(searchBox).bottom + 1, 0, 5); for (int scroll = getAllAppsScroll(); scroll != 0; diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java index a769acfc35..69afcc4e44 100644 --- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java +++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java @@ -117,8 +117,8 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { // part) one in the center, and parts of its right and left siblings. Find the // main task view by its width. final UiObject2 widestTask = Collections.max(taskViews, - (t1, t2) -> Integer.compare(t1.getVisibleBounds().width(), - t2.getVisibleBounds().width())); + (t1, t2) -> Integer.compare(mLauncher.getVisibleBounds(t1).width(), + mLauncher.getVisibleBounds(t2).width())); return new OverviewTask(mLauncher, widestTask, this); } diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java index d1a1254eed..6c664e0372 100644 --- a/tests/tapl/com/android/launcher3/tapl/Launchable.java +++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java @@ -57,7 +57,7 @@ abstract class Launchable { private Background launch(BySelector selector) { LauncherInstrumentation.log("Launchable.launch before click " + - mObject.getVisibleCenter() + " in " + mObject.getVisibleBounds()); + mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject)); mLauncher.executeAndWaitForEvent( () -> mLauncher.clickLauncherObject(mObject), diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index d171a695a7..5cc2cea6ba 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -971,7 +971,7 @@ public final class LauncherInstrumentation { int getBottomGestureMarginInContainer(UiObject2 container) { final int bottomGestureStartOnScreen = getRealDisplaySize().y - getBottomGestureSize(); - return container.getVisibleBounds().bottom - bottomGestureStartOnScreen; + return getVisibleBounds(container).bottom - bottomGestureStartOnScreen; } void clickLauncherObject(UiObject2 object) { @@ -989,10 +989,10 @@ public final class LauncherInstrumentation { Collection items, int topPaddingInContainer) { final UiObject2 lowestItem = Collections.max(items, (i1, i2) -> - Integer.compare(i1.getVisibleBounds().top, i2.getVisibleBounds().top)); + Integer.compare(getVisibleBounds(i1).top, getVisibleBounds(i2).top)); - final int itemRowCurrentTopOnScreen = lowestItem.getVisibleBounds().top; - final Rect containerRect = container.getVisibleBounds(); + final int itemRowCurrentTopOnScreen = getVisibleBounds(lowestItem).top; + final Rect containerRect = getVisibleBounds(container); final int itemRowNewTopOnScreen = containerRect.top + topPaddingInContainer; final int distance = itemRowCurrentTopOnScreen - itemRowNewTopOnScreen + getTouchSlop(); @@ -1011,7 +1011,7 @@ public final class LauncherInstrumentation { void scroll( UiObject2 container, Direction direction, Rect margins, int steps, boolean slowDown) { - final Rect rect = container.getVisibleBounds(); + final Rect rect = getVisibleBounds(container); if (margins != null) { rect.left += margins.left; rect.top += margins.top; @@ -1322,4 +1322,13 @@ public final class LauncherInstrumentation { void expectEvent(String sequence, Pattern expected) { if (sCheckingEvents) sEventChecker.expectPattern(sequence, expected); } + + Rect getVisibleBounds(UiObject2 object) { + try { + return object.getVisibleBounds(); + } catch (Throwable t) { + fail(t.toString()); + return null; + } + } } \ No newline at end of file diff --git a/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenuItem.java b/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenuItem.java index b8e6c0edb3..63a97f4ec4 100644 --- a/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenuItem.java +++ b/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenuItem.java @@ -41,7 +41,7 @@ public class OptionsPopupMenuItem { public void launch(@NonNull String expectedPackageName) { try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) { LauncherInstrumentation.log("OptionsPopupMenuItem before click " - + mObject.getVisibleCenter() + " in " + mObject.getVisibleBounds()); + + mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject)); mLauncher.clickLauncherObject(mObject); if (!Build.MODEL.contains("Cuttlefish") || Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q && diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java index 5c51782826..fae5f19126 100644 --- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java +++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java @@ -56,7 +56,7 @@ public final class OverviewTask { "want to dismiss a task")) { verifyActiveContainer(); // Dismiss the task via flinging it up. - final Rect taskBounds = mTask.getVisibleBounds(); + final Rect taskBounds = mLauncher.getVisibleBounds(mTask); final int centerX = taskBounds.centerX(); final int centerY = taskBounds.centerY(); mLauncher.linearGesture(centerX, centerY, centerX, 0, 10, false, diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java index a14d2f0501..5be57c6c0b 100644 --- a/tests/tapl/com/android/launcher3/tapl/Widgets.java +++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java @@ -73,7 +73,7 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer { mLauncher.scroll( widgetsContainer, Direction.UP, - new Rect(0, 0, widgetsContainer.getVisibleBounds().width(), 0), + new Rect(0, 0, mLauncher.getVisibleBounds(widgetsContainer).width(), 0), FLING_STEPS, false); try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) { verifyActiveContainer(); @@ -111,19 +111,19 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer { int maxWidth = 0; for (UiObject2 sibling : widget.getParent().getChildren()) { - maxWidth = Math.max(sibling.getVisibleBounds().width(), maxWidth); + maxWidth = Math.max(mLauncher.getVisibleBounds(sibling).width(), maxWidth); } - int visibleDelta = maxWidth - widget.getVisibleBounds().width(); + int visibleDelta = maxWidth - mLauncher.getVisibleBounds(widget).width(); if (visibleDelta > 0) { - Rect parentBounds = cell.getVisibleBounds(); + Rect parentBounds = mLauncher.getVisibleBounds(cell); mLauncher.linearGesture(parentBounds.centerX() + visibleDelta + mLauncher.getTouchSlop(), parentBounds.centerY(), parentBounds.centerX(), parentBounds.centerY(), 10, true, GestureScope.INSIDE); } - if (widget.getVisibleBounds().bottom + if (mLauncher.getVisibleBounds(widget).bottom <= displaySize.y - mLauncher.getBottomGestureSize()) { return new Widget(mLauncher, widget); } diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index 9ef64767e6..531f830745 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -176,7 +176,7 @@ public final class Workspace extends Home { mLauncher, getHotseatAppIcon("Chrome"), new Point(mLauncher.getDevice().getDisplayWidth(), - workspace.getVisibleBounds().centerY()), + mLauncher.getVisibleBounds(workspace).centerY()), "deep_shortcuts_container", false); verifyActiveContainer(); From ff65674a10d276dcfcf0da029960784b59e826a9 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Tue, 31 Mar 2020 19:25:48 -0700 Subject: [PATCH 04/17] Remove accessibility long click option if no long click action occurs. Bug: 149370403 Change-Id: Ifdbd673a53229c76a99f7b8ef9569db7195481a2 --- .../accessibility/LauncherAccessibilityDelegate.java | 6 +++--- .../android/launcher3/popup/PopupContainerWithArrow.java | 9 ++++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index 24c846cf83..f45c0b19d2 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -157,8 +157,8 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme } private boolean itemSupportsLongClick(View host, ItemInfo info) { - return new CustomActionsPopup(mLauncher, host).canShow() - || ShortcutUtil.supportsShortcuts(info); + return PopupContainerWithArrow.canShow(host, info) + || new CustomActionsPopup(mLauncher, host).canShow(); } private boolean itemSupportsAccessibleDrag(ItemInfo item) { @@ -181,7 +181,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme public boolean performAction(final View host, final ItemInfo item, int action) { if (action == ACTION_LONG_CLICK) { - if (ShortcutUtil.supportsShortcuts(item)) { + if (PopupContainerWithArrow.canShow(host, item)) { // Long press should be consumed for workspace items, and it should invoke the // Shortcuts / Notifications / Actions pop-up menu, and not start a drag as the // standard long press path does. diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index 9bac259efe..406e1b2180 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -184,6 +184,13 @@ public class PopupContainerWithArrow extends Arr return false; } + /** + * Returns true if we can show the container. + */ + public static boolean canShow(View icon, ItemInfo item) { + return icon instanceof BubbleTextView && ShortcutUtil.supportsShortcuts(item); + } + /** * Shows the notifications and deep shortcuts associated with {@param icon}. * @return the container if shown or null. @@ -196,7 +203,7 @@ public class PopupContainerWithArrow extends Arr return null; } ItemInfo item = (ItemInfo) icon.getTag(); - if (!ShortcutUtil.supportsShortcuts(item)) { + if (!canShow(icon, item)) { return null; } From 00a031fbabfcd71f249ae675386bb426e9388057 Mon Sep 17 00:00:00 2001 From: vadimt Date: Wed, 1 Apr 2020 15:33:35 -0700 Subject: [PATCH 05/17] More diags for missing widgets Bug: 152645831 Change-Id: Idf6584220328a0853cd8dd65ebb5c54bab9980fd --- tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java index a3c70ecbe9..001a88fdf6 100644 --- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java @@ -275,8 +275,10 @@ public class BindWidgetTest extends AbstractLauncherUiTest { } private void verifyPendingWidgetPresent() { + final Widget widget = mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT); + if (widget == null) mLauncher.dumpViewHierarchy(); // b/152645831 assertTrue("Pending widget is not present", - mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT) != null); + widget != null); } /** From 6b7cc3fdc217762963f1e84144aa9b4a079a70f9 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Mon, 30 Mar 2020 18:03:59 -0700 Subject: [PATCH 06/17] Re-enable orientation sensor for Overview animation Slight revert of ag/10668129 with adjustment of disabling it for tests. Fixes: 151456795 Test: Ran the labtest command for OOP tests for crosshatch (where this issue was first detected) Change-Id: I315d138c2e4a6d4068304e9b5fb2e1b7feb34e63 --- .../android/quickstep/views/RecentsView.java | 58 +++++++++++++++++++ .../testing/TestInformationHandler.java | 4 ++ .../launcher3/testing/TestProtocol.java | 3 + .../launcher3/ui/AbstractLauncherUiTest.java | 2 + .../tapl/LauncherInstrumentation.java | 4 ++ 5 files changed, 71 insertions(+) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 57a9940925..4a36f54046 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -69,6 +69,7 @@ import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; +import android.view.OrientationEventListener; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; @@ -88,6 +89,7 @@ import com.android.launcher3.LauncherState; import com.android.launcher3.PagedView; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.PendingAnimation.EndState; @@ -98,6 +100,7 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.states.RotationHelper; +import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; @@ -167,6 +170,7 @@ public abstract class RecentsView extends PagedView impl } }; + private OrientationEventListener mOrientationListener; private int mPreviousRotation; protected RecentsAnimationController mRecentsAnimationController; protected RecentsAnimationTargets mRecentsAnimationTargets; @@ -374,6 +378,22 @@ public abstract class RecentsView extends PagedView impl // Initialize quickstep specific cache params here, as this is constructed only once mActivity.getViewCache().setCacheSize(R.layout.digital_wellbeing_toast, 5); + + mOrientationListener = new OrientationEventListener(getContext()) { + @Override + public void onOrientationChanged(int i) { + int rotation = RotationHelper.getRotationFromDegrees(i); + if (mPreviousRotation != rotation) { + animateRecentsRotationInPlace(rotation); + if (rotation == 0) { + showActionsView(); + } else { + hideActionsView(); + } + mPreviousRotation = rotation; + } + } + }; } public OverScroller getScroller() { @@ -502,6 +522,15 @@ public abstract class RecentsView extends PagedView impl } public void setOverviewStateEnabled(boolean enabled) { + if (supportsVerticalLandscape() + && !TestProtocol.sDisableSensorRotation // Ignore hardware dependency for tests + && mOrientationListener.canDetectOrientation()) { + if (enabled) { + mOrientationListener.enable(); + } else { + mOrientationListener.disable(); + } + } mOverviewStateEnabled = enabled; updateTaskStackListenerState(); if (!enabled) { @@ -945,6 +974,35 @@ public abstract class RecentsView extends PagedView impl setSwipeDownShouldLaunchApp(true); } + private void animateRecentsRotationInPlace(int newRotation) { + if (!supportsVerticalLandscape()) { + return; + } + + AnimatorSet pa = setRecentsChangedOrientation(true); + pa.addListener(AnimationSuccessListener.forRunnable(() -> { + updateLayoutRotation(newRotation); + mActivity.getDragLayer().recreateControllers(); + rotateAllChildTasks(); + setRecentsChangedOrientation(false).start(); + })); + pa.start(); + } + + public AnimatorSet setRecentsChangedOrientation(boolean fadeInChildren) { + getRunningTaskIndex(); + int runningIndex = getCurrentPage(); + AnimatorSet as = new AnimatorSet(); + for (int i = 0; i < getTaskViewCount(); i++) { + if (runningIndex == i) { + continue; + } + View taskView = getTaskViewAt(i); + as.play(ObjectAnimator.ofFloat(taskView, View.ALPHA, fadeInChildren ? 0 : 1)); + } + return as; + } + abstract protected boolean supportsVerticalLandscape(); private void rotateAllChildTasks() { diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java index 4e49c6edf2..e786f07237 100644 --- a/src/com/android/launcher3/testing/TestInformationHandler.java +++ b/src/com/android/launcher3/testing/TestInformationHandler.java @@ -184,6 +184,10 @@ public class TestInformationHandler implements ResourceBasedOverride { mDeviceProfile.allAppsCellHeightPx); break; } + + case TestProtocol.REQUEST_MOCK_SENSOR_ROTATION: + TestProtocol.sDisableSensorRotation = true; + break; } return response; } diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java index 9f20df66ce..3181752883 100644 --- a/src/com/android/launcher3/testing/TestProtocol.java +++ b/src/com/android/launcher3/testing/TestProtocol.java @@ -92,6 +92,9 @@ public final class TestProtocol { public static final String REQUEST_OVERVIEW_ACTIONS_ENABLED = "overview-actions-enabled"; + public static boolean sDisableSensorRotation; + public static final String REQUEST_MOCK_SENSOR_ROTATION = "mock-sensor-rotation"; + public static final String PERMANENT_DIAG_TAG = "TaplTarget"; public static final String NO_BACKGROUND_TO_OVERVIEW_TAG = "b/138251824"; diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 7cd656e811..873f1cbe6d 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -162,6 +162,7 @@ public abstract class AbstractLauncherUiTest { mLauncher.enableDebugTracing(); // Avoid double-reporting of Launcher crashes. mLauncher.setOnLauncherCrashed(() -> mLauncherPid = 0); + mLauncher.disableSensorRotation(); } protected final LauncherActivityRule mActivityMonitor = new LauncherActivityRule(); @@ -277,6 +278,7 @@ public abstract class AbstractLauncherUiTest { clearPackageData(mDevice.getLauncherPackageName()); mLauncher.enableDebugTracing(); mLauncherPid = mLauncher.getPid(); + mLauncher.disableSensorRotation(); } } diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index d171a695a7..d7374a4b63 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -1255,6 +1255,10 @@ public final class LauncherInstrumentation { TestProtocol.TEST_INFO_RESPONSE_FIELD); } + public void disableSensorRotation() { + getTestInfo(TestProtocol.REQUEST_MOCK_SENSOR_ROTATION); + } + public void disableDebugTracing() { getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING); } From 257589dd48b5c94e986a0df47118090e42c75e1b Mon Sep 17 00:00:00 2001 From: vadimt Date: Thu, 2 Apr 2020 13:02:15 -0700 Subject: [PATCH 07/17] More tracing for widgets scroll bug Bug: 152354290 Change-Id: I772d6faf4f96f423246b379169964de072174e50 --- .../launcher3/widget/WidgetsRecyclerView.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java index c15557beb2..cf1c387a7a 100644 --- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java @@ -19,11 +19,14 @@ package com.android.launcher3.widget; import android.content.Context; import android.graphics.Point; import android.util.AttributeSet; +import android.util.Log; import android.view.MotionEvent; import android.view.View; import com.android.launcher3.BaseRecyclerView; import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.testing.TestProtocol; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -169,4 +172,14 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch @Override public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "WidgetsRecyclerView: state:" + + getScrollState() + + " can scroll: " + getLayoutManager().canScrollVertically() + " event: " + ev); + } + return super.dispatchTouchEvent(ev); + } } \ No newline at end of file From 6eaf989d985116d417c77f518a7b5317246a3686 Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Wed, 1 Apr 2020 11:40:40 -0700 Subject: [PATCH 08/17] Hybrid Hotseat a11y - speak meaningful accessibility label for predicted items - disable accessibility focus for on-boarding preview items - add PIN as an accessibility action - remove move and remove actions for prediction icons Bug:152376193 Bug:152359303 Bug:152374583 Bug:152357657 Bug:152268303 Bug:152379490 Change-Id: I40fe0ef6329cd5b1d9215ac5fa1716f15db89ac8 --- .../hybridhotseat/HotseatEduDialog.java | 1 + .../HotseatPredictionController.java | 5 ++- .../uioverrides/PredictedAppIcon.java | 38 ++++++++++++++++++- quickstep/res/values/strings.xml | 3 ++ res/values/config.xml | 1 + .../android/launcher3/DeleteDropTarget.java | 2 +- .../LauncherAccessibilityDelegate.java | 28 +++++++++++++- 7 files changed, 72 insertions(+), 6 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java index 8944088213..b8c4a2147f 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java @@ -210,6 +210,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable WorkspaceItemInfo info = predictions.get(i); PredictedAppIcon icon = PredictedAppIcon.createIcon(mSampleHotseat, info); icon.setEnabled(false); + icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); icon.verifyHighRes(); CellLayout.LayoutParams lp = new CellLayout.LayoutParams(i, 0, 1, 1); mSampleHotseat.addViewToCellLayout(icon, i, info.getViewId(), lp, true); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java index d3bb4f9062..9bc097527c 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java @@ -349,7 +349,10 @@ public class HotseatPredictionController implements DragController.DragListener, mHotSeatItemsCount); } - private void pinPrediction(ItemInfo info) { + /** + * Pins a predicted app icon into place. + */ + public void pinPrediction(ItemInfo info) { PredictedAppIcon icon = (PredictedAppIcon) mHotseat.getChildAt( mHotseat.getCellXFromOrder(info.rank), mHotseat.getCellYFromOrder(info.rank)); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java index 4bbb48ce83..304c77f3da 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.uioverrides; +import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.PIN_PREDICTION; import static com.android.launcher3.graphics.IconShape.getShape; import android.content.Context; @@ -26,15 +27,19 @@ import android.graphics.Rect; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityNodeInfo; import androidx.core.graphics.ColorUtils; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.WorkspaceItemInfo; +import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.graphics.IconPalette; +import com.android.launcher3.hybridhotseat.HotseatPredictionController; import com.android.launcher3.icons.IconNormalizer; import com.android.launcher3.touch.ItemClickHandler; import com.android.launcher3.touch.ItemLongClickListener; @@ -43,7 +48,8 @@ import com.android.launcher3.views.DoubleShadowBubbleTextView; /** * A BubbleTextView with a ring around it's drawable */ -public class PredictedAppIcon extends DoubleShadowBubbleTextView { +public class PredictedAppIcon extends DoubleShadowBubbleTextView implements + LauncherAccessibilityDelegate.AccessibilityActionHandler { private static final float RING_EFFECT_RATIO = 0.11f; @@ -97,6 +103,13 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView { super.applyFromWorkspaceItem(info); int color = IconPalette.getMutedColor(info.bitmap.color, 0.54f); mIconRingPaint.setColor(ColorUtils.setAlphaComponent(color, 200)); + if (mIsPinned) { + setContentDescription(info.contentDescription); + } else { + setContentDescription( + getContext().getString(R.string.hotseat_prediction_content_description, + info.contentDescription)); + } } /** @@ -104,9 +117,9 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView { */ public void pin(WorkspaceItemInfo info) { if (mIsPinned) return; + mIsPinned = true; applyFromWorkspaceItem(info); setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE); - mIsPinned = true; ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true; invalidate(); } @@ -121,6 +134,27 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView { verifyHighRes(); } + @Override + public void addSupportedAccessibilityActions(AccessibilityNodeInfo accessibilityNodeInfo) { + accessibilityNodeInfo.addAction( + new AccessibilityNodeInfo.AccessibilityAction(PIN_PREDICTION, + getContext().getText(R.string.pin_prediction))); + } + + @Override + public boolean performAccessibilityAction(int action, ItemInfo info) { + QuickstepLauncher launcher = Launcher.cast(Launcher.getLauncher(getContext())); + if (action == PIN_PREDICTION) { + if (launcher == null || launcher.getHotseatPredictionController() == null) { + return false; + } + HotseatPredictionController controller = launcher.getHotseatPredictionController(); + controller.pinPrediction(info); + return true; + } + return false; + } + @Override public void getIconBounds(Rect outBounds) { super.getIconBounds(outBounds); diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml index 40d7c7a7c0..37516c65db 100644 --- a/quickstep/res/values/strings.xml +++ b/quickstep/res/values/strings.xml @@ -93,6 +93,9 @@ App suggestions added to empty space + + Predicted app: %1$s + Try the back gesture diff --git a/res/values/config.xml b/res/values/config.xml index 1675a986d3..ef6761387c 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -109,6 +109,7 @@ + diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index 423f2bbf1c..6f0ebd2a09 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -67,7 +67,7 @@ public class DeleteDropTarget extends ButtonDropTarget { public boolean supportsAccessibilityDrop(ItemInfo info, View view) { if (info instanceof WorkspaceItemInfo) { // Support the action unless the item is in a context menu. - return info.screenId >= 0; + return canRemove(info); } return (info instanceof LauncherAppWidgetInfo) diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index 6f7f8e6a29..0337fd623a 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -56,6 +56,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme public static final int REMOVE = R.id.action_remove; public static final int UNINSTALL = R.id.action_uninstall; public static final int DISMISS_PREDICTION = R.id.action_dismiss_prediction; + public static final int PIN_PREDICTION = R.id.action_pin_prediction; public static final int RECONFIGURE = R.id.action_reconfigure; protected static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace; protected static final int MOVE = R.id.action_move; @@ -120,6 +121,10 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme if (!(host.getTag() instanceof ItemInfo)) return; ItemInfo item = (ItemInfo) host.getTag(); + if (host instanceof AccessibilityActionHandler) { + ((AccessibilityActionHandler) host).addSupportedAccessibilityActions(info); + } + // If the request came from keyboard, do not add custom shortcuts as that is already // exposed as a direct shortcut if (!fromKeyboard && ShortcutUtil.supportsShortcuts(item)) { @@ -154,7 +159,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme private boolean itemSupportsAccessibleDrag(ItemInfo item) { if (item instanceof WorkspaceItemInfo) { // Support the action unless the item is in a context menu. - return item.screenId >= 0; + return item.screenId >= 0 && item.container != Favorites.CONTAINER_HOTSEAT_PREDICTION; } return (item instanceof LauncherAppWidgetInfo) || (item instanceof FolderInfo); @@ -185,7 +190,10 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme return true; } } - + if (host instanceof AccessibilityActionHandler + && ((AccessibilityActionHandler) host).performAccessibilityAction(action, item)) { + return true; + } if (action == MOVE) { beginAccessibleDrag(host, item); } else if (action == ADD_TO_WORKSPACE) { @@ -456,4 +464,20 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme } return screenId; } + + /** + * An interface allowing views to handle their own action. + */ + public interface AccessibilityActionHandler { + + /** + * performs accessibility action and returns true on success + */ + boolean performAccessibilityAction(int action, ItemInfo itemInfo); + + /** + * adds all the accessibility actions that can be handled. + */ + void addSupportedAccessibilityActions(AccessibilityNodeInfo accessibilityNodeInfo); + } } From bebae01a94d8dbb4da29d1b0f686783140ae1ba1 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 2 Apr 2020 18:06:03 -0700 Subject: [PATCH 09/17] Not controlling actions visibility during alpha Bug: 153102687 Change-Id: Ib8f920d03a1123f08e76b0ca8b39784c073348c1 --- .../launcher3/uioverrides/RecentsViewStateController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java index 131b71fc53..3d6e519d3f 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.uioverrides; +import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA; import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT; import static com.android.launcher3.anim.Interpolators.LINEAR; @@ -91,7 +92,7 @@ public final class RecentsViewStateController extends View actionsView = mLauncher.getActionsView(); if (actionsView != null) { - propertySetter.setViewAlpha(actionsView, buttonAlpha, actionInterpolator); + propertySetter.setFloat(actionsView, VIEW_ALPHA, buttonAlpha, actionInterpolator); } } From 3d87a3397ef346ce6fdb339806f149550d6de35f Mon Sep 17 00:00:00 2001 From: vadimt Date: Thu, 2 Apr 2020 18:35:37 -0700 Subject: [PATCH 10/17] More widgets tracing Bug: 152354290 Change-Id: I16b35f080a989f810e6513342c016124d07a4d37 --- .../launcher3/widget/WidgetsRecyclerView.java | 39 ++++++++++++++++--- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java index cf1c387a7a..17baa271e2 100644 --- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java @@ -158,28 +158,55 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch mScrollbar.isHitInParent(e.getX(), e.getY(), mFastScrollerOffset); } if (mTouchDownOnScroller) { - return mScrollbar.handleTouchEvent(e, mFastScrollerOffset); + final boolean result = mScrollbar.handleTouchEvent(e, mFastScrollerOffset); + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onInterceptTouchEvent 1 " + result); + } + return result; + } + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onInterceptTouchEvent 2 false"); } return false; } @Override public void onTouchEvent(RecyclerView rv, MotionEvent e) { + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "WidgetsRecyclerView.onTouchEvent"); + } if (mTouchDownOnScroller) { mScrollbar.handleTouchEvent(e, mFastScrollerOffset); } } @Override - public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { } + public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onRequestDisallowInterceptTouchEvent " + + disallowIntercept); + } + } @Override public boolean dispatchTouchEvent(MotionEvent ev) { + final boolean result = super.dispatchTouchEvent(ev); if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { - Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "WidgetsRecyclerView: state:" - + getScrollState() - + " can scroll: " + getLayoutManager().canScrollVertically() + " event: " + ev); + Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "WidgetsRecyclerView: state: " + + getScrollState() + + " can scroll: " + getLayoutManager().canScrollVertically() + + " result: " + result + + " layout suppressed: " + isLayoutSuppressed() + + " event: " + ev); } - return super.dispatchTouchEvent(ev); + return result; + } + + @Override + public void stopNestedScroll() { + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "stopNestedScroll"); + } + super.stopNestedScroll(); } } \ No newline at end of file From 8492edb131543525a5ae3b3049955297eba2ee19 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 2 Apr 2020 17:20:07 -0700 Subject: [PATCH 11/17] Removing new object creating during scroll/draw Change-Id: I627832c1659ac332d0ea3279dffba9d3c71ec2af --- .../android/quickstep/views/RecentsView.java | 30 ++++++++++--------- .../touch/LandscapePagedViewHandler.java | 21 ++++++------- .../touch/PagedOrientationHandler.java | 18 ++++------- .../touch/PortraitPagedViewHandler.java | 21 ++++++------- 4 files changed, 40 insertions(+), 50 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index c73b3ee73f..a069c566c6 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -813,23 +813,14 @@ public abstract class RecentsView extends PagedView impl if (getPageCount() == 0 || getPageAt(0).getMeasuredWidth() == 0) { return; } - CurveProperties curveProperties = mOrientationHandler - .getCurveProperties(this, mInsets); - int scroll = curveProperties.scroll; - final int halfPageSize = curveProperties.halfPageSize; - final int screenCenter = curveProperties.screenCenter; - final int halfScreenSize = curveProperties.halfScreenSize; - final int pageSpacing = mPageSpacing; - mScrollState.scrollFromEdge = mIsRtl ? scroll : (mMaxScroll - scroll); + mOrientationHandler.getCurveProperties(this, mInsets, mScrollState); + mScrollState.scrollFromEdge = + mIsRtl ? mScrollState.scroll : (mMaxScroll - mScrollState.scroll); final int pageCount = getPageCount(); for (int i = 0; i < pageCount; i++) { View page = getPageAt(i); - float pageCenter = mOrientationHandler.getViewCenterPosition(page) + halfPageSize; - float distanceFromScreenCenter = screenCenter - pageCenter; - float distanceToReachEdge = halfScreenSize + halfPageSize + pageSpacing; - mScrollState.linearInterpolation = Math.min(1, - Math.abs(distanceFromScreenCenter) / distanceToReachEdge); + mScrollState.updateInterpolation(mOrientationHandler.getChildStart(page), mPageSpacing); ((PageCallbacks) page).onPageScroll(mScrollState); } } @@ -1201,7 +1192,7 @@ public abstract class RecentsView extends PagedView impl default void onPageScroll(ScrollState scrollState) {} } - public static class ScrollState { + public static class ScrollState extends CurveProperties { /** * The progress from 0 to 1, where 0 is the center @@ -1213,6 +1204,17 @@ public abstract class RecentsView extends PagedView impl * The amount by which all the content is scrolled relative to the end of the list. */ public float scrollFromEdge; + + /** + * Updates linearInterpolation for the provided child position + */ + public void updateInterpolation(int childStart, int pageSpacing) { + float pageCenter = childStart + halfPageSize; + float distanceFromScreenCenter = screenCenter - pageCenter; + float distanceToReachEdge = halfScreenSize + halfPageSize + pageSpacing; + linearInterpolation = Math.min(1, + Math.abs(distanceFromScreenCenter) / distanceToReachEdge); + } } public void setIgnoreResetTask(int taskId) { diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java index 1db65b9705..6715bc1a0b 100644 --- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java +++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java @@ -16,8 +16,11 @@ package com.android.launcher3.touch; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; +import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL; + import android.content.res.Resources; -import android.graphics.Matrix; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; @@ -32,13 +35,8 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherState.ScaleAndTranslation; import com.android.launcher3.PagedView; import com.android.launcher3.Utilities; -import com.android.launcher3.states.RotationHelper; import com.android.launcher3.util.OverScroller; -import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; -import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; -import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL; - public class LandscapePagedViewHandler implements PagedOrientationHandler { @Override @@ -67,12 +65,11 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { } @Override - public CurveProperties getCurveProperties(PagedView pagedView, Rect mInsets) { - int scroll = pagedView.getScrollY(); - final int halfPageSize = pagedView.getNormalChildHeight() / 2; - final int screenCenter = mInsets.top + pagedView.getPaddingTop() + scroll + halfPageSize; - final int halfScreenSize = pagedView.getMeasuredHeight() / 2; - return new CurveProperties(scroll, halfPageSize, screenCenter, halfScreenSize); + public void getCurveProperties(PagedView view, Rect mInsets, CurveProperties out) { + out.scroll = view.getScrollY(); + out.halfPageSize = view.getNormalChildHeight() / 2; + out.halfScreenSize = view.getMeasuredHeight() / 2; + out.screenCenter = mInsets.top + view.getPaddingTop() + out.scroll + out.halfPageSize; } @Override diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java index b4802cda90..24fa81590a 100644 --- a/src/com/android/launcher3/touch/PagedOrientationHandler.java +++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java @@ -83,7 +83,7 @@ public interface PagedOrientationHandler { void delegateScrollTo(PagedView pagedView, int primaryScroll); void delegateScrollBy(PagedView pagedView, int unboundedScroll, int x, int y); void scrollerStartScroll(OverScroller scroller, int newPosition); - CurveProperties getCurveProperties(PagedView pagedView, Rect insets); + void getCurveProperties(PagedView view, Rect mInsets, CurveProperties out); boolean isGoingUp(float displacement); /** @@ -92,18 +92,12 @@ public interface PagedOrientationHandler { */ void adjustFloatingIconStartVelocity(PointF velocity); - class CurveProperties { - public final int scroll; - public final int halfPageSize; - public final int screenCenter; - public final int halfScreenSize; - public CurveProperties(int scroll, int halfPageSize, int screenCenter, int halfScreenSize) { - this.scroll = scroll; - this.halfPageSize = halfPageSize; - this.screenCenter = screenCenter; - this.halfScreenSize = halfScreenSize; - } + class CurveProperties { + public int scroll; + public int halfPageSize; + public int screenCenter; + public int halfScreenSize; } class ChildBounds { diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java index 22eee49df6..6d903b3882 100644 --- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java +++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java @@ -16,8 +16,11 @@ package com.android.launcher3.touch; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; +import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL; + import android.content.res.Resources; -import android.graphics.Matrix; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; @@ -32,13 +35,8 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherState.ScaleAndTranslation; import com.android.launcher3.PagedView; import com.android.launcher3.Utilities; -import com.android.launcher3.states.RotationHelper; import com.android.launcher3.util.OverScroller; -import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; -import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; -import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL; - public class PortraitPagedViewHandler implements PagedOrientationHandler { @Override @@ -67,12 +65,11 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { } @Override - public CurveProperties getCurveProperties(PagedView pagedView, Rect mInsets) { - int scroll = pagedView.getScrollX(); - final int halfPageSize = pagedView.getNormalChildWidth() / 2; - final int screenCenter = mInsets.left + pagedView.getPaddingLeft() + scroll + halfPageSize; - final int halfScreenSize = pagedView.getMeasuredWidth() / 2; - return new CurveProperties(scroll, halfPageSize, screenCenter, halfScreenSize); + public void getCurveProperties(PagedView view, Rect mInsets, CurveProperties out) { + out.scroll = view.getScrollX(); + out.halfPageSize = view.getNormalChildWidth() / 2; + out.halfScreenSize = view.getMeasuredWidth() / 2; + out.screenCenter = mInsets.left + view.getPaddingLeft() + out.scroll + out.halfPageSize; } @Override From c61664988c135c269de92e39adee6a20bc8ef1fa Mon Sep 17 00:00:00 2001 From: vadimt Date: Fri, 3 Apr 2020 13:14:27 -0700 Subject: [PATCH 12/17] Tracing for widgets sheet Bug: 152354290 Change-Id: I956e7aa0a93cf4d57409f68a34a184dfdec984ca --- .../android/launcher3/widget/WidgetsRecyclerView.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java index 17baa271e2..7ec62145e1 100644 --- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java @@ -209,4 +209,15 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch } super.stopNestedScroll(); } + + @Override + public void setLayoutFrozen(boolean frozen) { + if (frozen != isLayoutSuppressed()) { + if (Utilities.IS_RUNNING_IN_TEST_HARNESS) { + Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "setLayoutFrozen " + frozen + + " @ " + android.util.Log.getStackTraceString(new Throwable())); + } + } + super.setLayoutFrozen(frozen); + } } \ No newline at end of file From c4d3201538b6a48229e80d1b697affaa2ca94282 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 3 Apr 2020 17:10:11 -0700 Subject: [PATCH 13/17] Removing support for fake landscape Bug: 111068105 Change-Id: If31d2f700ddee1d21541735de3a8006ee2a53c5c --- .../uioverrides/QuickstepLauncher.java | 81 ---------- .../android/quickstep/BaseSwipeUpHandler.java | 5 +- .../OtherActivityInputConsumer.java | 2 +- .../android/quickstep/views/RecentsView.java | 9 +- .../launcher3/BaseQuickstepLauncher.java | 5 +- .../quickstep/util/NavBarPosition.java | 82 +--------- src/com/android/launcher3/BubbleTextView.java | 6 +- src/com/android/launcher3/CellLayout.java | 45 +----- src/com/android/launcher3/Hotseat.java | 11 +- src/com/android/launcher3/Launcher.java | 63 +------- .../android/launcher3/LauncherRootView.java | 4 +- .../launcher3/ShortcutAndWidgetContainer.java | 6 +- src/com/android/launcher3/Utilities.java | 23 +-- src/com/android/launcher3/Workspace.java | 30 ++-- .../WorkspaceStateTransitionAnimation.java | 6 +- .../launcher3/config/FeatureFlags.java | 3 - .../launcher3/dragndrop/DragLayer.java | 149 ------------------ .../android/launcher3/folder/FolderIcon.java | 5 +- .../launcher3/folder/PreviewBackground.java | 2 +- .../launcher3/graphics/RotationMode.java | 48 ------ .../launcher3/states/RotationHelper.java | 53 ++----- .../launcher3/views/ActivityContext.java | 14 -- .../launcher3/views/FloatingIconView.java | 28 ++-- .../android/launcher3/views/Transposable.java | 26 --- 24 files changed, 64 insertions(+), 642 deletions(-) delete mode 100644 src/com/android/launcher3/graphics/RotationMode.java delete mode 100644 src/com/android/launcher3/views/Transposable.java diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index a6eea0c0b9..da8111430b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -19,12 +19,9 @@ import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; -import android.content.Context; import android.content.Intent; import android.content.res.Configuration; -import android.graphics.Rect; import android.os.Bundle; -import android.view.Gravity; import android.view.View; import androidx.annotation.Nullable; @@ -38,7 +35,6 @@ import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.Folder; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.hybridhotseat.HotseatPredictionController; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController; @@ -71,77 +67,6 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { */ public static final AsyncCommand SET_SHELF_HEIGHT = (context, arg1, arg2) -> SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2); - public static final RotationMode ROTATION_LANDSCAPE = new RotationMode(-90) { - @Override - public void mapRect(int left, int top, int right, int bottom, Rect out) { - out.left = top; - out.top = right; - out.right = bottom; - out.bottom = left; - } - - @Override - public void mapInsets(Context context, Rect insets, Rect out) { - // If there is a display cutout, the top insets in portrait would also include the - // cutout, which we will get as the left inset in landscape. Using the max of left and - // top allows us to cover both cases (with or without cutout). - if (SysUINavigationMode.getMode(context) == NO_BUTTON) { - out.top = Math.max(insets.top, insets.left); - out.bottom = Math.max(insets.right, insets.bottom); - out.left = out.right = 0; - } else { - out.top = Math.max(insets.top, insets.left); - out.bottom = insets.right; - out.left = insets.bottom; - out.right = 0; - } - } - }; - public static final RotationMode ROTATION_SEASCAPE = new RotationMode(90) { - @Override - public void mapRect(int left, int top, int right, int bottom, Rect out) { - out.left = bottom; - out.top = left; - out.right = top; - out.bottom = right; - } - - @Override - public void mapInsets(Context context, Rect insets, Rect out) { - if (SysUINavigationMode.getMode(context) == NO_BUTTON) { - out.top = Math.max(insets.top, insets.right); - out.bottom = Math.max(insets.left, insets.bottom); - out.left = out.right = 0; - } else { - out.top = Math.max(insets.top, insets.right); - out.bottom = insets.left; - out.right = insets.bottom; - out.left = 0; - } - } - - @Override - public int toNaturalGravity(int absoluteGravity) { - int horizontalGravity = absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK; - int verticalGravity = absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK; - - if (horizontalGravity == Gravity.RIGHT) { - horizontalGravity = Gravity.LEFT; - } else if (horizontalGravity == Gravity.LEFT) { - horizontalGravity = Gravity.RIGHT; - } - - if (verticalGravity == Gravity.TOP) { - verticalGravity = Gravity.BOTTOM; - } else if (verticalGravity == Gravity.BOTTOM) { - verticalGravity = Gravity.TOP; - } - - return ((absoluteGravity & ~Gravity.HORIZONTAL_GRAVITY_MASK) - & ~Gravity.VERTICAL_GRAVITY_MASK) - | horizontalGravity | verticalGravity; - } - }; private HotseatPredictionController mHotseatPredictionController; @Override @@ -152,12 +77,6 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { } } - @Override - protected RotationMode getFakeRotationMode(DeviceProfile dp) { - return !dp.isVerticalBarLayout() ? RotationMode.NORMAL - : (dp.isSeascape() ? ROTATION_SEASCAPE : ROTATION_LANDSCAPE); - } - @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java index 113cdec9d0..5abeae4653 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java @@ -46,7 +46,6 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.model.PagedViewOrientedState; import com.android.launcher3.states.RotationHelper; import com.android.launcher3.testing.TestProtocol; @@ -149,8 +148,8 @@ public abstract class BaseSwipeUpHandler getRecentsViewDispatcher(RotationMode navBarRotationMode) { - return mRecentsView != null ? mRecentsView.getEventDispatcher(navBarRotationMode) : null; + public Consumer getRecentsViewDispatcher(float navbarRotation) { + return mRecentsView != null ? mRecentsView.getEventDispatcher(navbarRotation) : null; } @UiThread diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java index a462949738..fe9ef2befe 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java @@ -182,7 +182,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC if (mPassedWindowMoveSlop && mInteractionHandler != null && !mRecentsViewDispatcher.hasConsumer()) { mRecentsViewDispatcher.setConsumer(mInteractionHandler - .getRecentsViewDispatcher(mNavBarPosition.getRotationMode())); + .getRecentsViewDispatcher(mNavBarPosition.getRotation())); } int edgeFlags = ev.getEdgeFlags(); ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index a069c566c6..e3b3102562 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -99,7 +99,6 @@ import com.android.launcher3.anim.PropertyListBuilder; import com.android.launcher3.anim.SpringProperty; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.states.RotationHelper; import com.android.launcher3.testing.TestProtocol; @@ -1984,13 +1983,13 @@ public abstract class RecentsView extends PagedView impl return offsetX; } - public Consumer getEventDispatcher(RotationMode navBarRotationMode) { + public Consumer getEventDispatcher(float navbarRotation) { float degreesRotated; - if (navBarRotationMode == RotationMode.NORMAL) { + if (navbarRotation == 0) { degreesRotated = mOrientationState.areMultipleLayoutOrientationsDisabled() ? 0 : RotationHelper.getDegreesFromRotation(mLayoutRotation); } else { - degreesRotated = -navBarRotationMode.surfaceRotation; + degreesRotated = -navbarRotation; } if (degreesRotated == 0) { return super::onTouchEvent; @@ -2000,7 +1999,7 @@ public abstract class RecentsView extends PagedView impl // undo that transformation since PagedView also accommodates for the transformation via // PagedOrientationHandler return e -> { - if (navBarRotationMode != RotationMode.NORMAL + if (navbarRotation != 0 && !mOrientationState.areMultipleLayoutOrientationsDisabled()) { RotationHelper.transformEventForNavBar(e, true); super.onTouchEvent(e); diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index 135daef6bd..abdff0d7bb 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -88,9 +88,7 @@ public abstract class BaseQuickstepLauncher extends Launcher super.onCreate(savedInstanceState); mSystemActions = new SystemActions(this); - SysUINavigationMode.Mode mode = SysUINavigationMode.INSTANCE.get(this) - .addModeChangeListener(this); - getRotationHelper().setRotationHadDifferentUI(mode != Mode.NO_BUTTON); + SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this); if (!getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) { getStateManager().addStateListener(new LauncherStateManager.StateListener() { @@ -141,7 +139,6 @@ public abstract class BaseQuickstepLauncher extends Launcher @Override public void onNavigationModeChanged(Mode newMode) { getDragLayer().recreateControllers(); - getRotationHelper().setRotationHadDifferentUI(newMode != Mode.NO_BUTTON); } @Override diff --git a/quickstep/src/com/android/quickstep/util/NavBarPosition.java b/quickstep/src/com/android/quickstep/util/NavBarPosition.java index 8dc19dc71e..74e6b29295 100644 --- a/quickstep/src/com/android/quickstep/util/NavBarPosition.java +++ b/quickstep/src/com/android/quickstep/util/NavBarPosition.java @@ -17,12 +17,8 @@ package com.android.quickstep.util; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; -import android.content.Context; -import android.graphics.Rect; -import android.view.Gravity; import android.view.Surface; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.util.DefaultDisplay; import com.android.quickstep.SysUINavigationMode; @@ -31,79 +27,6 @@ import com.android.quickstep.SysUINavigationMode; */ public class NavBarPosition { - public static final RotationMode ROTATION_LANDSCAPE = new RotationMode(-90) { - @Override - public void mapRect(int left, int top, int right, int bottom, Rect out) { - out.left = top; - out.top = right; - out.right = bottom; - out.bottom = left; - } - - @Override - public void mapInsets(Context context, Rect insets, Rect out) { - // If there is a display cutout, the top insets in portrait would also include the - // cutout, which we will get as the left inset in landscape. Using the max of left and - // top allows us to cover both cases (with or without cutout). - if (SysUINavigationMode.getMode(context) == NO_BUTTON) { - out.top = Math.max(insets.top, insets.left); - out.bottom = Math.max(insets.right, insets.bottom); - out.left = out.right = 0; - } else { - out.top = Math.max(insets.top, insets.left); - out.bottom = insets.right; - out.left = insets.bottom; - out.right = 0; - } - } - }; - - public static final RotationMode ROTATION_SEASCAPE = new RotationMode(90) { - @Override - public void mapRect(int left, int top, int right, int bottom, Rect out) { - out.left = bottom; - out.top = left; - out.right = top; - out.bottom = right; - } - - @Override - public void mapInsets(Context context, Rect insets, Rect out) { - if (SysUINavigationMode.getMode(context) == NO_BUTTON) { - out.top = Math.max(insets.top, insets.right); - out.bottom = Math.max(insets.left, insets.bottom); - out.left = out.right = 0; - } else { - out.top = Math.max(insets.top, insets.right); - out.bottom = insets.left; - out.right = insets.bottom; - out.left = 0; - } - } - - @Override - public int toNaturalGravity(int absoluteGravity) { - int horizontalGravity = absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK; - int verticalGravity = absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK; - - if (horizontalGravity == Gravity.RIGHT) { - horizontalGravity = Gravity.LEFT; - } else if (horizontalGravity == Gravity.LEFT) { - horizontalGravity = Gravity.RIGHT; - } - - if (verticalGravity == Gravity.TOP) { - verticalGravity = Gravity.BOTTOM; - } else if (verticalGravity == Gravity.BOTTOM) { - verticalGravity = Gravity.TOP; - } - - return ((absoluteGravity & ~Gravity.HORIZONTAL_GRAVITY_MASK) - & ~Gravity.VERTICAL_GRAVITY_MASK) - | horizontalGravity | verticalGravity; - } - }; - private final SysUINavigationMode.Mode mMode; private final int mDisplayRotation; @@ -120,8 +43,7 @@ public class NavBarPosition { return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_270; } - public RotationMode getRotationMode() { - return isLeftEdge() ? ROTATION_SEASCAPE - : (isRightEdge() ? ROTATION_LANDSCAPE : RotationMode.NORMAL); + public float getRotation() { + return isLeftEdge() ? 90 : (isRightEdge() ? -90 : 0); } } diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index c8e73ba8d4..921e8ac14d 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -151,27 +151,25 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.BubbleTextView, defStyle, 0); mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false); + DeviceProfile grid = mActivity.getDeviceProfile(); mDisplay = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE); final int defaultIconSize; if (mDisplay == DISPLAY_WORKSPACE) { - DeviceProfile grid = mActivity.getWallpaperDeviceProfile(); setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx); setCompoundDrawablePadding(grid.iconDrawablePaddingPx); defaultIconSize = grid.iconSizePx; } else if (mDisplay == DISPLAY_ALL_APPS) { - DeviceProfile grid = mActivity.getDeviceProfile(); setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx); setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx); defaultIconSize = grid.allAppsIconSizePx; } else if (mDisplay == DISPLAY_FOLDER) { - DeviceProfile grid = mActivity.getDeviceProfile(); setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.folderChildTextSizePx); setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx); defaultIconSize = grid.folderChildIconSizePx; } else { // widget_selection or shortcut_popup - defaultIconSize = mActivity.getDeviceProfile().iconSizePx; + defaultIconSize = grid.iconSizePx; } mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false); diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 9682d09cd1..4259196465 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -59,14 +59,12 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DraggableView; import com.android.launcher3.folder.PreviewBackground; import com.android.launcher3.graphics.DragPreviewProvider; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.util.CellAndSpan; import com.android.launcher3.util.GridOccupancy; import com.android.launcher3.util.ParcelableSparseArray; import com.android.launcher3.util.Themes; import com.android.launcher3.util.Thunk; import com.android.launcher3.views.ActivityContext; -import com.android.launcher3.views.Transposable; import com.android.launcher3.widget.LauncherAppWidgetHostView; import java.lang.annotation.Retention; @@ -77,7 +75,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Stack; -public class CellLayout extends ViewGroup implements Transposable { +public class CellLayout extends ViewGroup { private static final String TAG = "CellLayout"; private static final boolean LOGD = false; @@ -184,7 +182,6 @@ public class CellLayout extends ViewGroup implements Transposable { // Related to accessible drag and drop private boolean mUseTouchHelper = false; - private RotationMode mRotationMode = RotationMode.NORMAL; public CellLayout(Context context) { this(context, null); @@ -206,7 +203,7 @@ public class CellLayout extends ViewGroup implements Transposable { setClipToPadding(false); mActivity = ActivityContext.lookupContext(context); - DeviceProfile grid = mActivity.getWallpaperDeviceProfile(); + DeviceProfile grid = mActivity.getDeviceProfile(); mCellWidth = mCellHeight = -1; mFixedCellWidth = mFixedCellHeight = -1; @@ -314,24 +311,6 @@ public class CellLayout extends ViewGroup implements Transposable { } } - public void setRotationMode(RotationMode mode) { - if (mRotationMode != mode) { - mRotationMode = mode; - requestLayout(); - } - } - - @Override - public RotationMode getRotationMode() { - return mRotationMode; - } - - @Override - public void setPadding(int left, int top, int right, int bottom) { - mRotationMode.mapRect(left, top, right, bottom, mTempRect); - super.setPadding(mTempRect.left, mTempRect.top, mTempRect.right, mTempRect.bottom); - } - @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (mUseTouchHelper || @@ -789,13 +768,6 @@ public class CellLayout extends ViewGroup implements Transposable { int childWidthSize = widthSize - (getPaddingLeft() + getPaddingRight()); int childHeightSize = heightSize - (getPaddingTop() + getPaddingBottom()); - mShortcutsAndWidgets.setRotation(mRotationMode.surfaceRotation); - if (mRotationMode.isTransposed) { - int tmp = childWidthSize; - childWidthSize = childHeightSize; - childHeightSize = tmp; - } - if (mFixedCellWidth < 0 || mFixedCellHeight < 0) { int cw = DeviceProfile.calculateCellWidth(childWidthSize, mCountX); int ch = DeviceProfile.calculateCellHeight(childHeightSize, mCountY); @@ -846,15 +818,7 @@ public class CellLayout extends ViewGroup implements Transposable { right + mTempRect.right + getPaddingRight(), bottom + mTempRect.bottom + getPaddingBottom()); - if (mRotationMode.isTransposed) { - int halfW = mShortcutsAndWidgets.getMeasuredWidth() / 2; - int halfH = mShortcutsAndWidgets.getMeasuredHeight() / 2; - int cX = (left + right) / 2; - int cY = (top + bottom) / 2; - mShortcutsAndWidgets.layout(cX - halfW, cY - halfH, cX + halfW, cY + halfH); - } else { - mShortcutsAndWidgets.layout(left, top, right, bottom); - } + mShortcutsAndWidgets.layout(left, top, right, bottom); } /** @@ -863,8 +827,7 @@ public class CellLayout extends ViewGroup implements Transposable { * width in {@link DeviceProfile#calculateCellWidth(int, int)}. */ public int getUnusedHorizontalSpace() { - return (mRotationMode.isTransposed ? getMeasuredHeight() : getMeasuredWidth()) - - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth); + return getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth); } public Drawable getScrimBackground() { diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index 76cfe1c59e..78bd2ff502 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -27,15 +27,13 @@ import android.view.ViewDebug; import android.view.ViewGroup; import android.widget.FrameLayout; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; -import com.android.launcher3.views.Transposable; import java.util.ArrayList; -public class Hotseat extends CellLayout implements LogContainerProvider, Insettable, Transposable { +public class Hotseat extends CellLayout implements LogContainerProvider, Insettable { @ViewDebug.ExportedProperty(category = "launcher") private boolean mHasVerticalHotseat; @@ -89,7 +87,7 @@ public class Hotseat extends CellLayout implements LogContainerProvider, Insetta @Override public void setInsets(Rect insets) { FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); - DeviceProfile grid = mActivity.getWallpaperDeviceProfile(); + DeviceProfile grid = mActivity.getDeviceProfile(); insets = grid.getInsets(); if (grid.isVerticalBarLayout()) { @@ -117,9 +115,4 @@ public class Hotseat extends CellLayout implements LogContainerProvider, Insetta public boolean onTouchEvent(MotionEvent event) { return event.getY() > getCellHeight(); } - - @Override - public RotationMode getRotationMode() { - return Launcher.getLauncher(getContext()).getRotationMode(); - } } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 5b9f676bbd..046db3d9a3 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -56,7 +56,6 @@ import android.content.pm.PackageManager; import android.content.res.Configuration; import android.database.sqlite.SQLiteDatabase; import android.graphics.Point; -import android.graphics.Rect; import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; @@ -102,7 +101,6 @@ import com.android.launcher3.dragndrop.DragView; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderGridOrganizer; import com.android.launcher3.folder.FolderIcon; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.icons.IconCache; import com.android.launcher3.keyboard.CustomActionsPopup; import com.android.launcher3.keyboard.ViewGroupFocusHelper; @@ -317,9 +315,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, private float mCurrentAssistantVisibility = 0f; - private DeviceProfile mStableDeviceProfile; - private RotationMode mRotationMode = RotationMode.NORMAL; - protected LauncherOverlayManager mOverlayManager; // If true, overlay callbacks are deferred private boolean mDeferOverlayCallbacks; @@ -503,24 +498,12 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, super.onConfigurationChanged(newConfig); } - public void reload() { - onIdpChanged(mDeviceProfile.inv); - } - - private boolean supportsFakeLandscapeUI() { - return FeatureFlags.FAKE_LANDSCAPE_UI.get() && !mRotationHelper.homeScreenCanRotate(); - } - @Override public void reapplyUi() { reapplyUi(true /* cancelCurrentAnimation */); } public void reapplyUi(boolean cancelCurrentAnimation) { - if (supportsFakeLandscapeUI()) { - mRotationMode = mStableDeviceProfile == null - ? RotationMode.NORMAL : getFakeRotationMode(mDeviceProfile); - } getRootView().dispatchInsets(); getStateManager().reapplyState(cancelCurrentAnimation); } @@ -533,7 +516,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, private void onIdpChanged(InvariantDeviceProfile idp) { mUserEventDispatcher = null; - DeviceProfile oldWallpaperProfile = getWallpaperDeviceProfile(); initDeviceProfile(idp); dispatchDeviceProfileChanged(); reapplyUi(); @@ -542,9 +524,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, // Calling onSaveInstanceState ensures that static cache used by listWidgets is // initialized properly. onSaveInstanceState(new Bundle()); - if (oldWallpaperProfile != getWallpaperDeviceProfile()) { - mModel.rebindCallbacks(); - } + mModel.rebindCallbacks(); } public void onAssistantVisibilityChanged(float visibility) { @@ -571,41 +551,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize); } - if (supportsFakeLandscapeUI() && mDeviceProfile.isVerticalBarLayout()) { - mStableDeviceProfile = mDeviceProfile.inv.portraitProfile; - mRotationMode = getFakeRotationMode(mDeviceProfile); - } else { - mStableDeviceProfile = null; - mRotationMode = RotationMode.NORMAL; - } - - mRotationHelper.updateRotationAnimation(); onDeviceProfileInitiated(); - mModelWriter = mModel.getWriter(getWallpaperDeviceProfile().isVerticalBarLayout(), true); - } - - public void updateInsets(Rect insets) { - mDeviceProfile.updateInsets(insets); - if (mStableDeviceProfile != null) { - Rect r = mStableDeviceProfile.getInsets(); - mRotationMode.mapInsets(this, insets, r); - mStableDeviceProfile.updateInsets(r); - } - } - - @Override - public RotationMode getRotationMode() { - return mRotationMode; - } - - /** - * Device profile to be used by UI elements which are shown directly on top of the wallpaper - * and whose presentation is tied to the wallpaper (and physical device) and not the activity - * configuration. - */ - @Override - public DeviceProfile getWallpaperDeviceProfile() { - return mStableDeviceProfile == null ? mDeviceProfile : mStableDeviceProfile; + mModelWriter = mModel.getWriter(getDeviceProfile().isVerticalBarLayout(), true); } public RotationHelper getRotationHelper() { @@ -2049,7 +1996,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, mAppWidgetHost.clearViews(); if (mHotseat != null) { - mHotseat.resetLayout(getWallpaperDeviceProfile().isVerticalBarLayout()); + mHotseat.resetLayout(getDeviceProfile().isVerticalBarLayout()); } TraceHelper.INSTANCE.endSection(traceToken); } @@ -2724,10 +2671,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, return new TouchController[] {getDragController(), new AllAppsSwipeController(this)}; } - protected RotationMode getFakeRotationMode(DeviceProfile deviceProfile) { - return RotationMode.NORMAL; - } - protected ScaleAndTranslation getOverviewScaleAndTranslationForNormalState() { return new ScaleAndTranslation(1.1f, 0f, 0f); } diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java index 2b2224a600..b4fbbc3279 100644 --- a/src/com/android/launcher3/LauncherRootView.java +++ b/src/com/android/launcher3/LauncherRootView.java @@ -83,7 +83,7 @@ public class LauncherRootView extends InsettableFrameLayout { UI_STATE_ROOT_VIEW, drawInsetBar ? FLAG_DARK_NAV : 0); // Update device profile before notifying th children. - mLauncher.updateInsets(insets); + mLauncher.getDeviceProfile().updateInsets(insets); boolean resetState = !insets.equals(mInsets); setInsets(insets); @@ -127,7 +127,7 @@ public class LauncherRootView extends InsettableFrameLayout { } public void dispatchInsets() { - mLauncher.updateInsets(mInsets); + mLauncher.getDeviceProfile().updateInsets(mInsets); super.setInsets(mInsets); } diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java index c07dd9d3f8..6326b7ac6d 100644 --- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java +++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java @@ -93,7 +93,7 @@ public class ShortcutAndWidgetContainer extends ViewGroup { public void setupLp(View child) { CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); if (child instanceof LauncherAppWidgetHostView) { - DeviceProfile profile = mActivity.getWallpaperDeviceProfile(); + DeviceProfile profile = mActivity.getDeviceProfile(); lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, profile.appWidgetScale.x, profile.appWidgetScale.y); } else { @@ -108,12 +108,12 @@ public class ShortcutAndWidgetContainer extends ViewGroup { public int getCellContentHeight() { return Math.min(getMeasuredHeight(), - mActivity.getWallpaperDeviceProfile().getCellHeight(mContainerType)); + mActivity.getDeviceProfile().getCellHeight(mContainerType)); } public void measureChild(View child) { CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams(); - final DeviceProfile profile = mActivity.getWallpaperDeviceProfile(); + final DeviceProfile profile = mActivity.getDeviceProfile(); if (child instanceof LauncherAppWidgetHostView) { lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 122b393c7f..0cd08d4faa 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -62,7 +62,6 @@ import android.view.animation.Interpolator; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.FolderAdaptiveIcon; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.graphics.TintedDrawableSpan; import com.android.launcher3.icons.IconProvider; import com.android.launcher3.icons.LauncherIcons; @@ -72,7 +71,6 @@ import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.shortcuts.ShortcutRequest; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.PackageManagerHelper; -import com.android.launcher3.views.Transposable; import com.android.launcher3.widget.PendingAddShortcutInfo; import java.lang.reflect.Method; @@ -165,7 +163,7 @@ public final class Utilities { public static float getDescendantCoordRelativeToAncestor( View descendant, View ancestor, float[] coord, boolean includeRootScroll) { return getDescendantCoordRelativeToAncestor(descendant, ancestor, coord, includeRootScroll, - false, null); + false); } /** @@ -178,15 +176,12 @@ public final class Utilities { * @param includeRootScroll Whether or not to account for the scroll of the descendant: * sometimes this is relevant as in a child's coordinates within the descendant. * @param ignoreTransform If true, view transform is ignored - * @param outRotation If not null, and {@param ignoreTransform} is true, this is set to the - * overall rotation of the view in degrees. * @return The factor by which this descendant is scaled relative to this DragLayer. Caution * this scale factor is assumed to be equal in X and Y, and so if at any point this * assumption fails, we will need to return a pair of scale factors. */ public static float getDescendantCoordRelativeToAncestor(View descendant, View ancestor, - float[] coord, boolean includeRootScroll, boolean ignoreTransform, - float[] outRotation) { + float[] coord, boolean includeRootScroll, boolean ignoreTransform) { float scale = 1.0f; View v = descendant; while(v != ancestor && v != null) { @@ -196,19 +191,7 @@ public final class Utilities { offsetPoints(coord, -v.getScrollX(), -v.getScrollY()); } - if (ignoreTransform) { - if (v instanceof Transposable) { - RotationMode m = ((Transposable) v).getRotationMode(); - if (m.isTransposed) { - sMatrix.setRotate(m.surfaceRotation, v.getPivotX(), v.getPivotY()); - sMatrix.mapPoints(coord); - - if (outRotation != null) { - outRotation[0] += m.surfaceRotation; - } - } - } - } else { + if (!ignoreTransform) { v.getMatrix().mapPoints(coord); } offsetPoints(coord, v.getLeft(), v.getTop()); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 8bc0242920..fc40fa60b6 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -75,7 +75,6 @@ import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.folder.PreviewBackground; import com.android.launcher3.graphics.DragPreviewProvider; import com.android.launcher3.graphics.PreloadIconDrawable; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.icons.BitmapRenderer; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.pageindicators.WorkspacePageIndicator; @@ -276,20 +275,13 @@ public class Workspace extends PagedView @Override public void setInsets(Rect insets) { DeviceProfile grid = mLauncher.getDeviceProfile(); - DeviceProfile stableGrid = mLauncher.getWallpaperDeviceProfile(); - mMaxDistanceForFolderCreation = stableGrid.isTablet - ? 0.75f * stableGrid.iconSizePx - : 0.55f * stableGrid.iconSizePx; + mMaxDistanceForFolderCreation = grid.isTablet + ? 0.75f * grid.iconSizePx : 0.55f * grid.iconSizePx; mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens(); - Rect padding = stableGrid.workspacePadding; - - RotationMode rotationMode = mLauncher.getRotationMode(); - - rotationMode.mapRect(padding, mTempRect); - setPadding(mTempRect.left, mTempRect.top, mTempRect.right, mTempRect.bottom); - rotationMode.mapRect(stableGrid.getInsets(), mInsets); + Rect padding = grid.workspacePadding; + setPadding(padding.left, padding.top, padding.right, padding.bottom); if (mWorkspaceFadeInAdjacentScreens) { // In landscape mode the page spacing is set to the default. @@ -302,12 +294,11 @@ public class Workspace extends PagedView } - int paddingLeftRight = stableGrid.cellLayoutPaddingLeftRightPx; - int paddingBottom = stableGrid.cellLayoutBottomPaddingPx; + int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx; + int paddingBottom = grid.cellLayoutBottomPaddingPx; for (int i = mWorkspaceScreens.size() - 1; i >= 0; i--) { - CellLayout page = mWorkspaceScreens.valueAt(i); - page.setRotationMode(rotationMode); - page.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom); + mWorkspaceScreens.valueAt(i) + .setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom); } } @@ -327,7 +318,7 @@ public class Workspace extends PagedView float scale = 1; if (isWidget) { - DeviceProfile profile = mLauncher.getWallpaperDeviceProfile(); + DeviceProfile profile = mLauncher.getDeviceProfile(); scale = Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y); } size[0] = r.width(); @@ -555,10 +546,9 @@ public class Workspace extends PagedView // created CellLayout. CellLayout newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate( R.layout.workspace_screen, this, false /* attachToRoot */); - DeviceProfile grid = mLauncher.getWallpaperDeviceProfile(); + DeviceProfile grid = mLauncher.getDeviceProfile(); int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx; int paddingBottom = grid.cellLayoutBottomPaddingPx; - newScreen.setRotationMode(mLauncher.getRotationMode()); newScreen.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom); mWorkspaceScreens.put(screenId, newScreen); diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index c521c34e33..c4c4377e83 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -104,10 +104,8 @@ public class WorkspaceStateTransitionAnimation { Interpolator scaleInterpolator = config.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT); propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator); - if (!hotseat.getRotationMode().isTransposed) { - setPivotToScaleWithWorkspace(hotseat); - setPivotToScaleWithWorkspace(qsbScaleView); - } + setPivotToScaleWithWorkspace(hotseat); + setPivotToScaleWithWorkspace(qsbScaleView); float hotseatScale = hotseatScaleAndTranslation.scale; Interpolator hotseatScaleInterpolator = config.getInterpolator(ANIM_HOTSEAT_SCALE, scaleInterpolator); diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 92f511222d..ec3435024c 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -95,9 +95,6 @@ public final class FeatureFlags { public static final BooleanFlag ENABLE_HINTS_IN_OVERVIEW = getDebugFlag( "ENABLE_HINTS_IN_OVERVIEW", false, "Show chip hints and gleams on the overview screen"); - public static final BooleanFlag FAKE_LANDSCAPE_UI = getDebugFlag( - "FAKE_LANDSCAPE_UI", false, "Rotate launcher UI instead of using transposed layout"); - public static final BooleanFlag FOLDER_NAME_SUGGEST = new DeviceFlag( "FOLDER_NAME_SUGGEST", true, "Suggests folder names instead of blank text."); diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index 9ece3d3bca..970c5a02e7 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -17,10 +17,6 @@ package com.android.launcher3.dragndrop; -import static android.view.View.MeasureSpec.EXACTLY; -import static android.view.View.MeasureSpec.getMode; -import static android.view.View.MeasureSpec.getSize; - import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5; import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent; @@ -34,14 +30,12 @@ import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Rect; import android.util.AttributeSet; -import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.animation.Interpolator; -import android.widget.FrameLayout; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.CellLayout; @@ -52,12 +46,10 @@ import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.Workspace; import com.android.launcher3.folder.Folder; import com.android.launcher3.graphics.OverviewScrim; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.graphics.WorkspaceAndHotseatScrim; import com.android.launcher3.keyboard.ViewGroupFocusHelper; import com.android.launcher3.util.Thunk; import com.android.launcher3.views.BaseDragLayer; -import com.android.launcher3.views.Transposable; import java.util.ArrayList; @@ -560,145 +552,4 @@ public class DragLayer extends BaseDragLayer { public OverviewScrim getOverviewScrim() { return mOverviewScrim; } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - RotationMode rotation = mActivity.getRotationMode(); - int count = getChildCount(); - - if (!rotation.isTransposed - || getMode(widthMeasureSpec) != EXACTLY - || getMode(heightMeasureSpec) != EXACTLY) { - - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - child.setRotation(rotation.surfaceRotation); - } - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } else { - - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.getVisibility() == GONE) { - continue; - } - if (!(child instanceof Transposable)) { - measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); - } else { - measureChildWithMargins(child, heightMeasureSpec, 0, widthMeasureSpec, 0); - - child.setPivotX(child.getMeasuredWidth() / 2); - child.setPivotY(child.getMeasuredHeight() / 2); - child.setRotation(rotation.surfaceRotation); - } - } - setMeasuredDimension(getSize(widthMeasureSpec), getSize(heightMeasureSpec)); - } - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - RotationMode rotation = mActivity.getRotationMode(); - if (!rotation.isTransposed) { - super.onLayout(changed, left, top, right, bottom); - return; - } - - final int count = getChildCount(); - - final int parentWidth = right - left; - final int parentHeight = bottom - top; - - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.getVisibility() == GONE) { - continue; - } - - final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) child.getLayoutParams(); - - if (lp instanceof LayoutParams) { - final LayoutParams dlp = (LayoutParams) lp; - if (dlp.customPosition) { - child.layout(dlp.x, dlp.y, dlp.x + dlp.width, dlp.y + dlp.height); - continue; - } - } - - final int width = child.getMeasuredWidth(); - final int height = child.getMeasuredHeight(); - - int childLeft; - int childTop; - - int gravity = lp.gravity; - if (gravity == -1) { - gravity = Gravity.TOP | Gravity.START; - } - - final int layoutDirection = getLayoutDirection(); - - int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); - - if (child instanceof Transposable) { - absoluteGravity = rotation.toNaturalGravity(absoluteGravity); - - switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.CENTER_HORIZONTAL: - childTop = (parentHeight - height) / 2 + - lp.topMargin - lp.bottomMargin; - break; - case Gravity.RIGHT: - childTop = width / 2 + lp.rightMargin - height / 2; - break; - case Gravity.LEFT: - default: - childTop = parentHeight - lp.leftMargin - width / 2 - height / 2; - } - - switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) { - case Gravity.CENTER_VERTICAL: - childLeft = (parentWidth - width) / 2 + - lp.leftMargin - lp.rightMargin; - break; - case Gravity.BOTTOM: - childLeft = parentWidth - width / 2 - height / 2 - lp.bottomMargin; - break; - case Gravity.TOP: - default: - childLeft = height / 2 - width / 2 + lp.topMargin; - } - } else { - switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { - case Gravity.CENTER_HORIZONTAL: - childLeft = (parentWidth - width) / 2 + - lp.leftMargin - lp.rightMargin; - break; - case Gravity.RIGHT: - childLeft = parentWidth - width - lp.rightMargin; - break; - case Gravity.LEFT: - default: - childLeft = lp.leftMargin; - } - - switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) { - case Gravity.TOP: - childTop = lp.topMargin; - break; - case Gravity.CENTER_VERTICAL: - childTop = (parentHeight - height) / 2 + - lp.topMargin - lp.bottomMargin; - break; - case Gravity.BOTTOM: - childTop = parentHeight - height - lp.bottomMargin; - break; - default: - childTop = lp.topMargin; - } - } - - child.layout(childLeft, childTop, childLeft + width, childTop + height); - } - } } diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 8251d68b70..eda9545b17 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -172,7 +172,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel "is dependent on this"); } - DeviceProfile grid = activity.getWallpaperDeviceProfile(); + DeviceProfile grid = activity.getDeviceProfile(); FolderIcon icon = (FolderIcon) LayoutInflater.from(group.getContext()) .inflate(resId, group, false); @@ -570,8 +570,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel public void drawDot(Canvas canvas) { if (!mForceHideDot && ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0)) { Rect iconBounds = mDotParams.iconBounds; - BubbleTextView.getIconBounds(this, iconBounds, - mActivity.getWallpaperDeviceProfile().iconSizePx); + BubbleTextView.getIconBounds(this, iconBounds, mActivity.getDeviceProfile().iconSizePx); float iconScale = (float) mBackground.previewSize / iconBounds.width(); Utilities.scaleRectAboutCenter(iconBounds, iconScale); diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java index 2d177d2f2d..27b906bcbb 100644 --- a/src/com/android/launcher3/folder/PreviewBackground.java +++ b/src/com/android/launcher3/folder/PreviewBackground.java @@ -153,7 +153,7 @@ public class PreviewBackground extends CellLayout.DelegatedCellDrawing { mBgColor = ta.getColor(R.styleable.FolderIconPreview_folderFillColor, 0); ta.recycle(); - DeviceProfile grid = activity.getWallpaperDeviceProfile(); + DeviceProfile grid = activity.getDeviceProfile(); previewSize = grid.folderIconSizePx; basePreviewOffsetX = (availableSpaceX - previewSize) / 2; diff --git a/src/com/android/launcher3/graphics/RotationMode.java b/src/com/android/launcher3/graphics/RotationMode.java deleted file mode 100644 index 6dd356ada9..0000000000 --- a/src/com/android/launcher3/graphics/RotationMode.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.graphics; - -import android.content.Context; -import android.graphics.Rect; - -public abstract class RotationMode { - - public static final RotationMode NORMAL = new RotationMode(0) { }; - - public final float surfaceRotation; - public final boolean isTransposed; - - public RotationMode(float surfaceRotation) { - this.surfaceRotation = surfaceRotation; - isTransposed = surfaceRotation != 0; - } - - public final void mapRect(Rect rect, Rect out) { - mapRect(rect.left, rect.top, rect.right, rect.bottom, out); - } - - public void mapRect(int left, int top, int right, int bottom, Rect out) { - out.set(left, top, right, bottom); - } - - public void mapInsets(Context context, Rect insets, Rect out) { - out.set(insets); - } - - public int toNaturalGravity(int absoluteGravity) { - return absoluteGravity; - } -} diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java index d28fcf6884..8bb6a08a34 100644 --- a/src/com/android/launcher3/states/RotationHelper.java +++ b/src/com/android/launcher3/states/RotationHelper.java @@ -26,6 +26,7 @@ import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE; import static com.android.launcher3.config.FeatureFlags.FLAG_ENABLE_FIXED_ROTATION_TRANSFORM; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; +import android.app.Activity; import android.content.ContentResolver; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; @@ -36,12 +37,9 @@ import android.graphics.RectF; import android.provider.Settings; import android.view.MotionEvent; import android.view.Surface; -import android.view.WindowManager; -import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.UiThreadHelper; import java.util.ArrayList; @@ -77,7 +75,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { public static final int REQUEST_ROTATE = 1; public static final int REQUEST_LOCK = 2; - private final Launcher mLauncher; + private final Activity mActivity; private final SharedPreferences mSharedPrefs; private final SharedPreferences mFeatureFlagsPrefs; @@ -104,17 +102,16 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { // This is used to defer setting rotation flags until the activity is being created private boolean mInitialized; private boolean mDestroyed; - private boolean mRotationHasDifferentUI; private int mLastActivityFlags = -1; - public RotationHelper(Launcher launcher) { - mLauncher = launcher; + public RotationHelper(Activity activity) { + mActivity = activity; // On large devices we do not handle auto-rotate differently. - mIgnoreAutoRotateSettings = mLauncher.getResources().getBoolean(R.bool.allow_rotation); + mIgnoreAutoRotateSettings = mActivity.getResources().getBoolean(R.bool.allow_rotation); if (!mIgnoreAutoRotateSettings) { - mSharedPrefs = Utilities.getPrefs(mLauncher); + mSharedPrefs = Utilities.getPrefs(mActivity); mSharedPrefs.registerOnSharedPreferenceChangeListener(this); mAutoRotateEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, getAllowRotationDefaultValue()); @@ -122,8 +119,8 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { mSharedPrefs = null; } - mContentResolver = launcher.getContentResolver(); - mFeatureFlagsPrefs = Utilities.getFeatureFlagsPrefs(mLauncher); + mContentResolver = activity.getContentResolver(); + mFeatureFlagsPrefs = Utilities.getFeatureFlagsPrefs(mActivity); mFeatureFlagsPrefs.registerOnSharedPreferenceChangeListener(this); updateForcedRotation(true); } @@ -144,7 +141,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { mForcedRotation = isForcedRotation; } UI_HELPER_EXECUTOR.execute(() -> { - if (mLauncher.checkSelfPermission(WRITE_SECURE_SETTINGS) == PERMISSION_GRANTED) { + if (mActivity.checkSelfPermission(WRITE_SECURE_SETTINGS) == PERMISSION_GRANTED) { Settings.Global.putInt(mContentResolver, FIXED_ROTATION_TRANSFORM_SETTING_NAME, mForcedRotation ? 1 : 0); } @@ -165,29 +162,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { mForcedRotationChangedListeners.remove(listener); } - public void setRotationHadDifferentUI(boolean rotationHasDifferentUI) { - mRotationHasDifferentUI = rotationHasDifferentUI; - } - - public boolean homeScreenCanRotate() { - return mRotationHasDifferentUI || mIgnoreAutoRotateSettings || mAutoRotateEnabled - || mStateHandlerRequest != REQUEST_NONE - || mLauncher.getDeviceProfile().isMultiWindowMode; - } - - public void updateRotationAnimation() { - if (FeatureFlags.FAKE_LANDSCAPE_UI.get()) { - WindowManager.LayoutParams lp = mLauncher.getWindow().getAttributes(); - int oldAnim = lp.rotationAnimation; - lp.rotationAnimation = homeScreenCanRotate() - ? WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE - : WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; - if (oldAnim != lp.rotationAnimation) { - mLauncher.getWindow().setAttributes(lp); - } - } - } - @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) { if (FLAG_ENABLE_FIXED_ROTATION_TRANSFORM.equals(s)) { @@ -199,17 +173,13 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { mAutoRotateEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, getAllowRotationDefaultValue()); if (mAutoRotateEnabled != wasRotationEnabled) { - notifyChange(); - updateRotationAnimation(); - mLauncher.reapplyUi(); } } public void setStateHandlerRequest(int request) { if (mStateHandlerRequest != request) { mStateHandlerRequest = request; - updateRotationAnimation(); notifyChange(); } } @@ -231,7 +201,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { // Used by tests only. public void forceAllowRotationForTesting(boolean allowRotation) { mIgnoreAutoRotateSettings = - allowRotation || mLauncher.getResources().getBoolean(R.bool.allow_rotation); + allowRotation || mActivity.getResources().getBoolean(R.bool.allow_rotation); // TODO(b/150214193) Tests currently expect launcher to be able to be rotated // Modify tests for this new behavior mForcedRotation = !allowRotation; @@ -243,7 +213,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { if (!mInitialized) { mInitialized = true; notifyChange(); - updateRotationAnimation(); } } @@ -285,7 +254,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { } if (activityFlags != mLastActivityFlags) { mLastActivityFlags = activityFlags; - UiThreadHelper.setOrientationAsync(mLauncher, activityFlags); + UiThreadHelper.setOrientationAsync(mActivity, activityFlags); } } diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java index 0331a86cd5..c9cdeffb9b 100644 --- a/src/com/android/launcher3/views/ActivityContext.java +++ b/src/com/android/launcher3/views/ActivityContext.java @@ -22,7 +22,6 @@ import android.view.View.AccessibilityDelegate; import com.android.launcher3.DeviceProfile; import com.android.launcher3.ItemInfo; -import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.dot.DotInfo; /** @@ -57,19 +56,6 @@ public interface ActivityContext { DeviceProfile getDeviceProfile(); - /** - * Device profile to be used by UI elements which are shown directly on top of the wallpaper - * and whose presentation is tied to the wallpaper (and physical device) and not the activity - * configuration. - */ - default DeviceProfile getWallpaperDeviceProfile() { - return getDeviceProfile(); - } - - default RotationMode getRotationMode() { - return RotationMode.NORMAL; - } - static ActivityContext lookupContext(Context context) { if (context instanceof ActivityContext) { return (ActivityContext) context; diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 3e2560f13c..ad8d69dfa7 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -92,8 +92,6 @@ public class FloatingIconView extends FrameLayout implements private ClipIconView mClipIconView; private @Nullable Drawable mBadge; - private float mRotation; - private View mOriginalIcon; private RectF mPositionOut; private Runnable mOnTargetChangeRunnable; @@ -194,18 +192,17 @@ public class FloatingIconView extends FrameLayout implements * @param positionOut Rect that will hold the size and position of v. */ private void matchPositionOf(Launcher launcher, View v, boolean isOpening, RectF positionOut) { - float rotation = getLocationBoundsForView(launcher, v, isOpening, positionOut); + getLocationBoundsForView(launcher, v, isOpening, positionOut); final InsettableFrameLayout.LayoutParams lp = new InsettableFrameLayout.LayoutParams( Math.round(positionOut.width()), Math.round(positionOut.height())); - updatePosition(rotation, positionOut, lp); + updatePosition(positionOut, lp); setLayoutParams(lp); mClipIconView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height)); } - private void updatePosition(float rotation, RectF pos, InsettableFrameLayout.LayoutParams lp) { - mRotation = rotation; + private void updatePosition(RectF pos, InsettableFrameLayout.LayoutParams lp) { mPositionOut.set(pos); lp.ignoreInsets = true; // Position the floating view exactly on top of the original @@ -228,7 +225,7 @@ public class FloatingIconView extends FrameLayout implements * - For DeepShortcutView, we return the bounds of the icon view. * - For BubbleTextView, we return the icon bounds. */ - private static float getLocationBoundsForView(Launcher launcher, View v, boolean isOpening, + private static void getLocationBoundsForView(Launcher launcher, View v, boolean isOpening, RectF outRect) { boolean ignoreTransform = !isOpening; if (v instanceof DeepShortcutView) { @@ -239,7 +236,7 @@ public class FloatingIconView extends FrameLayout implements ignoreTransform = false; } if (v == null) { - return 0; + return; } Rect iconBounds = new Rect(); @@ -253,15 +250,13 @@ public class FloatingIconView extends FrameLayout implements float[] points = new float[] {iconBounds.left, iconBounds.top, iconBounds.right, iconBounds.bottom}; - float[] rotation = new float[] {0}; Utilities.getDescendantCoordRelativeToAncestor(v, launcher.getDragLayer(), points, - false, ignoreTransform, rotation); + false, ignoreTransform); outRect.set( Math.min(points[0], points[2]), Math.min(points[1], points[3]), Math.max(points[0], points[2]), Math.max(points[1], points[3])); - return rotation[0]; } /** @@ -443,14 +438,10 @@ public class FloatingIconView extends FrameLayout implements @Override protected void dispatchDraw(Canvas canvas) { - int count = canvas.save(); - canvas.rotate(mRotation, - mFinalDrawableBounds.exactCenterX(), mFinalDrawableBounds.exactCenterY()); super.dispatchDraw(canvas); if (mBadge != null) { mBadge.draw(canvas); } - canvas.restoreToCount(count); } public void fastFinish() { @@ -487,11 +478,10 @@ public class FloatingIconView extends FrameLayout implements @Override public void onGlobalLayout() { if (mOriginalIcon.isAttachedToWindow() && mPositionOut != null) { - float rotation = getLocationBoundsForView(mLauncher, mOriginalIcon, mIsOpening, + getLocationBoundsForView(mLauncher, mOriginalIcon, mIsOpening, sTmpRectF); - if (rotation != mRotation || !sTmpRectF.equals(mPositionOut)) { - updatePosition(rotation, sTmpRectF, - (InsettableFrameLayout.LayoutParams) getLayoutParams()); + if (!sTmpRectF.equals(mPositionOut)) { + updatePosition(sTmpRectF, (InsettableFrameLayout.LayoutParams) getLayoutParams()); if (mOnTargetChangeRunnable != null) { mOnTargetChangeRunnable.run(); } diff --git a/src/com/android/launcher3/views/Transposable.java b/src/com/android/launcher3/views/Transposable.java deleted file mode 100644 index 929c1aa9b3..0000000000 --- a/src/com/android/launcher3/views/Transposable.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * 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.views; - -import com.android.launcher3.graphics.RotationMode; - -/** - * Indicates that a view can be transposed. - */ -public interface Transposable { - - RotationMode getRotationMode(); -} From 0fda3ba99c3e0ca185c8b6e94f71e7688ed31862 Mon Sep 17 00:00:00 2001 From: Andy Wickham Date: Fri, 3 Apr 2020 19:54:00 +0000 Subject: [PATCH 14/17] Removes WindowManager and DisplayManager from sandbox. Adds the back gesture panel directly to the fragment View hierarchy rather than a separate Window. Bug: 148542211 Change-Id: I196a72d29217308a5bdb78fdcff1face5d475379 --- .../BackGestureTutorialFragment.java | 4 +- .../interaction/EdgeBackGestureHandler.java | 72 +++++-------------- .../interaction/EdgeBackGesturePanel.java | 34 ++++----- 3 files changed, 31 insertions(+), 79 deletions(-) diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java index 593b6952dd..aeb718dceb 100644 --- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java +++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java @@ -107,11 +107,11 @@ public class BackGestureTutorialFragment extends Fragment implements BackGesture } void onAttachedToWindow() { - mEdgeBackGestureHandler.setIsEnabled(true); + mEdgeBackGestureHandler.setViewGroupParent((ViewGroup) getRootView()); } void onDetachedFromWindow() { - mEdgeBackGestureHandler.setIsEnabled(false); + mEdgeBackGestureHandler.setViewGroupParent(null); } @Override diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java index 04cd2f49d6..f34530ec8e 100644 --- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java +++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java @@ -17,21 +17,18 @@ package com.android.quickstep.interaction; import android.content.Context; import android.content.res.Resources; -import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PointF; -import android.hardware.display.DisplayManager; -import android.hardware.display.DisplayManager.DisplayListener; -import android.os.Handler; -import android.os.Looper; import android.os.SystemProperties; import android.view.Display; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewConfiguration; -import android.view.WindowManager; -import android.view.WindowManager.LayoutParams; +import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; + +import androidx.annotation.Nullable; import com.android.launcher3.ResourceUtils; @@ -40,7 +37,7 @@ import com.android.launcher3.ResourceUtils; * * Forked from platform/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java. */ -public class EdgeBackGestureHandler implements DisplayListener, OnTouchListener { +public class EdgeBackGestureHandler implements OnTouchListener { private static final String TAG = "EdgeBackGestureHandler"; private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt( @@ -106,29 +103,22 @@ public class EdgeBackGestureHandler implements DisplayListener, OnTouchListener mEdgeWidth = ResourceUtils.getNavbarSize("config_backGestureInset", res); } - void setIsEnabled(boolean isEnabled) { - if (isEnabled == mIsEnabled) { - return; - } - mIsEnabled = isEnabled; + void setViewGroupParent(@Nullable ViewGroup parent) { + mIsEnabled = parent != null; if (mEdgeBackPanel != null) { mEdgeBackPanel.onDestroy(); mEdgeBackPanel = null; } - if (!mIsEnabled) { - mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this); - } else { - updateDisplaySize(); - mContext.getSystemService(DisplayManager.class).registerDisplayListener(this, - new Handler(Looper.getMainLooper())); - + if (mIsEnabled) { // Add a nav bar panel window. - mEdgeBackPanel = new EdgeBackGesturePanel(mContext); + mEdgeBackPanel = new EdgeBackGesturePanel(mContext, parent, createLayoutParams()); mEdgeBackPanel.setBackCallback(mBackCallback); - mEdgeBackPanel.setLayoutParams(createLayoutParams()); - updateDisplaySize(); + if (mContext.getDisplay() != null) { + mContext.getDisplay().getRealSize(mDisplaySize); + mEdgeBackPanel.setDisplaySize(mDisplaySize); + } } } @@ -136,21 +126,11 @@ public class EdgeBackGestureHandler implements DisplayListener, OnTouchListener mGestureCallback = callback; } - private WindowManager.LayoutParams createLayoutParams() { + private LayoutParams createLayoutParams() { Resources resources = mContext.getResources(); - WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams( + return new LayoutParams( ResourceUtils.getNavbarSize("navigation_edge_panel_width", resources), - ResourceUtils.getNavbarSize("navigation_edge_panel_height", resources), - LayoutParams.TYPE_APPLICATION_PANEL, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH - | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, - PixelFormat.TRANSLUCENT); - layoutParams.setTitle(TAG + mDisplayId); - layoutParams.windowAnimations = 0; - layoutParams.setFitInsetsTypes(0 /* types */); - return layoutParams; + ResourceUtils.getNavbarSize("navigation_edge_panel_height", resources)); } @Override @@ -232,26 +212,6 @@ public class EdgeBackGestureHandler implements DisplayListener, OnTouchListener } } - @Override - public void onDisplayAdded(int displayId) { } - - @Override - public void onDisplayRemoved(int displayId) { } - - @Override - public void onDisplayChanged(int displayId) { - if (displayId == mDisplayId) { - updateDisplaySize(); - } - } - - private void updateDisplaySize() { - mContext.getDisplay().getRealSize(mDisplaySize); - if (mEdgeBackPanel != null) { - mEdgeBackPanel.setDisplaySize(mDisplaySize); - } - } - void setInsets(int leftInset, int rightInset) { mLeftInset = leftInset; mRightInset = rightInset; diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java index 34eeafc676..5bf50260aa 100644 --- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java +++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java @@ -26,11 +26,11 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.Point; import android.os.SystemClock; -import android.view.Gravity; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; -import android.view.WindowManager; +import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; @@ -110,7 +110,6 @@ public class EdgeBackGesturePanel extends View { private static final Interpolator RUBBER_BAND_INTERPOLATOR_APPEAR = new PathInterpolator(1.0f / RUBBER_BAND_AMOUNT_APPEAR, 1.0f, 1.0f, 1.0f); - private final WindowManager mWindowManager; private BackCallback mBackCallback; /** @@ -147,7 +146,6 @@ public class EdgeBackGesturePanel extends View { private VelocityTracker mVelocityTracker; private int mArrowPaddingEnd; - private WindowManager.LayoutParams mLayoutParams; /** * True if the panel is currently on the left of the screen @@ -232,11 +230,9 @@ public class EdgeBackGesturePanel extends View { } }; - public EdgeBackGesturePanel(Context context) { + public EdgeBackGesturePanel(Context context, ViewGroup parent, LayoutParams layoutParams) { super(context); - mWindowManager = context.getSystemService(WindowManager.class); - mDensity = context.getResources().getDisplayMetrics().density; mBaseTranslation = dp(BASE_TRANSLATION_DP); @@ -290,11 +286,15 @@ public class EdgeBackGesturePanel extends View { mSwipeThreshold = ResourceUtils.getDimenByName( "navigation_edge_action_drag_threshold", context.getResources(), 16 /* defaultValue */); + parent.addView(this, layoutParams); setVisibility(GONE); } void onDestroy() { - mWindowManager.removeView(this); + ViewGroup parent = (ViewGroup) getParent(); + if (parent != null) { + parent.removeView(this); + } } @Override @@ -305,9 +305,6 @@ public class EdgeBackGesturePanel extends View { @SuppressLint("RtlHardcoded") void setIsLeftPanel(boolean isLeftPanel) { mIsLeftPanel = isLeftPanel; - mLayoutParams.gravity = mIsLeftPanel - ? (Gravity.LEFT | Gravity.TOP) - : (Gravity.RIGHT | Gravity.TOP); } boolean getIsLeftPanel() { @@ -323,11 +320,6 @@ public class EdgeBackGesturePanel extends View { mBackCallback = callback; } - void setLayoutParams(WindowManager.LayoutParams layoutParams) { - mLayoutParams = layoutParams; - mWindowManager.addView(this, mLayoutParams); - } - private float getCurrentAngle() { return mCurrentAngle; } @@ -349,7 +341,6 @@ public class EdgeBackGesturePanel extends View { mStartY = event.getY(); setVisibility(VISIBLE); updatePosition(event.getY()); - mWindowManager.updateViewLayout(this, mLayoutParams); break; case MotionEvent.ACTION_MOVE: handleMoveEvent(event); @@ -614,10 +605,11 @@ public class EdgeBackGesturePanel extends View { } private void updatePosition(float touchY) { - float position = touchY - mFingerOffset; - position = Math.max(position, mMinArrowPosition); - position -= mLayoutParams.height / 2.0f; - mLayoutParams.y = MathUtils.clamp((int) position, 0, mDisplaySize.y); + float positionY = touchY - mFingerOffset; + positionY = Math.max(positionY, mMinArrowPosition); + positionY -= getLayoutParams().height / 2.0f; + setX(mIsLeftPanel ? 0 : mDisplaySize.x - getLayoutParams().width); + setY(MathUtils.clamp((int) positionY, 0, mDisplaySize.y)); } private void setDesiredVerticalTransition(float verticalTranslation, boolean animated) { From 1de8b5cfe6961559d6a5c48938ad01d386ffea39 Mon Sep 17 00:00:00 2001 From: thiruram Date: Thu, 2 Apr 2020 17:12:18 -0700 Subject: [PATCH 15/17] Fixes smart folder logging bug when creating new folder. Bug: 153159419 Change-Id: I5d41bdf6d83b091ee641a8160b026c6bff919417 --- src/com/android/launcher3/Launcher.java | 1 - src/com/android/launcher3/folder/Folder.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 046db3d9a3..03817ada09 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1700,7 +1700,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, public FolderIcon addFolder(CellLayout layout, int container, final int screenId, int cellX, int cellY) { final FolderInfo folderInfo = new FolderInfo(); - folderInfo.title = ""; // Update the model getModelWriter().addItemToDatabase(folderInfo, container, screenId, cellX, cellY); diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index c241dc2148..202836daa5 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -437,7 +437,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo } mItemsInvalidated = true; mInfo.addListener(this); - mPreviousLabel = mInfo.title.toString(); + Optional.ofNullable(mInfo.title).ifPresent(title -> mPreviousLabel = title.toString()); mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME); if (!isEmpty(mInfo.title)) { From 5edf9e2923f7a89a073a79d8e7d4d88ad524a297 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Fri, 27 Mar 2020 20:06:52 -0700 Subject: [PATCH 16/17] Update vertical drag handle (all apps arrow) - Change drawable to match specs, using ShadowDrawable to add shadow when necessary based on workspace theme. - New drawable is 18dp by 6dp; add support for different width vs height, and decouple from workspace page indicator (which is still 24dp tall). Bug: 151768994 Change-Id: Icfd0eac197ebc4d1f5bb799f8538c4bd99d800cd --- quickstep/res/values/dimens.xml | 3 -- .../android/quickstep/util/LayoutUtils.java | 5 +-- .../quickstep/views/ShelfScrimView.java | 20 +++++---- .../drag_handle_indicator_shadow.xml | 19 ++++++++ res/drawable/drag_handle_indicator.xml | 40 ----------------- .../drag_handle_indicator_no_shadow.xml | 29 +++++++++++++ res/layout/launcher.xml | 2 +- res/values/dimens.xml | 13 ++++-- res/values/drawables.xml | 1 + src/com/android/launcher3/DeviceProfile.java | 30 ++++++------- .../allapps/AllAppsTransitionController.java | 4 +- .../WorkspacePageIndicator.java | 2 +- .../android/launcher3/views/ScrimView.java | 43 ++++++++++++------- 13 files changed, 118 insertions(+), 93 deletions(-) create mode 100644 res/drawable-v24/drag_handle_indicator_shadow.xml delete mode 100644 res/drawable/drag_handle_indicator.xml create mode 100644 res/drawable/drag_handle_indicator_no_shadow.xml diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 8d42c4a6c4..dcc85d52cc 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -66,9 +66,6 @@ docked_stack_divider_thickness - 2 * docked_stack_divider_insets --> 10dp - - 24dp - 48dp diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java index ba99016561..1f1a99937e 100644 --- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java +++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java @@ -33,9 +33,6 @@ import com.android.quickstep.SysUINavigationMode; import java.lang.annotation.Retention; -import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS; -import static java.lang.annotation.RetentionPolicy.SOURCE; - public class LayoutUtils { private static final int MULTI_WINDOW_STRATEGY_HALF_SCREEN = 1; @@ -68,7 +65,7 @@ public class LayoutUtils { // UI when shown. extraSpace = 0; } else { - extraSpace = getDefaultSwipeHeight(context, dp) + dp.verticalDragHandleSizePx + extraSpace = getDefaultSwipeHeight(context, dp) + dp.workspacePageIndicatorHeight + res.getDimensionPixelSize( R.dimen.dynamic_grid_hotseat_extra_vertical_size) + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding); diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java index 14c458ed53..c2ccd90d62 100644 --- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java +++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java @@ -88,7 +88,6 @@ public class ShelfScrimView extends ScrimView private float mShiftRange; - private final float mShelfOffset; private float mTopOffset; private float mShelfTop; private float mShelfTopAtThreshold; @@ -110,7 +109,6 @@ public class ShelfScrimView extends ScrimView mRadius = BOTTOM_CORNER_RADIUS_RATIO * Themes.getDialogCornerRadius(context); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mShelfOffset = context.getResources().getDimension(R.dimen.shelf_surface_offset); // Just assume the easiest UI for now, until we have the proper layout information. mDrawingFlatColor = true; } @@ -179,7 +177,7 @@ public class ShelfScrimView extends ScrimView Math.min(hotseatSize, LayoutUtils.getDefaultSwipeHeight(context, dp)); mDragHandleProgress = 1 - (dragHandleTop / mShiftRange); } - mTopOffset = dp.getInsets().top - mShelfOffset; + mTopOffset = dp.getInsets().top - mDragHandleSize.y; mShelfTopAtThreshold = mShiftRange * SCRIM_CATCHUP_THRESHOLD + mTopOffset; } updateColors(); @@ -190,12 +188,15 @@ public class ShelfScrimView extends ScrimView @Override public void updateColors() { super.updateColors(); + mDragHandleOffset = 0; if (mDrawingFlatColor) { - mDragHandleOffset = 0; return; } - mDragHandleOffset = mShelfOffset - mDragHandleSize; + if (mProgress < mDragHandleProgress) { + mDragHandleOffset = mShiftRange * (mDragHandleProgress - mProgress); + } + if (mProgress >= SCRIM_CATCHUP_THRESHOLD) { mShelfTop = mShiftRange * mProgress + mTopOffset; } else { @@ -231,10 +232,6 @@ public class ShelfScrimView extends ScrimView (float) 0, LINEAR)); mRemainingScreenColor = setColorAlphaBound(mScrimColor, remainingScrimAlpha); } - - if (mProgress < mDragHandleProgress) { - mDragHandleOffset += mShiftRange * (mDragHandleProgress - mProgress); - } } @Override @@ -290,4 +287,9 @@ public class ShelfScrimView extends ScrimView mPaint.setColor(mShelfColor); canvas.drawRoundRect(0, mShelfTop, width, height + mRadius, mRadius, mRadius, mPaint); } + + @Override + public float getVisualTop() { + return mShelfTop; + } } diff --git a/res/drawable-v24/drag_handle_indicator_shadow.xml b/res/drawable-v24/drag_handle_indicator_shadow.xml new file mode 100644 index 0000000000..774bc38ceb --- /dev/null +++ b/res/drawable-v24/drag_handle_indicator_shadow.xml @@ -0,0 +1,19 @@ + + + diff --git a/res/drawable/drag_handle_indicator.xml b/res/drawable/drag_handle_indicator.xml deleted file mode 100644 index b01b84ab81..0000000000 --- a/res/drawable/drag_handle_indicator.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - diff --git a/res/drawable/drag_handle_indicator_no_shadow.xml b/res/drawable/drag_handle_indicator_no_shadow.xml new file mode 100644 index 0000000000..341e60cda9 --- /dev/null +++ b/res/drawable/drag_handle_indicator_no_shadow.xml @@ -0,0 +1,29 @@ + + + + + + + diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml index ab6c960be6..de13277b7f 100644 --- a/res/layout/launcher.xml +++ b/res/layout/launcher.xml @@ -57,7 +57,7 @@ diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 271511e6b2..0b589a2ed8 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -20,7 +20,6 @@ 8dp - 1dp 8dp 8dp @@ -36,10 +35,18 @@ 34dp 0dp + + 24dp + 1dp + 0dp + 4dp - 24dp - 0dp + 18dp + 6dp + 1dp + 48dp + 16dp 48dp diff --git a/res/values/drawables.xml b/res/values/drawables.xml index 9c57ec1214..7d631426cb 100644 --- a/res/values/drawables.xml +++ b/res/values/drawables.xml @@ -18,4 +18,5 @@ @drawable/ic_remove_no_shadow @drawable/ic_uninstall_no_shadow @drawable/ic_block_no_shadow + @drawable/drag_handle_indicator_no_shadow \ No newline at end of file diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index c0490696c9..4e1e5863b3 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -76,9 +76,9 @@ public class DeviceProfile { public float workspaceSpringLoadShrinkFactor; public final int workspaceSpringLoadedBottomSpace; - // Drag handle - public final int verticalDragHandleSizePx; - private final int verticalDragHandleOverlapWorkspace; + // Workspace page indicator + public final int workspacePageIndicatorHeight; + private final int mWorkspacePageIndicatorOverlapWorkspace; // Workspace icons public int iconSizePx; @@ -190,10 +190,10 @@ public class DeviceProfile { cellLayoutBottomPaddingPx = 0; } - verticalDragHandleSizePx = res.getDimensionPixelSize( - R.dimen.vertical_drag_handle_size); - verticalDragHandleOverlapWorkspace = - res.getDimensionPixelSize(R.dimen.vertical_drag_handle_overlap_workspace); + workspacePageIndicatorHeight = res.getDimensionPixelSize( + R.dimen.workspace_page_indicator_height); + mWorkspacePageIndicatorOverlapWorkspace = + res.getDimensionPixelSize(R.dimen.workspace_page_indicator_overlap_workspace); iconDrawablePaddingOriginalPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding); @@ -211,7 +211,7 @@ public class DeviceProfile { hotseatBarSidePaddingEndPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding); // Add a bit of space between nav bar and hotseat in vertical bar layout. - hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? verticalDragHandleSizePx : 0; + hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0; hotseatBarSizePx = ResourceUtils.pxFromDp(inv.iconSize, dm) + (isVerticalBarLayout() ? (hotseatBarSidePaddingStartPx + hotseatBarSidePaddingEndPx) : (res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size) @@ -227,7 +227,7 @@ public class DeviceProfile { // in portrait mode closer together by adding more height to the hotseat. // Note: This calculation was created after noticing a pattern in the design spec. int extraSpace = getCellSize().y - iconSizePx - iconDrawablePaddingPx * 2 - - verticalDragHandleSizePx; + - workspacePageIndicatorHeight; hotseatBarSizePx += extraSpace; hotseatBarBottomPaddingPx += extraSpace; @@ -376,7 +376,7 @@ public class DeviceProfile { if (!isVerticalLayout) { int expectedWorkspaceHeight = availableHeightPx - hotseatBarSizePx - - verticalDragHandleSizePx - edgeMarginPx; + - workspacePageIndicatorHeight - edgeMarginPx; float minRequiredHeight = dropTargetBarSizePx + workspaceSpringLoadedBottomSpace; workspaceSpringLoadShrinkFactor = Math.min( res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f, @@ -480,14 +480,14 @@ public class DeviceProfile { padding.bottom = edgeMarginPx; if (isSeascape()) { padding.left = hotseatBarSizePx; - padding.right = verticalDragHandleSizePx; + padding.right = hotseatBarSidePaddingStartPx; } else { - padding.left = verticalDragHandleSizePx; + padding.left = hotseatBarSidePaddingStartPx; padding.right = hotseatBarSizePx; } } else { - int paddingBottom = hotseatBarSizePx + verticalDragHandleSizePx - - verticalDragHandleOverlapWorkspace; + int paddingBottom = hotseatBarSizePx + workspacePageIndicatorHeight + - mWorkspacePageIndicatorOverlapWorkspace; if (isTablet) { // Pad the left and right of the workspace to ensure consistent spacing // between all icons @@ -554,7 +554,7 @@ public class DeviceProfile { mInsets.top + dropTargetBarSizePx + edgeMarginPx, mInsets.left + availableWidthPx - edgeMarginPx, mInsets.top + availableHeightPx - hotseatBarSizePx - - verticalDragHandleSizePx - edgeMarginPx); + - workspacePageIndicatorHeight - edgeMarginPx); } } diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 7600f527d0..00128eb033 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -123,8 +123,8 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil // Use a light system UI (dark icons) if all apps is behind at least half of the // status bar. - boolean forceChange = shiftCurrent - mScrimView.getDragHandleSize() - <= mLauncher.getDeviceProfile().getInsets().top / 2; + boolean forceChange = Math.min(shiftCurrent, mScrimView.getVisualTop()) + <= mLauncher.getDeviceProfile().getInsets().top / 2f; if (forceChange) { mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, !mIsDarkTheme); } else { diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java index 0f2ca72c0e..408796f8c7 100644 --- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java +++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java @@ -121,7 +121,7 @@ public class WorkspacePageIndicator extends View implements Insettable, PageIndi mLinePaint.setAlpha(0); mLauncher = Launcher.getLauncher(context); - mLineHeight = res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_line_height); + mLineHeight = res.getDimensionPixelSize(R.dimen.workspace_page_indicator_line_height); boolean darkText = WallpaperColorInfo.INSTANCE.get(context).supportsDarkText(); mActiveAlpha = darkText ? BLACK_ALPHA : WHITE_ALPHA; diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java index 6d204f6c5b..adad097217 100644 --- a/src/com/android/launcher3/views/ScrimView.java +++ b/src/com/android/launcher3/views/ScrimView.java @@ -33,8 +33,10 @@ import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.RectEvaluator; import android.content.Context; +import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; @@ -115,7 +117,9 @@ public class ScrimView extends View implements Insettable, O protected int mEndFlatColor; protected int mEndFlatColorAlpha; - protected final int mDragHandleSize; + protected final Point mDragHandleSize; + private final int mDragHandleTouchSize; + private final int mDragHandlePaddingInVerticalBarLayout; protected float mDragHandleOffset; private final Rect mDragHandleBounds; private final RectF mHitRect = new RectF(); @@ -136,9 +140,13 @@ public class ScrimView extends View implements Insettable, O mMaxScrimAlpha = 0.7f; - mDragHandleSize = context.getResources() - .getDimensionPixelSize(R.dimen.vertical_drag_handle_size); - mDragHandleBounds = new Rect(0, 0, mDragHandleSize, mDragHandleSize); + Resources res = context.getResources(); + mDragHandleSize = new Point(res.getDimensionPixelSize(R.dimen.vertical_drag_handle_width), + res.getDimensionPixelSize(R.dimen.vertical_drag_handle_height)); + mDragHandleBounds = new Rect(0, 0, mDragHandleSize.x, mDragHandleSize.y); + mDragHandleTouchSize = res.getDimensionPixelSize(R.dimen.vertical_drag_handle_touch_size); + mDragHandlePaddingInVerticalBarLayout = context.getResources() + .getDimensionPixelSize(R.dimen.vertical_drag_handle_padding_in_vertical_bar_layout); mAccessibilityHelper = createAccessibilityHelper(); ViewCompat.setAccessibilityDelegate(this, mAccessibilityHelper); @@ -297,24 +305,26 @@ public class ScrimView extends View implements Insettable, O DeviceProfile grid = mLauncher.getDeviceProfile(); final int left; final int width = getMeasuredWidth(); - final int top = getMeasuredHeight() - mDragHandleSize - grid.getInsets().bottom; + final int top = getMeasuredHeight() - mDragHandleSize.y - grid.getInsets().bottom; final int topMargin; if (grid.isVerticalBarLayout()) { - topMargin = grid.workspacePadding.bottom; + topMargin = grid.workspacePadding.bottom + mDragHandlePaddingInVerticalBarLayout; if (grid.isSeascape()) { - left = width - grid.getInsets().right - mDragHandleSize; + left = width - grid.getInsets().right - mDragHandleSize.x + - mDragHandlePaddingInVerticalBarLayout; } else { - left = mDragHandleSize + grid.getInsets().left; + left = grid.getInsets().left + mDragHandlePaddingInVerticalBarLayout; } } else { - left = (width - mDragHandleSize) / 2; + left = Math.round((width - mDragHandleSize.x) / 2f); topMargin = grid.hotseatBarSizePx; } mDragHandleBounds.offsetTo(left, top - topMargin); mHitRect.set(mDragHandleBounds); - float inset = -mDragHandleSize / 2; - mHitRect.inset(inset, inset); + // Inset outwards to increase touch size. + mHitRect.inset((mDragHandleSize.x - mDragHandleTouchSize) / 2f, + (mDragHandleSize.y - mDragHandleTouchSize) / 2f); if (mDragHandle != null) { mDragHandle.setBounds(mDragHandleBounds); @@ -341,7 +351,7 @@ public class ScrimView extends View implements Insettable, O if (visible != wasVisible) { if (visible) { mDragHandle = recycle != null ? recycle : - mLauncher.getDrawable(R.drawable.drag_handle_indicator); + mLauncher.getDrawable(R.drawable.drag_handle_indicator_shadow); mDragHandle.setBounds(mDragHandleBounds); updateDragHandleAlpha(); @@ -397,7 +407,7 @@ public class ScrimView extends View implements Insettable, O @Override protected int getVirtualViewAt(float x, float y) { - return mDragHandleBounds.contains((int) x, (int) y) + return mHitRect.contains((int) x, (int) y) ? DRAG_HANDLE_ID : INVALID_ID; } @@ -470,7 +480,10 @@ public class ScrimView extends View implements Insettable, O } } - public int getDragHandleSize() { - return mDragHandleSize; + /** + * @return The top of this scrim view, or {@link Float#MAX_VALUE} if there's no distinct top. + */ + public float getVisualTop() { + return Float.MAX_VALUE; } } From 490a8ab7c7a8bf4622c7531b0b372f38a64d6c67 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Wed, 1 Apr 2020 17:03:58 -0700 Subject: [PATCH 17/17] Add all apps education bounce animation - Update existing arrow bounce animation to repeat 3 times, and play it when swiping up from nav bar on first home screen as well as when tapping the arrow. Bug: 151768994 Change-Id: Ib120764fdeab6cd932018b6fed8b1093dda20641 --- .../quickstep/views/LauncherRecentsView.java | 3 +- src/com/android/launcher3/Launcher.java | 6 +- src/com/android/launcher3/Workspace.java | 2 +- .../allapps/AllAppsTransitionController.java | 4 +- .../android/launcher3/states/HintState.java | 10 +- .../android/launcher3/views/ScrimView.java | 119 ++++++++++++------ 6 files changed, 97 insertions(+), 47 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java index e62de186f8..98eb29aa67 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java @@ -42,7 +42,6 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.Hotseat; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager.StateListener; -import com.android.launcher3.R; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.appprediction.PredictionUiStateManager; import com.android.launcher3.appprediction.PredictionUiStateManager.Client; @@ -172,7 +171,7 @@ public class LauncherRecentsView extends RecentsView mActivity.getAllAppsController(), ALL_APPS_PROGRESS, allAppsProgressOffscreen)); ObjectAnimator dragHandleAnim = ObjectAnimator.ofInt( - mActivity.findViewById(R.id.scrim_view), ScrimView.DRAG_HANDLE_ALPHA, 0); + mActivity.getScrimView(), ScrimView.DRAG_HANDLE_ALPHA, 0); dragHandleAnim.setInterpolator(Interpolators.ACCEL_2); anim.play(dragHandleAnim); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 5b9f676bbd..fb8bd455c0 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1193,7 +1193,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, // Setup the drag controller (drop targets have to be added in reverse order in priority) mDropTargetBar.setup(mDragController); - mAllAppsController.setupViews(mAppsView); + mAllAppsController.setupViews(mAppsView, mScrimView); } /** @@ -1415,6 +1415,10 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, return mDropTargetBar; } + public ScrimView getScrimView() { + return mScrimView; + } + public LauncherAppWidgetHost getAppWidgetHost() { return mAppWidgetHost; } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 8bc0242920..190ec162ee 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -125,7 +125,7 @@ public class Workspace extends PagedView private static final int ADJACENT_SCREEN_DROP_DURATION = 300; - private static final int DEFAULT_PAGE = 0; + public static final int DEFAULT_PAGE = 0; private LayoutTransition mLayoutTransition; @Thunk final WallpaperManager mWallpaperManager; diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 00128eb033..68b070617d 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -212,9 +212,9 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil return AnimationSuccessListener.forRunnable(this::onProgressAnimationEnd); } - public void setupViews(AllAppsContainerView appsView) { + public void setupViews(AllAppsContainerView appsView, ScrimView scrimView) { mAppsView = appsView; - mScrimView = mLauncher.findViewById(R.id.scrim_view); + mScrimView = scrimView; } /** diff --git a/src/com/android/launcher3/states/HintState.java b/src/com/android/launcher3/states/HintState.java index 290dbb6baa..43f30f1e07 100644 --- a/src/com/android/launcher3/states/HintState.java +++ b/src/com/android/launcher3/states/HintState.java @@ -17,6 +17,7 @@ package com.android.launcher3.states; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; +import com.android.launcher3.LauncherStateManager; import com.android.launcher3.Workspace; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; @@ -50,8 +51,13 @@ public class HintState extends LauncherState { @Override public void onStateTransitionEnd(Launcher launcher) { - launcher.getStateManager().goToState(NORMAL); + LauncherStateManager stateManager = launcher.getStateManager(); Workspace workspace = launcher.getWorkspace(); - workspace.post(workspace::moveToDefaultScreen); + boolean willMoveScreens = workspace.getNextPage() != Workspace.DEFAULT_PAGE; + stateManager.goToState(NORMAL, true, willMoveScreens ? null + : launcher.getScrimView()::startDragHandleEducationAnim); + if (willMoveScreens) { + workspace.post(workspace::moveToDefaultScreen); + } } } diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java index adad097217..39e1eac758 100644 --- a/src/com/android/launcher3/views/ScrimView.java +++ b/src/com/android/launcher3/views/ScrimView.java @@ -22,8 +22,10 @@ import static androidx.core.graphics.ColorUtils.compositeColors; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; -import static com.android.launcher3.anim.Interpolators.ACCEL; +import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL; +import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.anim.Interpolators.clampToProgress; import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; import android.animation.Animator; @@ -100,6 +102,12 @@ public class ScrimView extends View implements Insettable, O private static final int SETTINGS = R.string.settings_button_text; private static final int ALPHA_CHANNEL_COUNT = 1; + private static final long DRAG_HANDLE_BOUNCE_DURATION_MS = 300; + // How much to delay before repeating the bounce. + private static final long DRAG_HANDLE_BOUNCE_DELAY_MS = 200; + // Repeat this many times (i.e. total number of bounces is 1 + this). + private static final int DRAG_HANDLE_BOUNCE_REPEAT_COUNT = 2; + private final Rect mTempRect = new Rect(); private final int[] mTempPos = new int[2]; @@ -123,6 +131,7 @@ public class ScrimView extends View implements Insettable, O protected float mDragHandleOffset; private final Rect mDragHandleBounds; private final RectF mHitRect = new RectF(); + private ObjectAnimator mDragHandleAnim; private final MultiValueAlpha mMultiValueAlpha; @@ -212,6 +221,7 @@ public class ScrimView extends View implements Insettable, O public void setProgress(float progress) { if (mProgress != progress) { mProgress = progress; + stopDragHandleEducationAnim(); updateColors(); updateDragHandleAlpha(); invalidate(); @@ -259,46 +269,77 @@ public class ScrimView extends View implements Insettable, O @Override public boolean onTouchEvent(MotionEvent event) { - boolean value = super.onTouchEvent(event); - if (!value && mDragHandle != null && event.getAction() == ACTION_DOWN - && mDragHandle.getAlpha() == 255 - && mHitRect.contains(event.getX(), event.getY())) { - - final Drawable drawable = mDragHandle; - mDragHandle = null; - - Rect bounds = new Rect(mDragHandleBounds); - bounds.offset(0, -(int) mDragHandleOffset); - drawable.setBounds(bounds); - - Rect topBounds = new Rect(bounds); - topBounds.offset(0, -bounds.height() / 2); - - Rect invalidateRegion = new Rect(bounds); - invalidateRegion.top = topBounds.top; - - Keyframe frameTop = Keyframe.ofObject(0.6f, topBounds); - frameTop.setInterpolator(DEACCEL); - Keyframe frameBot = Keyframe.ofObject(1, bounds); - frameBot.setInterpolator(ACCEL); - PropertyValuesHolder holder = PropertyValuesHolder .ofKeyframe("bounds", - Keyframe.ofObject(0, bounds), frameTop, frameBot); - holder.setEvaluator(new RectEvaluator()); - - ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(drawable, holder); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - getOverlay().remove(drawable); - updateDragHandleVisibility(drawable); + boolean superHandledTouch = super.onTouchEvent(event); + if (event.getAction() == ACTION_DOWN) { + if (!superHandledTouch && mHitRect.contains(event.getX(), event.getY())) { + if (startDragHandleEducationAnim()) { + return true; } - }); - anim.addUpdateListener((v) -> invalidate(invalidateRegion)); - getOverlay().add(drawable); - anim.start(); - return true; + } + stopDragHandleEducationAnim(); + } + return superHandledTouch; + } + + /** + * Animates the drag handle to demonstrate how to get to all apps. + * @return Whether the animation was started (false if drag handle is invisible). + */ + public boolean startDragHandleEducationAnim() { + stopDragHandleEducationAnim(); + + if (mDragHandle == null || mDragHandle.getAlpha() != 255) { + return false; + } + + final Drawable drawable = mDragHandle; + mDragHandle = null; + + Rect bounds = new Rect(mDragHandleBounds); + bounds.offset(0, -(int) mDragHandleOffset); + drawable.setBounds(bounds); + + Rect topBounds = new Rect(bounds); + topBounds.offset(0, -bounds.height()); + + Rect invalidateRegion = new Rect(bounds); + invalidateRegion.top = topBounds.top; + + final float progressToReachTop = 0.6f; + Keyframe frameTop = Keyframe.ofObject(progressToReachTop, topBounds); + frameTop.setInterpolator(DEACCEL); + Keyframe frameBot = Keyframe.ofObject(1, bounds); + frameBot.setInterpolator(ACCEL_DEACCEL); + PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("bounds", + Keyframe.ofObject(0, bounds), frameTop, frameBot); + holder.setEvaluator(new RectEvaluator()); + + mDragHandleAnim = ObjectAnimator.ofPropertyValuesHolder(drawable, holder); + long totalBounceDuration = DRAG_HANDLE_BOUNCE_DURATION_MS + DRAG_HANDLE_BOUNCE_DELAY_MS; + // The bounce finishes by this progress, the rest of the duration just delays next bounce. + float delayStartProgress = 1f - (float) DRAG_HANDLE_BOUNCE_DELAY_MS / totalBounceDuration; + mDragHandleAnim.addUpdateListener((v) -> invalidate(invalidateRegion)); + mDragHandleAnim.setDuration(totalBounceDuration); + mDragHandleAnim.setInterpolator(clampToProgress(LINEAR, 0, delayStartProgress)); + mDragHandleAnim.setRepeatCount(DRAG_HANDLE_BOUNCE_REPEAT_COUNT); + getOverlay().add(drawable); + + mDragHandleAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mDragHandleAnim = null; + getOverlay().remove(drawable); + updateDragHandleVisibility(drawable); + } + }); + mDragHandleAnim.start(); + return true; + } + + private void stopDragHandleEducationAnim() { + if (mDragHandleAnim != null) { + mDragHandleAnim.end(); } - return value; } protected void updateDragHandleBounds() {