From 7886c56a575842d27f976afc65caddcce67f85ec Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Mon, 6 May 2019 12:38:35 -0700 Subject: [PATCH 01/41] Reapply recents attached state in createActivityController() There were a couple cases where swiping up showed the adjacent task even though we had set it as detached from the app window. Test: - Launch an app from all apps, swipe to home - Force stop launcher, swipe to home Bug: 129985827 Change-Id: Ib8a836e3cc467d44be601d81e3a7d04d6487c968 --- .../android/quickstep/LauncherActivityControllerHelper.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java index 50f25fbf11..61774937f2 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java @@ -186,6 +186,12 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe @Override public void createActivityController(long transitionLength) { createActivityControllerInternal(activity, fromState, transitionLength, callback); + // Creating the activity controller animation sometimes reapplies the launcher state + // (because we set the animation as the current state animation), so we reapply the + // attached state here as well to ensure recents is shown/hidden appropriately. + if (SysUINavigationMode.getMode(activity) == Mode.NO_BUTTON) { + setRecentsAttachedToAppWindow(mIsAttachedToWindow, false); + } } @Override From fd968a79a89e1762f048cf1f6855be6351c3c559 Mon Sep 17 00:00:00 2001 From: Kevin Date: Fri, 10 May 2019 15:57:10 -0700 Subject: [PATCH 02/41] Scale down thumbnail with app surface The recents Go app to overview transition has app scale down to a thumbnail but normally covers the thumbnail. However, apps with transparency will be semi-visible and will allow the user to see the thumbnail in the back at its final size. Instead, we should fit the thumbnail to the surface so that they both scale down at the same time. Bug: 132458092 Test: Go to app with transparency, scale down Change-Id: Iaebeaaf2ddcfc86fd4f55ef9d8c3f19583947c48 --- go/quickstep/res/layout/task_item_view.xml | 3 +- .../quickstep/views/IconRecentsView.java | 42 +++++++++++++++++-- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/go/quickstep/res/layout/task_item_view.xml b/go/quickstep/res/layout/task_item_view.xml index 699178d57f..ab2cf2804b 100644 --- a/go/quickstep/res/layout/task_item_view.xml +++ b/go/quickstep/res/layout/task_item_view.xml @@ -18,7 +18,8 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="@dimen/task_item_height" - android:orientation="horizontal"> + android:orientation="horizontal" + android:clipChildren="false"> Date: Mon, 13 May 2019 15:15:04 -0700 Subject: [PATCH 03/41] Preemptively clear the thumbnail cache as tasks are removed - Also reset the task thumbnail as the task views are recycled Bug: 132309376 Change-Id: Ic2cc846e451b6b59ae76326930cb4b85627e95c4 --- .../src/com/android/quickstep/views/TaskView.java | 6 ++++++ .../src/com/android/quickstep/RecentTasksList.java | 10 ++++++++++ quickstep/src/com/android/quickstep/RecentsModel.java | 6 ++++++ .../src/com/android/quickstep/TaskThumbnailCache.java | 7 +++++++ 4 files changed, 29 insertions(+) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java index 6cd46d94a1..e14cddd0e8 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java @@ -474,6 +474,12 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { public void onRecycle() { resetViewTransforms(); setFullscreenProgress(0); + // Clear any references to the thumbnail (it will be re-read either from the cache or the + // system on next bind) + mSnapshotView.setThumbnail(mTask, null); + if (mTask != null) { + mTask.thumbnail = null; + } } @Override diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java index 06a36c9f0c..3538373122 100644 --- a/quickstep/src/com/android/quickstep/RecentTasksList.java +++ b/quickstep/src/com/android/quickstep/RecentTasksList.java @@ -119,6 +119,16 @@ public class RecentTasksList extends TaskStackChangeListener { mChangeId++; } + @Override + public void onTaskRemoved(int taskId) { + for (int i = mTasks.size() - 1; i >= 0; i--) { + if (mTasks.get(i).key.id == taskId) { + mTasks.remove(i); + return; + } + } + } + @Override public synchronized void onActivityPinned(String packageName, int userId, int taskId, int stackId) { diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java index 675cfe2ef6..9f12484589 100644 --- a/quickstep/src/com/android/quickstep/RecentsModel.java +++ b/quickstep/src/com/android/quickstep/RecentsModel.java @@ -166,6 +166,12 @@ public class RecentsModel extends TaskStackChangeListener { } } + @Override + public void onTaskRemoved(int taskId) { + Task.TaskKey dummyKey = new Task.TaskKey(taskId, 0, null, null, 0, 0); + mThumbnailCache.remove(dummyKey); + } + public void setSystemUiProxy(ISystemUiProxy systemUiProxy) { mSystemUiProxy = systemUiProxy; } diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java index d05196bc84..57c5a27833 100644 --- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java +++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java @@ -186,6 +186,13 @@ public class TaskThumbnailCache { mCache.evictAll(); } + /** + * Removes the cached thumbnail for the given task. + */ + public void remove(Task.TaskKey key) { + mCache.remove(key); + } + /** * @return The cache size. */ From 6e173752cf1839fc90b2b3d30763a52898a61ceb Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 13 May 2019 18:08:23 -0700 Subject: [PATCH 04/41] Adding TouchController for quickswitching on homescreen in transposed layout Bug: 130689544 Change-Id: I6126fbf29d9b4099d59dd8ad2ba4b4b9673bc46b --- .../uioverrides/RecentsUiFactory.java | 4 ++ .../OverviewToAllAppsTouchController.java | 3 +- .../QuickSwitchTouchController.java | 6 ++- .../TransposedQuickSwitchTouchController.java | 44 +++++++++++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java index 3a2958d54e..ebae1cd968 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java @@ -42,6 +42,7 @@ import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchCon import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController; import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchController; import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController; +import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController; import com.android.launcher3.util.TouchController; import com.android.launcher3.util.UiThreadHelper; import com.android.launcher3.util.UiThreadHelper.AsyncCommand; @@ -148,6 +149,9 @@ public abstract class RecentsUiFactory { if (launcher.getDeviceProfile().isVerticalBarLayout()) { list.add(new OverviewToAllAppsTouchController(launcher)); list.add(new LandscapeEdgeSwipeController(launcher)); + if (mode.hasGestures) { + list.add(new TransposedQuickSwitchTouchController(launcher)); + } } else { list.add(new PortraitStatesTouchController(launcher, mode.hasGestures /* allowDragToOverview */)); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java index 82ab34ba2c..73f328bc1d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java @@ -24,6 +24,7 @@ import android.view.MotionEvent; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; +import com.android.launcher3.Utilities; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.quickstep.TouchInteractionService; import com.android.quickstep.views.RecentsView; @@ -52,7 +53,7 @@ public class OverviewToAllAppsTouchController extends PortraitStatesTouchControl // In all-apps only listen if the container cannot scroll itself return mLauncher.getAppsView().shouldContainerScroll(ev); } else if (mLauncher.isInState(NORMAL)) { - return true; + return (ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) == 0; } else if (mLauncher.isInState(OVERVIEW)) { RecentsView rv = mLauncher.getOverviewPanel(); return ev.getY() > (rv.getBottom() - rv.getPaddingBottom()); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java index a1a790c2b6..e1dd124a9d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java @@ -57,7 +57,11 @@ public class QuickSwitchTouchController extends AbstractStateChangeTouchControll private @Nullable TaskView mTaskToLaunch; public QuickSwitchTouchController(Launcher launcher) { - super(launcher, SwipeDetector.HORIZONTAL); + this(launcher, SwipeDetector.HORIZONTAL); + } + + protected QuickSwitchTouchController(Launcher l, SwipeDetector.Direction dir) { + super(l, dir); } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java new file mode 100644 index 0000000000..f1e4041eb2 --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java @@ -0,0 +1,44 @@ +/* + * 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.uioverrides.touchcontrollers; + +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.touch.SwipeDetector; + +public class TransposedQuickSwitchTouchController extends QuickSwitchTouchController { + + public TransposedQuickSwitchTouchController(Launcher launcher) { + super(launcher, SwipeDetector.VERTICAL); + } + + @Override + protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) { + return super.getTargetState(fromState, + isDragTowardPositive ^ mLauncher.getDeviceProfile().isSeascape()); + } + + @Override + protected float initCurrentAnimation(int animComponents) { + float multiplier = super.initCurrentAnimation(animComponents); + return mLauncher.getDeviceProfile().isSeascape() ? multiplier : -multiplier; + } + + @Override + protected float getShiftRange() { + return mLauncher.getDeviceProfile().heightPx / 2f; + } +} From 3d13f303ee23a1ce01e81a450f71bcfe0cde025a Mon Sep 17 00:00:00 2001 From: vadimt Date: Fri, 10 May 2019 19:51:22 -0700 Subject: [PATCH 05/41] Using model-time scrolling in all apps This should solve flakes when the test thread wakes up too rarely to inject enough touch events. Change-Id: I461583d35eb4bfe0192b81c242aacf8d20e353d1 --- .../appprediction/AllAppsTipView.java | 4 +- src/com/android/launcher3/TestProtocol.java | 1 + .../allapps/AllAppsRecyclerView.java | 12 ++- .../launcher3/allapps/DiscoveryBounce.java | 6 +- .../compat/AccessibilityManagerCompat.java | 7 ++ .../com/android/launcher3/tapl/AllApps.java | 19 +++-- .../tapl/LauncherInstrumentation.java | 78 +++++++++++++++---- 7 files changed, 95 insertions(+), 32 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java index 948f39e0fc..d3042cf82e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java @@ -19,7 +19,6 @@ package com.android.launcher3.appprediction; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.quickstep.logging.UserEventDispatcherExtension.ALL_APPS_PREDICTION_TIPS; -import android.app.ActivityManager; import android.content.Context; import android.graphics.CornerPathEffect; import android.graphics.Paint; @@ -39,6 +38,7 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager; import com.android.launcher3.R; +import com.android.launcher3.Utilities; import com.android.launcher3.allapps.FloatingHeaderView; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.compat.UserManagerCompat; @@ -152,7 +152,7 @@ public class AllAppsTipView extends AbstractFloatingView { || !launcher.isInState(ALL_APPS) || hasSeenAllAppsTip(launcher) || UserManagerCompat.getInstance(launcher).isDemoUser() - || ActivityManager.isRunningInTestHarness()) { + || Utilities.IS_RUNNING_IN_TEST_HARNESS) { return false; } diff --git a/src/com/android/launcher3/TestProtocol.java b/src/com/android/launcher3/TestProtocol.java index ecbaa5bda8..eefecda5d7 100644 --- a/src/com/android/launcher3/TestProtocol.java +++ b/src/com/android/launcher3/TestProtocol.java @@ -24,6 +24,7 @@ public final class TestProtocol { public static final String SCROLL_Y_FIELD = "scrollY"; public static final String STATE_FIELD = "state"; public static final String SWITCHED_TO_STATE_MESSAGE = "TAPL_SWITCHED_TO_STATE"; + public static final String SCROLL_FINISHED_MESSAGE = "TAPL_SCROLL_FINISHED"; public static final String RESPONSE_MESSAGE_POSTFIX = "_RESPONSE"; public static final int NORMAL_STATE_ORDINAL = 0; public static final int SPRING_LOADED_STATE_ORDINAL = 1; diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index 180ca48166..548d5de0e0 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -32,7 +32,8 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; -import com.android.launcher3.graphics.DrawableFactory; +import com.android.launcher3.Utilities; +import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; @@ -419,4 +420,13 @@ public class AllAppsRecyclerView extends BaseRecyclerView implements LogContaine public boolean hasOverlappingRendering() { return false; } + + @Override + public void onScrollStateChanged(int state) { + super.onScrollStateChanged(state); + + if (state == SCROLL_STATE_IDLE && Utilities.IS_RUNNING_IN_TEST_HARNESS) { + AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext()); + } + } } diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java index 7467119b5f..1d62b435d8 100644 --- a/src/com/android/launcher3/allapps/DiscoveryBounce.java +++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java @@ -24,7 +24,6 @@ import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerTyp import android.animation.Animator; import android.animation.AnimatorInflater; import android.animation.AnimatorListenerAdapter; -import android.app.ActivityManager; import android.content.SharedPreferences; import android.os.Handler; import android.view.MotionEvent; @@ -32,6 +31,7 @@ import android.view.MotionEvent; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; import com.android.launcher3.R; +import com.android.launcher3.Utilities; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.states.InternalStateHandler; @@ -134,7 +134,7 @@ public class DiscoveryBounce extends AbstractFloatingView { && !shouldShowForWorkProfile(launcher)) || AbstractFloatingView.getTopOpenView(launcher) != null || UserManagerCompat.getInstance(launcher).isDemoUser() - || ActivityManager.isRunningInTestHarness()) { + || Utilities.IS_RUNNING_IN_TEST_HARNESS) { return; } @@ -159,7 +159,7 @@ public class DiscoveryBounce extends AbstractFloatingView { || (launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false) && !shouldShowForWorkProfile(launcher)) || UserManagerCompat.getInstance(launcher).isDemoUser() - || ActivityManager.isRunningInTestHarness()) { + || Utilities.IS_RUNNING_IN_TEST_HARNESS) { return; } diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java index b4d0c54fc6..86f773fa31 100644 --- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java +++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java @@ -62,6 +62,13 @@ public class AccessibilityManagerCompat { sendEventToTest(accessibilityManager, TestProtocol.SWITCHED_TO_STATE_MESSAGE, parcel); } + public static void sendScrollFinishedEventToTest(Context context) { + final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context); + if (accessibilityManager == null) return; + + sendEventToTest(accessibilityManager, TestProtocol.SCROLL_FINISHED_MESSAGE, null); + } + private static void sendEventToTest( AccessibilityManager accessibilityManager, String eventTag, Bundle data) { final AccessibilityEvent e = AccessibilityEvent.obtain( diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java index 68bdfe3270..7ad7b74318 100644 --- a/tests/tapl/com/android/launcher3/tapl/AllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java @@ -18,6 +18,8 @@ package com.android.launcher3.tapl; import static com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel.ZERO_BUTTON; +import android.graphics.Rect; + import androidx.annotation.NonNull; import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.Direction; @@ -32,7 +34,6 @@ import com.android.launcher3.TestProtocol; public class AllApps extends LauncherInstrumentation.VisibleContainer { private static final int MAX_SCROLL_ATTEMPTS = 40; private static final int MIN_INTERACT_SIZE = 100; - private static final int FLING_SPEED = LauncherInstrumentation.needSlowGestures() ? 1000 : 3000; private final int mHeight; @@ -102,7 +103,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { "search_container_all_apps"); int attempts = 0; - allAppsContainer.setGestureMargins(0, searchBox.getVisibleBounds().bottom + 1, 0, 5); + final Rect margins = new Rect(0, searchBox.getVisibleBounds().bottom + 1, 0, 5); for (int scroll = getScroll(allAppsContainer); scroll != 0; @@ -113,7 +114,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { "Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS, ++attempts <= MAX_SCROLL_ATTEMPTS); - allAppsContainer.scroll(Direction.UP, 1); + mLauncher.scrollWithModelTime(allAppsContainer, Direction.UP, 1, margins, 50); } try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled up")) { @@ -133,7 +134,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { // Try to figure out how much percentage of the container needs to be scrolled in order // to reveal the app icon to have the MIN_INTERACT_SIZE final float pct = Math.max(((float) (MIN_INTERACT_SIZE - appHeight)) / mHeight, 0.2f); - allAppsContainer.scroll(Direction.DOWN, pct); + mLauncher.scrollWithModelTime(allAppsContainer, Direction.DOWN, pct, null, 10); try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "scrolled an icon in all apps to make it visible - and then")) { mLauncher.waitForIdle(); @@ -150,9 +151,8 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { mLauncher.addContextLayer("want to fling forward in all apps")) { final UiObject2 allAppsContainer = verifyActiveContainer(); // Start the gesture in the center to avoid starting at elements near the top. - allAppsContainer.setGestureMargins(0, 0, 0, mHeight / 2); - allAppsContainer.fling(Direction.DOWN, - (int) (FLING_SPEED * mLauncher.getDisplayDensity())); + mLauncher.scrollWithModelTime( + allAppsContainer, Direction.DOWN, 1, new Rect(0, 0, 0, mHeight / 2), 10); verifyActiveContainer(); } } @@ -165,9 +165,8 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { mLauncher.addContextLayer("want to fling backward in all apps")) { final UiObject2 allAppsContainer = verifyActiveContainer(); // Start the gesture in the center, for symmetry with forward. - allAppsContainer.setGestureMargins(0, mHeight / 2, 0, 0); - allAppsContainer.fling(Direction.UP, - (int) (FLING_SPEED * mLauncher.getDisplayDensity())); + mLauncher.scrollWithModelTime( + allAppsContainer, Direction.UP, 1, new Rect(0, mHeight / 2, 0, 0), 10); verifyActiveContainer(); } } diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 4d8ff1bb2d..e5d27add2e 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -27,6 +27,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Point; +import android.graphics.Rect; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -46,6 +47,7 @@ import androidx.annotation.Nullable; import androidx.test.uiautomator.By; import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.Configurator; +import androidx.test.uiautomator.Direction; import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.UiObject2; import androidx.test.uiautomator.Until; @@ -360,7 +362,7 @@ public final class LauncherInstrumentation { ? NORMAL_STATE_ORDINAL : BACKGROUND_APP_STATE_ORDINAL; final Point displaySize = getRealDisplaySize(); - swipeViaMovePointer( + swipeWithModelTime( displaySize.x / 2, displaySize.y - 1, displaySize.x / 2, 0, finalState, ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME); @@ -548,18 +550,65 @@ public final class LauncherInstrumentation { () -> mDevice.swipe(startX, startY, endX, endY, steps)); } - void swipeViaMovePointer( + void swipeWithModelTime( int startX, int startY, int endX, int endY, int expectedState, int steps) { - changeStateViaGesture(startX, startY, endX, endY, expectedState, () -> { - final long downTime = SystemClock.uptimeMillis(); - final Point start = new Point(startX, startY); - final Point end = new Point(endX, endY); - sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start); - final long endTime = movePointer(downTime, downTime, steps * GESTURE_STEP_MS, start, - end); - sendPointer( - downTime, endTime, MotionEvent.ACTION_UP, end); - }); + changeStateViaGesture(startX, startY, endX, endY, expectedState, + () -> swipeWithModelTime(startX, startY, endX, endY, steps)); + } + + void scrollWithModelTime( + UiObject2 container, Direction direction, float percent, Rect margins, int steps) { + final Rect rect = container.getVisibleBounds(); + if (margins != null) { + rect.left += margins.left; + rect.top += margins.top; + rect.right -= margins.right; + rect.bottom -= margins.bottom; + } + + final int startX; + final int startY; + final int endX; + final int endY; + + switch (direction) { + case UP: { + startX = endX = rect.centerX(); + final int vertCenter = rect.centerY(); + final float halfGestureHeight = rect.height() * percent / 2.0f; + startY = (int) (vertCenter - halfGestureHeight); + endY = (int) (vertCenter + halfGestureHeight); + } + break; + case DOWN: { + startX = endX = rect.centerX(); + final int vertCenter = rect.centerY(); + final float halfGestureHeight = rect.height() * percent / 2.0f; + startY = (int) (vertCenter + halfGestureHeight); + endY = (int) (vertCenter - halfGestureHeight); + } + break; + default: + fail("Unsupported direction"); + return; + } + + executeAndWaitForEvent( + () -> swipeWithModelTime(startX, startY, endX, endY, steps), + event -> TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName()), + "Didn't receive a scroll end message: " + startX + ", " + startY + + ", " + endX + ", " + endY); + } + + // Inject a swipe gesture. Inject exactly 'steps' motion points, incrementing event time by a + // fixed interval each time. + private void swipeWithModelTime(int startX, int startY, int endX, int endY, int steps) { + final long downTime = SystemClock.uptimeMillis(); + final Point start = new Point(startX, startY); + final Point end = new Point(endX, endY); + sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start); + final long endTime = movePointer(downTime, downTime, steps * GESTURE_STEP_MS, start, end); + sendPointer(downTime, endTime, MotionEvent.ACTION_UP, end); } private void changeStateViaGesture(int startX, int startY, int endX, int endY, @@ -673,10 +722,7 @@ public final class LauncherInstrumentation { } static void sleep(int duration) { - try { - Thread.sleep(duration); - } catch (InterruptedException e) { - } + SystemClock.sleep(duration); } int getEdgeSensitivityWidth() { From 4b8b5fd4e4a8eef149d368cde0e4784b361bea43 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Tue, 14 May 2019 12:57:05 -0700 Subject: [PATCH 06/41] Center align foreground during app open, use springs during app close. Bug: 123900446 Change-Id: Ic4ffed28f6ba8782c3d8dfa031ebd70022c4c520 --- .../launcher3/views/FloatingIconView.java | 97 +++++++++++++++++-- 1 file changed, 87 insertions(+), 10 deletions(-) diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index cd0ae3d720..17bee6763a 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -62,6 +62,9 @@ import com.android.launcher3.shortcuts.DeepShortcutView; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; +import androidx.dynamicanimation.animation.FloatPropertyCompat; +import androidx.dynamicanimation.animation.SpringAnimation; +import androidx.dynamicanimation.animation.SpringForce; /** * A view that is created to look like another view with the purpose of creating fluid animations. @@ -76,6 +79,39 @@ public class FloatingIconView extends View implements private static final RectF sTmpRectF = new RectF(); private static final Object[] sTmpObjArray = new Object[1]; + // We spring the foreground drawable relative to the icon's movement in the DragLayer. + // We then use these two factor values to scale the movement of the fg within this view. + private static final int FG_TRANS_X_FACTOR = 200; + private static final int FG_TRANS_Y_FACTOR = 300; + + private static final FloatPropertyCompat mFgTransYProperty + = new FloatPropertyCompat("FloatingViewFgTransY") { + @Override + public float getValue(FloatingIconView view) { + return view.mFgTransY; + } + + @Override + public void setValue(FloatingIconView view, float transY) { + view.mFgTransY = transY; + view.invalidate(); + } + }; + + private static final FloatPropertyCompat mFgTransXProperty + = new FloatPropertyCompat("FloatingViewFgTransX") { + @Override + public float getValue(FloatingIconView view) { + return view.mFgTransX; + } + + @Override + public void setValue(FloatingIconView view, float transX) { + view.mFgTransX = transX; + view.invalidate(); + } + }; + private Runnable mEndRunnable; private CancellationSignal mLoadIconSignal; @@ -100,17 +136,30 @@ public class FloatingIconView extends View implements private final Rect mOutline = new Rect(); private final Rect mFinalDrawableBounds = new Rect(); - private final Rect mBgDrawableBounds = new Rect(); private AnimatorSet mFadeAnimatorSet; private ListenerView mListenerView; + private final SpringAnimation mFgSpringY; + private float mFgTransY; + private final SpringAnimation mFgSpringX; + private float mFgTransX; + private FloatingIconView(Launcher launcher) { super(launcher); mLauncher = launcher; mBlurSizeOutline = getResources().getDimensionPixelSize( R.dimen.blur_size_medium_outline); mListenerView = new ListenerView(launcher, null); + + mFgSpringX = new SpringAnimation(this, mFgTransXProperty) + .setSpring(new SpringForce() + .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY) + .setStiffness(SpringForce.STIFFNESS_LOW)); + mFgSpringY = new SpringAnimation(this, mFgTransYProperty) + .setSpring(new SpringForce() + .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY) + .setStiffness(SpringForce.STIFFNESS_LOW)); } @Override @@ -178,7 +227,31 @@ public class FloatingIconView extends View implements mRevealAnimator.setCurrentFraction(shapeRevealProgress); } - setBackgroundDrawableBounds(mOutline.height() / minSize); + float drawableScale = mOutline.height() / minSize; + setBackgroundDrawableBounds(drawableScale); + if (isOpening) { + // Center align foreground + int height = mFinalDrawableBounds.height(); + int width = mFinalDrawableBounds.width(); + int diffY = mIsVerticalBarLayout ? 0 + : (int) (((height * drawableScale) - height) / 2); + int diffX = mIsVerticalBarLayout ? (int) (((width * drawableScale) - width) / 2) + : 0; + sTmpRect.set(mFinalDrawableBounds); + sTmpRect.offset(diffX, diffY); + mForeground.setBounds(sTmpRect); + } else { + // Spring the foreground relative to the icon's movement within the DragLayer. + int diffX = (int) (dX / mLauncher.getDeviceProfile().availableWidthPx + * FG_TRANS_X_FACTOR); + int diffY = (int) (dY / mLauncher.getDeviceProfile().availableHeightPx + * FG_TRANS_Y_FACTOR); + + mFgSpringX.animateToFinalPosition(diffX); + mFgSpringY.animateToFinalPosition(diffY); + } + + } invalidate(); invalidateOutline(); @@ -393,17 +466,15 @@ public class FloatingIconView extends View implements } private void setBackgroundDrawableBounds(float scale) { - mBgDrawableBounds.set(mFinalDrawableBounds); - Utilities.scaleRectAboutCenter(mBgDrawableBounds, scale); + sTmpRect.set(mFinalDrawableBounds); + Utilities.scaleRectAboutCenter(sTmpRect, scale); // Since the drawable is at the top of the view, we need to offset to keep it centered. if (mIsVerticalBarLayout) { - mBgDrawableBounds.offsetTo((int) (mFinalDrawableBounds.left * scale), - mBgDrawableBounds.top); + sTmpRect.offsetTo((int) (mFinalDrawableBounds.left * scale), sTmpRect.top); } else { - mBgDrawableBounds.offsetTo(mBgDrawableBounds.left, - (int) (mFinalDrawableBounds.top * scale)); + sTmpRect.offsetTo(sTmpRect.left, (int) (mFinalDrawableBounds.top * scale)); } - mBackground.setBounds(mBgDrawableBounds); + mBackground.setBounds(sTmpRect); } @WorkerThread @@ -448,7 +519,10 @@ public class FloatingIconView extends View implements mBackground.draw(canvas); } if (mForeground != null) { + int count2 = canvas.save(); + canvas.translate(mFgTransX, mFgTransY); mForeground.draw(canvas); + canvas.restoreToCount(count2); } canvas.restoreToCount(count); } @@ -624,7 +698,6 @@ public class FloatingIconView extends View implements mBackground = null; mClipPath = null; mFinalDrawableBounds.setEmpty(); - mBgDrawableBounds.setEmpty(); if (mRevealAnimator != null) { mRevealAnimator.cancel(); } @@ -639,5 +712,9 @@ public class FloatingIconView extends View implements mOnTargetChangeRunnable = null; mTaskCornerRadius = 0; mOutline.setEmpty(); + mFgTransY = 0; + mFgSpringX.cancel(); + mFgTransX = 0; + mFgSpringY.cancel(); } } From d96bda552d7daae3c6ad2544c1e4549182d26c20 Mon Sep 17 00:00:00 2001 From: vadimt Date: Tue, 14 May 2019 13:48:15 -0700 Subject: [PATCH 07/41] Using model-time for swiping up from workspace to all apps Should eliminate some of flakes Change-Id: I7b75b6c66d9a6d561151491b0accde72b0dd3088 --- .../com/android/launcher3/tapl/LauncherInstrumentation.java | 2 +- tests/tapl/com/android/launcher3/tapl/Workspace.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 6801931e10..6d039d332b 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -617,7 +617,7 @@ public final class LauncherInstrumentation { // Inject a swipe gesture. Inject exactly 'steps' motion points, incrementing event time by a // fixed interval each time. - private void swipeWithModelTime(int startX, int startY, int endX, int endY, int steps) { + void swipeWithModelTime(int startX, int startY, int endX, int endY, int steps) { final long downTime = SystemClock.uptimeMillis(); final Point start = new Point(startX, startY); final Point end = new Point(endX, endY); diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index c0bafa2e7c..21a371c097 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -65,7 +65,8 @@ public final class Workspace extends Home { LauncherInstrumentation.log( "switchToAllApps: swipeHeight = " + swipeHeight + ", slop = " + mLauncher.getTouchSlop()); - mLauncher.swipe( + + mLauncher.swipeWithModelTime( start.x, start.y, start.x, From 04d29a61c9bee78a9c2c205b25c894b32d547618 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Tue, 14 May 2019 13:59:22 -0700 Subject: [PATCH 08/41] Recycle FloatingIconView for swipe up to home animation. Bug: 123900446 Change-Id: I63e900e86d921eddd3129ff68f895d6e8e7bff47 --- .../quickstep/LauncherActivityControllerHelper.java | 7 +++---- .../src/com/android/quickstep/views/RecentsView.java | 10 ++++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java index c33d25ccf6..434353d442 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java @@ -118,16 +118,15 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe } final RectF iconLocation = new RectF(); boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow(); - final FloatingIconView floatingView = canUseWorkspaceView - ? FloatingIconView.getFloatingIconView(activity, workspaceView, - true /* hideOriginal */, iconLocation, false /* isOpening */, null /* recycle */) + FloatingIconView floatingIconView = canUseWorkspaceView + ? recentsView.getFloatingIconView(activity, workspaceView, iconLocation) : null; return new HomeAnimationFactory() { @Nullable @Override public View getFloatingView() { - return floatingView; + return floatingIconView; } @NonNull 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 525ead836a..bded5ba4e5 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 @@ -75,6 +75,7 @@ import com.android.launcher3.BaseActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Insettable; import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAnimUtils.ViewProgressProperty; import com.android.launcher3.PagedView; import com.android.launcher3.R; @@ -91,6 +92,7 @@ import com.android.launcher3.util.OverScroller; import com.android.launcher3.util.PendingAnimation; import com.android.launcher3.util.Themes; import com.android.launcher3.util.ViewPool; +import com.android.launcher3.views.FloatingIconView; import com.android.quickstep.RecentsAnimationWrapper; import com.android.quickstep.RecentsModel; import com.android.quickstep.RecentsModel.TaskThumbnailChangeListener; @@ -288,6 +290,8 @@ public abstract class RecentsView extends PagedView impl private Layout mEmptyTextLayout; private LiveTileOverlay mLiveTileOverlay; + private FloatingIconView mFloatingIconView; + private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener = (inMultiWindowMode) -> { if (!inMultiWindowMode && mOverviewStateEnabled) { @@ -1704,4 +1708,10 @@ public abstract class RecentsView extends PagedView impl return super::onTouchEvent; } } + + public FloatingIconView getFloatingIconView(Launcher launcher, View view, RectF iconLocation) { + mFloatingIconView = FloatingIconView.getFloatingIconView(launcher, view, + true /* hideOriginal */, iconLocation, false /* isOpening */, mFloatingIconView); + return mFloatingIconView; + } } From 02877d0847a8ed3f0f5ad4877585bbeafbe5392c Mon Sep 17 00:00:00 2001 From: Tracy Zhou Date: Tue, 14 May 2019 13:22:56 -0700 Subject: [PATCH 09/41] Remove setFullscreenProgress() call in TaskView#onRecycle When onRecycle() is called, the current task view is detached from the parent, causing getParent() to return null. Test: manual Fixes: 132269977 Change-Id: I80826c2348cb23363c0482d3fd12283a7c90a689 --- .../src/com/android/quickstep/views/TaskView.java | 1 - 1 file changed, 1 deletion(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java index c4e1cce3ea..c67058d988 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java @@ -475,7 +475,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { @Override public void onRecycle() { resetViewTransforms(); - setFullscreenProgress(0); // Clear any references to the thumbnail (it will be re-read either from the cache or the // system on next bind) mSnapshotView.setThumbnail(mTask, null); From a03f96caef8b9b26876e1e9e9f797fac4bd1ecd1 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Tue, 14 May 2019 14:21:01 -0700 Subject: [PATCH 10/41] Tone down foreground springs until they are properly tuned. Change-Id: I8c66bc9b4828da82d5dcf74d1479795c4d75ed7c --- src/com/android/launcher3/views/FloatingIconView.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 17bee6763a..1a432a7ed0 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -81,8 +81,8 @@ public class FloatingIconView extends View implements // We spring the foreground drawable relative to the icon's movement in the DragLayer. // We then use these two factor values to scale the movement of the fg within this view. - private static final int FG_TRANS_X_FACTOR = 200; - private static final int FG_TRANS_Y_FACTOR = 300; + private static final int FG_TRANS_X_FACTOR = 80; + private static final int FG_TRANS_Y_FACTOR = 100; private static final FloatPropertyCompat mFgTransYProperty = new FloatPropertyCompat("FloatingViewFgTransY") { From de34dd5303ecb34116c0e86153dfabfa9b170ac2 Mon Sep 17 00:00:00 2001 From: Tony Date: Tue, 14 May 2019 17:36:30 -0500 Subject: [PATCH 11/41] Disallow pause when swiping up 75% of the way to all apps Test: TaplTestsQuickstep Bug: 131231579 Change-Id: If936007a5033ca5e349fd669c9377302239cbe22 --- .../touchcontrollers/FlingAndHoldTouchController.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java index 63581092b9..7a6cd2d550 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java @@ -43,9 +43,11 @@ import com.android.quickstep.views.RecentsView; public class FlingAndHoldTouchController extends PortraitStatesTouchController { private static final long PEEK_ANIM_DURATION = 100; + private static final float MAX_DISPLACEMENT_PERCENT = 0.75f; private final MotionPauseDetector mMotionPauseDetector; private final float mMotionPauseMinDisplacement; + private final float mMotionPauseMaxDisplacement; private AnimatorSet mPeekAnim; @@ -53,6 +55,7 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { super(l, false /* allowDragToOverview */); mMotionPauseDetector = new MotionPauseDetector(l); mMotionPauseMinDisplacement = ViewConfiguration.get(l).getScaledTouchSlop(); + mMotionPauseMaxDisplacement = getShiftRange() * MAX_DISPLACEMENT_PERCENT; } @Override @@ -101,7 +104,9 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { @Override public boolean onDrag(float displacement, MotionEvent event) { - mMotionPauseDetector.setDisallowPause(-displacement < mMotionPauseMinDisplacement); + float upDisplacement = -displacement; + mMotionPauseDetector.setDisallowPause(upDisplacement < mMotionPauseMinDisplacement + || upDisplacement > mMotionPauseMaxDisplacement); mMotionPauseDetector.addPosition(displacement, event.getEventTime()); return super.onDrag(displacement, event); } From 1824b562b28d401d41326f34a6ec2d00396ee422 Mon Sep 17 00:00:00 2001 From: vadimt Date: Tue, 14 May 2019 17:49:28 -0700 Subject: [PATCH 12/41] Using model-time for Overview.switchToAllApps Change-Id: I35efcc2f04d4d31f9b2a8d42a3fce29c481951f5 --- .../tapl/com/android/launcher3/tapl/AllApps.java | 8 ++++---- .../launcher3/tapl/LauncherInstrumentation.java | 16 +++++++--------- .../com/android/launcher3/tapl/Overview.java | 4 ++-- .../com/android/launcher3/tapl/Workspace.java | 6 +++--- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java index 7ad7b74318..a296975c3c 100644 --- a/tests/tapl/com/android/launcher3/tapl/AllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java @@ -114,7 +114,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { "Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS, ++attempts <= MAX_SCROLL_ATTEMPTS); - mLauncher.scrollWithModelTime(allAppsContainer, Direction.UP, 1, margins, 50); + mLauncher.scroll(allAppsContainer, Direction.UP, 1, margins, 50); } try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled up")) { @@ -134,7 +134,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { // Try to figure out how much percentage of the container needs to be scrolled in order // to reveal the app icon to have the MIN_INTERACT_SIZE final float pct = Math.max(((float) (MIN_INTERACT_SIZE - appHeight)) / mHeight, 0.2f); - mLauncher.scrollWithModelTime(allAppsContainer, Direction.DOWN, pct, null, 10); + mLauncher.scroll(allAppsContainer, Direction.DOWN, pct, null, 10); try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "scrolled an icon in all apps to make it visible - and then")) { mLauncher.waitForIdle(); @@ -151,7 +151,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { mLauncher.addContextLayer("want to fling forward in all apps")) { final UiObject2 allAppsContainer = verifyActiveContainer(); // Start the gesture in the center to avoid starting at elements near the top. - mLauncher.scrollWithModelTime( + mLauncher.scroll( allAppsContainer, Direction.DOWN, 1, new Rect(0, 0, 0, mHeight / 2), 10); verifyActiveContainer(); } @@ -165,7 +165,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { mLauncher.addContextLayer("want to fling backward in all apps")) { final UiObject2 allAppsContainer = verifyActiveContainer(); // Start the gesture in the center, for symmetry with forward. - mLauncher.scrollWithModelTime( + mLauncher.scroll( allAppsContainer, Direction.UP, 1, new Rect(0, mHeight / 2, 0, 0), 10); verifyActiveContainer(); } diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 6d039d332b..db447ee46a 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -365,10 +365,10 @@ public final class LauncherInstrumentation { ? NORMAL_STATE_ORDINAL : BACKGROUND_APP_STATE_ORDINAL; final Point displaySize = getRealDisplaySize(); - swipeWithModelTime( + swipeToState( displaySize.x / 2, displaySize.y - 1, displaySize.x / 2, 0, - finalState, ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME); + ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME, finalState); } } else { log(action = "clicking home button"); @@ -565,14 +565,12 @@ public final class LauncherInstrumentation { () -> mDevice.swipe(startX, startY, endX, endY, steps)); } - void swipeWithModelTime( - int startX, int startY, int endX, int endY, int expectedState, int steps) { + void swipeToState(int startX, int startY, int endX, int endY, int steps, int expectedState) { changeStateViaGesture(startX, startY, endX, endY, expectedState, - () -> swipeWithModelTime(startX, startY, endX, endY, steps)); + () -> linearGesture(startX, startY, endX, endY, steps)); } - void scrollWithModelTime( - UiObject2 container, Direction direction, float percent, Rect margins, int steps) { + void scroll(UiObject2 container, Direction direction, float percent, Rect margins, int steps) { final Rect rect = container.getVisibleBounds(); if (margins != null) { rect.left += margins.left; @@ -609,7 +607,7 @@ public final class LauncherInstrumentation { } executeAndWaitForEvent( - () -> swipeWithModelTime(startX, startY, endX, endY, steps), + () -> linearGesture(startX, startY, endX, endY, steps), event -> TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName()), "Didn't receive a scroll end message: " + startX + ", " + startY + ", " + endX + ", " + endY); @@ -617,7 +615,7 @@ public final class LauncherInstrumentation { // Inject a swipe gesture. Inject exactly 'steps' motion points, incrementing event time by a // fixed interval each time. - void swipeWithModelTime(int startX, int startY, int endX, int endY, int steps) { + private void linearGesture(int startX, int startY, int endX, int endY, int steps) { final long downTime = SystemClock.uptimeMillis(); final Point start = new Point(startX, startY); final Point end = new Point(endX, endY); diff --git a/tests/tapl/com/android/launcher3/tapl/Overview.java b/tests/tapl/com/android/launcher3/tapl/Overview.java index e625510878..08d974c99a 100644 --- a/tests/tapl/com/android/launcher3/tapl/Overview.java +++ b/tests/tapl/com/android/launcher3/tapl/Overview.java @@ -52,10 +52,10 @@ public final class Overview extends BaseOverview { // Swipe from the prediction row to the top. LauncherInstrumentation.log("Overview.switchToAllApps before swipe"); final UiObject2 predictionRow = mLauncher.waitForLauncherObject("prediction_row"); - mLauncher.swipe(mLauncher.getDevice().getDisplayWidth() / 2, + mLauncher.swipeToState(mLauncher.getDevice().getDisplayWidth() / 2, predictionRow.getVisibleBounds().centerY(), mLauncher.getDevice().getDisplayWidth() / 2, - 0, ALL_APPS_STATE_ORDINAL); + 0, 50, ALL_APPS_STATE_ORDINAL); try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( "swiped all way up from overview")) { diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index 21a371c097..3cab1a1ad8 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -66,13 +66,13 @@ public final class Workspace extends Home { "switchToAllApps: swipeHeight = " + swipeHeight + ", slop = " + mLauncher.getTouchSlop()); - mLauncher.swipeWithModelTime( + mLauncher.swipeToState( start.x, start.y, start.x, start.y - swipeHeight - mLauncher.getTouchSlop(), - ALL_APPS_STATE_ORDINAL - ); + 60, + ALL_APPS_STATE_ORDINAL); try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( "swiped to all apps")) { From 18862453596d73b8dd92f86bbb9cc63bad7e17e8 Mon Sep 17 00:00:00 2001 From: vadimt Date: Tue, 14 May 2019 18:55:37 -0700 Subject: [PATCH 13/41] Swipe from all apps from overview starts on an icon In some configs, Overview has prediction row, in others hotseat. So swiping from an icon in either. Change-Id: I98db3df4f612fcd9d5b7057835c49ca58313210f --- .../launcher3/tapl/LauncherInstrumentation.java | 12 ++++++++++++ tests/tapl/com/android/launcher3/tapl/Overview.java | 7 ++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index db447ee46a..ec62188950 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -531,6 +531,14 @@ public final class LauncherInstrumentation { return object; } + @NonNull + UiObject2 waitForLauncherObjectByClass(String clazz) { + final BySelector selector = getLauncherObjectSelectorByClass(clazz); + final UiObject2 object = mDevice.wait(Until.findObject(selector), WAIT_TIME_MS); + assertNotNull("Can't find a launcher object; selector: " + selector, object); + return object; + } + @NonNull UiObject2 waitForFallbackLauncherObject(String resName) { final BySelector selector = getFallbackLauncherObjectSelector(resName); @@ -543,6 +551,10 @@ public final class LauncherInstrumentation { return By.res(getLauncherPackageName(), resName); } + BySelector getLauncherObjectSelectorByClass(String clazz) { + return By.pkg(getLauncherPackageName()).clazz(clazz); + } + BySelector getFallbackLauncherObjectSelector(String resName) { return By.res(getOverviewPackageName(), resName); } diff --git a/tests/tapl/com/android/launcher3/tapl/Overview.java b/tests/tapl/com/android/launcher3/tapl/Overview.java index 08d974c99a..ec99d26c19 100644 --- a/tests/tapl/com/android/launcher3/tapl/Overview.java +++ b/tests/tapl/com/android/launcher3/tapl/Overview.java @@ -49,11 +49,12 @@ public final class Overview extends BaseOverview { "want to switch from overview to all apps")) { verifyActiveContainer(); - // Swipe from the prediction row to the top. + // Swipe from an app icon to the top. LauncherInstrumentation.log("Overview.switchToAllApps before swipe"); - final UiObject2 predictionRow = mLauncher.waitForLauncherObject("prediction_row"); + final UiObject2 appIcon = mLauncher.waitForLauncherObjectByClass( + "android.widget.TextView"); mLauncher.swipeToState(mLauncher.getDevice().getDisplayWidth() / 2, - predictionRow.getVisibleBounds().centerY(), + appIcon.getVisibleBounds().centerY(), mLauncher.getDevice().getDisplayWidth() / 2, 0, 50, ALL_APPS_STATE_ORDINAL); From 170a067f270854b07dfe049868d48651d2be39e4 Mon Sep 17 00:00:00 2001 From: Tony Date: Mon, 13 May 2019 18:48:59 -0500 Subject: [PATCH 14/41] Capture screenshot when animating to home This is to ensure the task thumbnail is captured in the correct orientation, rather than waiting until after the task pauses which might be after the device rotates to portrait. Also update the state to wait until screenshot is captured before finishing the transition to home. Change-Id: Ie42778d527c382ff80a2edf5d2a5dc7490e4e5ff --- .../android/quickstep/WindowTransformSwipeHandler.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java index 404cfe62ff..537858d9b7 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -170,7 +170,8 @@ public class WindowTransformSwipeHandler STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED; public enum GestureEndTarget { - HOME(1, STATE_SCALED_CONTROLLER_HOME, true, false, ContainerType.WORKSPACE, false), + HOME(1, STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT, true, false, + ContainerType.WORKSPACE, false), RECENTS(1, STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT | STATE_SCREENSHOT_VIEW_SHOWN, true, false, ContainerType.TASKSWITCHER, true), @@ -330,9 +331,8 @@ public class WindowTransformSwipeHandler | STATE_SCALED_CONTROLLER_RECENTS, this::finishCurrentTransitionToRecents); - mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_GESTURE_COMPLETED - | STATE_SCALED_CONTROLLER_HOME | STATE_APP_CONTROLLER_RECEIVED - | STATE_LAUNCHER_DRAWN, + mStateCallback.addCallback(STATE_SCREENSHOT_CAPTURED | STATE_GESTURE_COMPLETED + | STATE_SCALED_CONTROLLER_HOME, this::finishCurrentTransitionToHome); mStateCallback.addCallback(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED, this::reset); From 4028575ba0082a6d5ac2bff1a1800004d47b2637 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Tue, 14 May 2019 15:17:30 -0700 Subject: [PATCH 15/41] Fade in badges on top of icons after swipe up animation. Bug: 123900446 Change-Id: I54e367e0be72781e24d13ec6ea64dbddd85fb0bd --- src/com/android/launcher3/Utilities.java | 62 ++++++++++++++++++ .../android/launcher3/dragndrop/DragView.java | 63 +------------------ .../launcher3/views/FloatingIconView.java | 25 +++++++- 3 files changed, 88 insertions(+), 62 deletions(-) diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 02fc84b9a9..732aa9587b 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -17,6 +17,7 @@ package com.android.launcher3; import android.animation.ValueAnimator; +import android.annotation.TargetApi; import android.app.ActivityManager; import android.app.WallpaperManager; import android.content.BroadcastReceiver; @@ -32,12 +33,16 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.ShortcutInfo; import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; +import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.InsetDrawable; import android.os.Build; import android.os.Bundle; import android.os.DeadObjectException; @@ -62,6 +67,7 @@ import com.android.launcher3.compat.ShortcutConfigActivityInfo; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.FolderAdaptiveIcon; import com.android.launcher3.graphics.RotationMode; +import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.shortcuts.DeepShortcutManager; import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.util.IntArray; @@ -82,6 +88,8 @@ import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static com.android.launcher3.ItemInfoWithIcon.FLAG_ICON_BADGED; + /** * Various utilities shared amongst the Launcher's classes. */ @@ -654,6 +662,40 @@ public final class Utilities { } } + /** + * For apps icons and shortcut icons that have badges, this method creates a drawable that can + * later on be rendered on top of the layers for the badges. For app icons, work profile badges + * can only be applied. For deep shortcuts, when dragged from the pop up container, there's no + * badge. When dragged from workspace or folder, it may contain app AND/OR work profile badge + **/ + @TargetApi(Build.VERSION_CODES.O) + public static Drawable getBadge(Launcher launcher, ItemInfo info, Object obj) { + LauncherAppState appState = LauncherAppState.getInstance(launcher); + int iconSize = appState.getInvariantDeviceProfile().iconBitmapSize; + if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { + boolean iconBadged = (info instanceof ItemInfoWithIcon) + && (((ItemInfoWithIcon) info).runtimeStatusFlags & FLAG_ICON_BADGED) > 0; + if ((info.id == ItemInfo.NO_ID && !iconBadged) + || !(obj instanceof ShortcutInfo)) { + // The item is not yet added on home screen. + return new FixedSizeEmptyDrawable(iconSize); + } + ShortcutInfo si = (ShortcutInfo) obj; + LauncherIcons li = LauncherIcons.obtain(appState.getContext()); + Bitmap badge = li.getShortcutInfoBadge(si, appState.getIconCache()).iconBitmap; + li.recycle(); + float badgeSize = launcher.getResources().getDimension(R.dimen.profile_badge_size); + float insetFraction = (iconSize - badgeSize) / iconSize; + return new InsetDrawable(new FastBitmapDrawable(badge), + insetFraction, insetFraction, 0, 0); + } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) { + return ((FolderAdaptiveIcon) obj).getBadge(); + } else { + return launcher.getPackageManager() + .getUserBadgedIcon(new FixedSizeEmptyDrawable(iconSize), info.user); + } + } + public static int[] getIntArrayFromString(String tokenized) { StringTokenizer tokenizer = new StringTokenizer(tokenized, ","); int[] array = new int[tokenizer.countTokens()]; @@ -672,4 +714,24 @@ public final class Utilities { } return str.toString(); } + + private static class FixedSizeEmptyDrawable extends ColorDrawable { + + private final int mSize; + + public FixedSizeEmptyDrawable(int size) { + super(Color.TRANSPARENT); + mSize = size; + } + + @Override + public int getIntrinsicHeight() { + return mSize; + } + + @Override + public int getIntrinsicWidth() { + return mSize; + } + } } diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java index 3ab97b0d92..77b2cdc34d 100644 --- a/src/com/android/launcher3/dragndrop/DragView.java +++ b/src/com/android/launcher3/dragndrop/DragView.java @@ -16,7 +16,7 @@ package com.android.launcher3.dragndrop; -import static com.android.launcher3.ItemInfoWithIcon.FLAG_ICON_BADGED; +import static com.android.launcher3.Utilities.getBadge; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -24,7 +24,6 @@ import android.animation.FloatArrayEvaluator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.TargetApi; -import android.content.pm.ShortcutInfo; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; @@ -37,7 +36,6 @@ import android.graphics.Rect; import android.graphics.drawable.AdaptiveIconDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; -import android.graphics.drawable.InsetDrawable; import android.os.Build; import android.os.Handler; import android.os.Looper; @@ -45,9 +43,7 @@ import android.view.View; import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.ItemInfo; -import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; @@ -195,7 +191,6 @@ public class DragView extends View { new Handler(workerLooper).postAtFrontOfQueue(new Runnable() { @Override public void run() { - LauncherAppState appState = LauncherAppState.getInstance(mLauncher); Object[] outObj = new Object[1]; int w = mBitmap.getWidth(); int h = mBitmap.getHeight(); @@ -211,7 +206,7 @@ public class DragView extends View { // Badge is applied after icon normalization so the bounds for badge should not // be scaled down due to icon normalization. Rect badgeBounds = new Rect(bounds); - mBadge = getBadge(info, appState, outObj[0]); + mBadge = getBadge(mLauncher, info, outObj[0]); mBadge.setBounds(badgeBounds); // Do not draw the background in case of folder as its translucent @@ -307,40 +302,6 @@ public class DragView extends View { invalidate(); } - /** - * For apps icons and shortcut icons that have badges, this method creates a drawable that can - * later on be rendered on top of the layers for the badges. For app icons, work profile badges - * can only be applied. For deep shortcuts, when dragged from the pop up container, there's no - * badge. When dragged from workspace or folder, it may contain app AND/OR work profile badge - **/ - - @TargetApi(Build.VERSION_CODES.O) - private Drawable getBadge(ItemInfo info, LauncherAppState appState, Object obj) { - int iconSize = appState.getInvariantDeviceProfile().iconBitmapSize; - if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { - boolean iconBadged = (info instanceof ItemInfoWithIcon) - && (((ItemInfoWithIcon) info).runtimeStatusFlags & FLAG_ICON_BADGED) > 0; - if ((info.id == ItemInfo.NO_ID && !iconBadged) - || !(obj instanceof ShortcutInfo)) { - // The item is not yet added on home screen. - return new FixedSizeEmptyDrawable(iconSize); - } - ShortcutInfo si = (ShortcutInfo) obj; - LauncherIcons li = LauncherIcons.obtain(appState.getContext()); - Bitmap badge = li.getShortcutInfoBadge(si, appState.getIconCache()).iconBitmap; - li.recycle(); - float badgeSize = mLauncher.getResources().getDimension(R.dimen.profile_badge_size); - float insetFraction = (iconSize - badgeSize) / iconSize; - return new InsetDrawable(new FastBitmapDrawable(badge), - insetFraction, insetFraction, 0, 0); - } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) { - return ((FolderAdaptiveIcon) obj).getBadge(); - } else { - return mLauncher.getPackageManager() - .getUserBadgedIcon(new FixedSizeEmptyDrawable(iconSize), info.user); - } - } - @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight()); @@ -626,24 +587,4 @@ public class DragView extends View { mSpring.animateToFinalPosition(Utilities.boundToRange(value, -mDelta, mDelta)); } } - - private static class FixedSizeEmptyDrawable extends ColorDrawable { - - private final int mSize; - - public FixedSizeEmptyDrawable(int size) { - super(Color.TRANSPARENT); - mSize = size; - } - - @Override - public int getIntrinsicHeight() { - return mSize; - } - - @Override - public int getIntrinsicWidth() { - return mSize; - } - } } diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 1a432a7ed0..dba02fc369 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.views; +import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA; +import static com.android.launcher3.Utilities.getBadge; import static com.android.launcher3.Utilities.mapToRange; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM; @@ -121,6 +123,7 @@ public class FloatingIconView extends View implements private boolean mIsVerticalBarLayout = false; private boolean mIsAdaptiveIcon = false; + private @Nullable Drawable mBadge; private @Nullable Drawable mForeground; private @Nullable Drawable mBackground; private float mRotation; @@ -362,7 +365,9 @@ public class FloatingIconView extends View implements if (supportsAdaptiveIcons) { drawable = Utilities.getFullDrawable(mLauncher, info, lp.width, lp.height, false, sTmpObjArray); - if (!(drawable instanceof AdaptiveIconDrawable)) { + if ((drawable instanceof AdaptiveIconDrawable)) { + mBadge = getBadge(mLauncher, info, sTmpObjArray[0]); + } else { // The drawable we get back is not an adaptive icon, so we need to use the // BubbleTextView icon that is already legacy treated. drawable = btvIcon; @@ -421,6 +426,14 @@ public class FloatingIconView extends View implements mStartRevealRect.set(0, 0, originalWidth, originalHeight); + if (mBadge != null) { + mBadge.setBounds(mStartRevealRect); + if (!isOpening) { + DRAWABLE_ALPHA.set(mBadge, 0); + } + + } + if (!isFolderIcon) { mStartRevealRect.inset(mBlurSizeOutline, mBlurSizeOutline); } @@ -524,6 +537,9 @@ public class FloatingIconView extends View implements mForeground.draw(canvas); canvas.restoreToCount(count2); } + if (mBadge != null) { + mBadge.draw(canvas); + } canvas.restoreToCount(count); } @@ -642,6 +658,12 @@ public class FloatingIconView extends View implements } }); + if (mBadge != null) { + ObjectAnimator badgeFade = ObjectAnimator.ofInt(mBadge, DRAWABLE_ALPHA, 255); + badgeFade.addUpdateListener(valueAnimator -> invalidate()); + fade.play(badgeFade); + } + if (originalView instanceof BubbleTextView) { BubbleTextView btv = (BubbleTextView) originalView; btv.forceHideDot(true); @@ -716,5 +738,6 @@ public class FloatingIconView extends View implements mFgSpringX.cancel(); mFgTransX = 0; mFgSpringY.cancel(); + mBadge = null; } } From ffaca2e1627c24cdfd529eb0ad9e5a88d8cd4633 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Wed, 15 May 2019 10:43:00 -0700 Subject: [PATCH 16/41] Fix landscape app open/close animations. Bug: 123900446 Bug: 124510042 Change-Id: I3db80d6f8064ce26f97cdede63c0d25499416e98 --- .../QuickstepAppTransitionManagerImpl.java | 39 ++++++++++++++----- .../launcher3/views/FloatingIconView.java | 10 ++++- 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java index 7dd4df7d69..91c460148d 100644 --- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java @@ -476,6 +476,16 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans float shapeRevealDuration = APP_LAUNCH_DURATION * SHAPE_PROGRESS_DURATION; + final float startCrop; + final float endCrop; + if (mDeviceProfile.isVerticalBarLayout()) { + startCrop = windowTargetBounds.height(); + endCrop = windowTargetBounds.width(); + } else { + startCrop = windowTargetBounds.width(); + endCrop = windowTargetBounds.height(); + } + final float windowRadius = mDeviceProfile.isMultiWindowMode ? 0 : getWindowCornerRadius(mLauncher.getResources()); appAnimator.addUpdateListener(new MultiValueUpdateListener() { @@ -485,10 +495,10 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans EXAGGERATED_EASE); FloatProp mIconAlpha = new FloatProp(1f, 0f, APP_LAUNCH_ALPHA_START_DELAY, alphaDuration, LINEAR); - FloatProp mCropHeight = new FloatProp(windowTargetBounds.width(), - windowTargetBounds.height(), 0, APP_LAUNCH_DURATION, EXAGGERATED_EASE); - FloatProp mWindowRadius = new FloatProp(windowTargetBounds.width() / 2f, - windowRadius, 0, APP_LAUNCH_DURATION, EXAGGERATED_EASE); + FloatProp mCroppedSize = new FloatProp(startCrop, endCrop, 0, APP_LAUNCH_DURATION, + EXAGGERATED_EASE); + FloatProp mWindowRadius = new FloatProp(startCrop / 2f, windowRadius, 0, + APP_LAUNCH_DURATION, EXAGGERATED_EASE); @Override public void onUpdate(float percent) { @@ -496,10 +506,16 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans float iconWidth = bounds.width() * mIconScale.value; float iconHeight = bounds.height() * mIconScale.value; - // Animate the window crop so that it starts off as a square, and then reveals - // horizontally. - int windowWidth = windowTargetBounds.width(); - int windowHeight = (int) mCropHeight.value; + // Animate the window crop so that it starts off as a square. + final int windowWidth; + final int windowHeight; + if (mDeviceProfile.isVerticalBarLayout()) { + windowWidth = (int) mCroppedSize.value; + windowHeight = windowTargetBounds.height(); + } else { + windowWidth = windowTargetBounds.width(); + windowHeight = (int) mCroppedSize.value; + } crop.set(0, 0, windowWidth, windowHeight); // Scale the app window to match the icon size. @@ -522,6 +538,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans float transY0 = temp.top - offsetY; float croppedHeight = (windowTargetBounds.height() - crop.height()) * scale; + float croppedWidth = (windowTargetBounds.width() - crop.width()) * scale; SurfaceParams[] params = new SurfaceParams[targets.length]; for (int i = targets.length - 1; i >= 0; i--) { RemoteAnimationTargetCompat target = targets[i]; @@ -535,7 +552,11 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans alpha = 1f - mIconAlpha.value; cornerRadius = mWindowRadius.value; matrix.mapRect(currentBounds, targetBounds); - currentBounds.bottom -= croppedHeight; + if (mDeviceProfile.isVerticalBarLayout()) { + currentBounds.right -= croppedWidth; + } else { + currentBounds.bottom -= croppedHeight; + } mFloatingView.update(currentBounds, mIconAlpha.value, percent, 0f, cornerRadius * scale, true /* isOpening */); } else { diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index dba02fc369..cb7bba73cb 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -210,7 +210,12 @@ public class FloatingIconView extends View implements Math.max(shapeProgressStart, progress), shapeProgressStart, 1f, 0, toMax, LINEAR), 0, 1); - mOutline.bottom = (int) (rect.height() / scale); + if (mIsVerticalBarLayout) { + mOutline.right = (int) (rect.width() / scale); + } else { + mOutline.bottom = (int) (rect.height() / scale); + } + mTaskCornerRadius = cornerRadius / scale; if (mIsAdaptiveIcon) { if (!isOpening && shapeRevealProgress >= 0) { @@ -230,7 +235,8 @@ public class FloatingIconView extends View implements mRevealAnimator.setCurrentFraction(shapeRevealProgress); } - float drawableScale = mOutline.height() / minSize; + float drawableScale = (mIsVerticalBarLayout ? mOutline.width() : mOutline.height()) + / minSize; setBackgroundDrawableBounds(drawableScale); if (isOpening) { // Center align foreground From 41d51a0a15fe0017997fbdedd9c1d65aed27791b Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 14 May 2019 09:44:34 -0700 Subject: [PATCH 17/41] Enabling fake rotation by default Fake rotation is only enabled if homescreen rotation is not enabled Bug: 131360075 Change-Id: Ie56fc4b46b38d3a599ec6da3d506a971e73b0394 --- src/com/android/launcher3/CellLayout.java | 3 +- src/com/android/launcher3/Launcher.java | 19 ++++---- .../android/launcher3/config/BaseFlags.java | 2 +- .../launcher3/states/RotationHelper.java | 43 +++++++++++++++---- 4 files changed, 46 insertions(+), 21 deletions(-) diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 3eb01e6c3b..09fb2446da 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -845,7 +845,8 @@ public class CellLayout extends ViewGroup implements Transposable { * width in {@link DeviceProfile#calculateCellWidth(int, int)}. */ public int getUnusedHorizontalSpace() { - return getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth); + return (mRotationMode.isTransposed ? getMeasuredHeight() : getMeasuredWidth()) + - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth); } public Drawable getScrimBackground() { diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 823fb6b0a2..359d8d9320 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -303,6 +303,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, LauncherAppState app = LauncherAppState.getInstance(this); mOldConfig = new Configuration(getResources().getConfiguration()); mModel = app.setLauncher(this); + mRotationHelper = new RotationHelper(this); InvariantDeviceProfile idp = app.getInvariantDeviceProfile(); initDeviceProfile(idp); idp.addOnChangeListener(this); @@ -325,7 +326,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, setupViews(); mPopupDataProvider = new PopupDataProvider(this); - mRotationHelper = new RotationHelper(this); mAppTransitionManager = LauncherAppTransitionManager.newInstance(this); boolean internalStateHandled = InternalStateHandler.handleCreate(this, getIntent()); @@ -396,12 +396,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, } } }); - - if (FeatureFlags.FAKE_LANDSCAPE_UI.get()) { - WindowManager.LayoutParams lp = getWindow().getAttributes(); - lp.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; - getWindow().setAttributes(lp); - } } @Override @@ -428,9 +422,13 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, super.onConfigurationChanged(newConfig); } + private boolean supportsFakeLandscapeUI() { + return FeatureFlags.FAKE_LANDSCAPE_UI.get() && !mRotationHelper.homeScreenCanRotate(); + } + @Override - protected void reapplyUi() { - if (FeatureFlags.FAKE_LANDSCAPE_UI.get()) { + public void reapplyUi() { + if (supportsFakeLandscapeUI()) { mRotationMode = mStableDeviceProfile == null ? RotationMode.NORMAL : UiFactory.getRotationMode(mDeviceProfile); } @@ -486,7 +484,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize); } - if (FeatureFlags.FAKE_LANDSCAPE_UI.get() && mDeviceProfile.isVerticalBarLayout() + if (supportsFakeLandscapeUI() + && mDeviceProfile.isVerticalBarLayout() && !mDeviceProfile.isMultiWindowMode) { mStableDeviceProfile = mDeviceProfile.inv.portraitProfile; mRotationMode = UiFactory.getRotationMode(mDeviceProfile); diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java index 70df97ae8c..bad8282f54 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -111,7 +111,7 @@ abstract class BaseFlags { "Show chip hints and gleams on the overview screen"); public static final TogglableFlag FAKE_LANDSCAPE_UI = new TogglableFlag( - "FAKE_LANDSCAPE_UI", false, + "FAKE_LANDSCAPE_UI", true, "Rotate launcher UI instead of using transposed layout"); public static void initialize(Context context) { diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java index fb41ea1a28..3727fa6632 100644 --- a/src/com/android/launcher3/states/RotationHelper.java +++ b/src/com/android/launcher3/states/RotationHelper.java @@ -20,13 +20,16 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE; -import android.app.Activity; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.res.Resources; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; +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; /** @@ -49,7 +52,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { public static final int REQUEST_ROTATE = 1; public static final int REQUEST_LOCK = 2; - private final Activity mActivity; + private final Launcher mLauncher; private final SharedPreferences mPrefs; private boolean mIgnoreAutoRotateSettings; @@ -70,13 +73,13 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { private int mLastActivityFlags = -1; - public RotationHelper(Activity activity) { - mActivity = activity; + public RotationHelper(Launcher launcher) { + mLauncher = launcher; // On large devices we do not handle auto-rotate differently. - mIgnoreAutoRotateSettings = mActivity.getResources().getBoolean(R.bool.allow_rotation); + mIgnoreAutoRotateSettings = mLauncher.getResources().getBoolean(R.bool.allow_rotation); if (!mIgnoreAutoRotateSettings) { - mPrefs = Utilities.getPrefs(mActivity); + mPrefs = Utilities.getPrefs(mLauncher); mPrefs.registerOnSharedPreferenceChangeListener(this); mAutoRotateEnabled = mPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, getAllowRotationDefaultValue()); @@ -85,11 +88,32 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { } } + public boolean homeScreenCanRotate() { + return mIgnoreAutoRotateSettings || mAutoRotateEnabled + || mStateHandlerRequest != REQUEST_NONE; + } + + private void updateRotationAnimation() { + if (FeatureFlags.FAKE_LANDSCAPE_UI.get()) { + WindowManager.LayoutParams lp = mLauncher.getWindow().getAttributes(); + lp.rotationAnimation = homeScreenCanRotate() + ? WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE + : WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; + mLauncher.getWindow().setAttributes(lp); + } + } + @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) { + boolean wasRotationEnabled = mAutoRotateEnabled; mAutoRotateEnabled = mPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, getAllowRotationDefaultValue()); - notifyChange(); + if (mAutoRotateEnabled != wasRotationEnabled) { + + notifyChange(); + updateRotationAnimation(); + mLauncher.reapplyUi(); + } } public void setStateHandlerRequest(int request) { @@ -109,7 +133,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { // Used by tests only. public void forceAllowRotationForTesting(boolean allowRotation) { mIgnoreAutoRotateSettings = - allowRotation || mActivity.getResources().getBoolean(R.bool.allow_rotation); + allowRotation || mLauncher.getResources().getBoolean(R.bool.allow_rotation); notifyChange(); } @@ -117,6 +141,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { if (!mInitialized) { mInitialized = true; notifyChange(); + updateRotationAnimation(); } } @@ -150,7 +175,7 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { } if (activityFlags != mLastActivityFlags) { mLastActivityFlags = activityFlags; - UiThreadHelper.setOrientationAsync(mActivity, activityFlags); + UiThreadHelper.setOrientationAsync(mLauncher, activityFlags); } } From 65f6c628edb9da56f8c693e2263b0e1cc1ca44b3 Mon Sep 17 00:00:00 2001 From: vadimt Date: Wed, 15 May 2019 12:14:15 -0700 Subject: [PATCH 18/41] Including accessibility hierarchy for all TAPL fails Change-Id: I3357f33b3749bb9799b1b72af4c0084cd2e5dbca --- .../launcher3/tapl/LauncherInstrumentation.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index ec62188950..d3f1460048 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -58,6 +58,7 @@ import com.android.systemui.shared.system.QuickStepContract; import org.junit.Assert; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.Deque; @@ -214,7 +215,22 @@ public final class LauncherInstrumentation { }; } + private void dumpViewHierarchy() { + final ByteArrayOutputStream stream = new ByteArrayOutputStream(); + try { + mDevice.dumpWindowHierarchy(stream); + stream.flush(); + stream.close(); + for (String line : stream.toString().split("\\r?\\n")) { + Log.e(TAG, line.trim()); + } + } catch (IOException e) { + Log.e(TAG, "error dumping XML to logcat", e); + } + } + private void fail(String message) { + dumpViewHierarchy(); Assert.fail("http://go/tapl : " + getContextDescription() + message); } From 56fd673ef3c476c93e38a7522608be3fad284388 Mon Sep 17 00:00:00 2001 From: Tony Date: Wed, 15 May 2019 15:31:54 -0400 Subject: [PATCH 19/41] Capture screenshot when quick switching Bug: 130193889 Change-Id: Ia972445e6b0c928a67c8373f082d412c440189ab --- .../com/android/quickstep/WindowTransformSwipeHandler.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java index 537858d9b7..29034eae6e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -176,7 +176,8 @@ public class WindowTransformSwipeHandler RECENTS(1, STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT | STATE_SCREENSHOT_VIEW_SHOWN, true, false, ContainerType.TASKSWITCHER, true), - NEW_TASK(0, STATE_START_NEW_TASK, false, true, ContainerType.APP, true), + NEW_TASK(0, STATE_START_NEW_TASK | STATE_CAPTURE_SCREENSHOT, false, true, + ContainerType.APP, true), LAST_TASK(0, STATE_RESUME_LAST_TASK, false, true, ContainerType.APP, false); @@ -320,7 +321,7 @@ public class WindowTransformSwipeHandler mStateCallback.addCallback(STATE_RESUME_LAST_TASK | STATE_APP_CONTROLLER_RECEIVED, this::resumeLastTask); - mStateCallback.addCallback(STATE_START_NEW_TASK | STATE_APP_CONTROLLER_RECEIVED, + mStateCallback.addCallback(STATE_START_NEW_TASK | STATE_SCREENSHOT_CAPTURED, this::startNewTask); mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED From 1aca18d85e54f9aeca6a294e518c971a3f5d5f1d Mon Sep 17 00:00:00 2001 From: George Hodulik Date: Tue, 14 May 2019 15:26:19 -0700 Subject: [PATCH 20/41] Allow extras to be added in PredictionAppTracker subclasses. Bug: 132584688 Change-Id: I727009dab24a82968673d4df6d3a5daa11c34281 --- .../appprediction/PredictionAppTracker.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java index fa2810630c..af67e1bbbc 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java @@ -27,11 +27,13 @@ import android.app.prediction.AppTargetId; import android.content.ComponentName; import android.content.Context; import android.os.Build; +import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.UserHandle; import android.util.Log; +import androidx.annotation.Nullable; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.appprediction.PredictionUiStateManager.Client; import com.android.launcher3.model.AppLaunchTracker; @@ -97,6 +99,7 @@ public class PredictionAppTracker extends AppLaunchTracker { new AppPredictionContext.Builder(mContext) .setUiSurface(client.id) .setPredictedTargetCount(count) + .setExtras(getAppPredictionContextExtras(client)) .build()); predictor.registerPredictionUpdates(mContext.getMainExecutor(), PredictionUiStateManager.INSTANCE.get(mContext).appPredictorCallback(client)); @@ -104,6 +107,15 @@ public class PredictionAppTracker extends AppLaunchTracker { return predictor; } + /** + * Override to add custom extras. + */ + @WorkerThread + @Nullable + public Bundle getAppPredictionContextExtras(Client client){ + return null; + } + @WorkerThread private boolean handleMessage(Message msg) { switch (msg.what) { From a68874e1cbb2058ad495916f956626ca10002c86 Mon Sep 17 00:00:00 2001 From: vadimt Date: Wed, 15 May 2019 13:53:50 -0700 Subject: [PATCH 21/41] Deinitialize prediction tests Change-Id: Id97c509a7b609f2ba8b081c25712b24b50ec9ecf --- .../tests/src/com/android/quickstep/AppPredictionsUITests.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java index 47ce44c676..5e20e5643c 100644 --- a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java +++ b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java @@ -79,6 +79,8 @@ public class AppPredictionsUITests extends AbstractQuickStepTest { @After public void tearDown() throws Throwable { + AppLaunchTracker.INSTANCE.initializeForTesting(null); + PredictionUiStateManager.INSTANCE.initializeForTesting(null); mDevice.unfreezeRotation(); } From ad9fb5276d72789d4fad8cc18ef2c42064726c69 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 15 May 2019 14:24:27 -0700 Subject: [PATCH 22/41] Displaying edge-to-edge in landscape Bug: 131360075 Change-Id: I7bdf72dabf7f829f294b08ea9b348f7173ff7d31 --- res/values-v29/styles.xml | 32 ++++++++++++++++++++++++++++++++ res/values/styles.xml | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 res/values-v29/styles.xml diff --git a/res/values-v29/styles.xml b/res/values-v29/styles.xml new file mode 100644 index 0000000000..f62382304e --- /dev/null +++ b/res/values-v29/styles.xml @@ -0,0 +1,32 @@ + + + + + + + diff --git a/res/values/styles.xml b/res/values/styles.xml index 8116e30ef1..881f65d276 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -19,7 +19,7 @@ -