From bcf0d1e08217a98830ee3838619d9802c8cc33f4 Mon Sep 17 00:00:00 2001 From: vadimt Date: Wed, 1 May 2019 14:32:05 -0700 Subject: [PATCH 01/72] Reenabling switching between nav modes in tests Bug: 130558787 Bug: 131419978 Bug: 131630813 Change-Id: I8d62c7f2b33ac94bd4f899f7c8ed00c8293841e7 --- .../quickstep/FallbackRecentsTest.java | 3 - .../quickstep/NavigationModeSwitchRule.java | 3 +- .../launcher3/ui/AbstractLauncherUiTest.java | 68 ------------------- 3 files changed, 1 insertion(+), 73 deletions(-) diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java index 960d907dca..20fdff2c55 100644 --- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java +++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java @@ -44,7 +44,6 @@ import com.android.launcher3.tapl.LauncherInstrumentation; import com.android.launcher3.testcomponent.TestCommandReceiver; import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; @@ -98,7 +97,6 @@ public class FallbackRecentsTest { @NavigationModeSwitch(mode = THREE_BUTTON) @Test - @Ignore // b/131630813 public void goToOverviewFromHome() { mDevice.pressHome(); assertTrue("Fallback Launcher not visible", mDevice.wait(Until.hasObject(By.pkg( @@ -109,7 +107,6 @@ public class FallbackRecentsTest { @NavigationModeSwitch(mode = THREE_BUTTON) @Test - @Ignore // b/131630813 public void goToOverviewFromApp() { startAppFast("com.android.settings"); diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java index 93e403cc84..e552f56ba6 100644 --- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java +++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java @@ -71,8 +71,7 @@ public class NavigationModeSwitchRule implements TestRule { @Override public Statement apply(Statement base, Description description) { - // b/130558787; b/131419978 - if (false && TestHelpers.isInLauncherProcess() && + if (TestHelpers.isInLauncherProcess() && description.getAnnotation(NavigationModeSwitch.class) != null) { Mode mode = description.getAnnotation(NavigationModeSwitch.class).mode(); return new Statement() { diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 43bdb9f3f0..75db2f19e8 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -18,9 +18,6 @@ package com.android.launcher3.ui; import static androidx.test.InstrumentationRegistry.getInstrumentation; import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; -import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_2BUTTON_OVERLAY; -import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_3BUTTON_OVERLAY; -import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_GESTURAL_OVERLAY; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -35,7 +32,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.LauncherActivityInfo; import android.content.pm.PackageManager; -import android.os.Build; import android.os.Process; import android.os.RemoteException; import android.util.Log; @@ -64,7 +60,6 @@ import com.android.launcher3.util.rule.LauncherActivityRule; import com.android.launcher3.util.rule.ShellCommandRule; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.rules.TestRule; @@ -75,7 +70,6 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import java.lang.reflect.Method; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -111,68 +105,6 @@ public abstract class AbstractLauncherUiTest { } if (TestHelpers.isInLauncherProcess()) Utilities.enableRunningInTestHarnessForTests(); mLauncher = new LauncherInstrumentation(instrumentation); - - // b/130558787; b/131419978 - if (TestHelpers.isInLauncherProcess() && !LauncherInstrumentation.needSlowGestures()) { - try { - Class systemProps = Class.forName("android.os.SystemProperties"); - Method getInt = systemProps.getMethod("getInt", String.class, int.class); - int apiLevel = (int) getInt.invoke(null, "ro.product.first_api_level", 0); - - if (apiLevel >= Build.VERSION_CODES.P) { - setActiveOverlay(NAV_BAR_MODE_2BUTTON_OVERLAY, - LauncherInstrumentation.NavigationModel.TWO_BUTTON); - } - if (apiLevel >= Build.VERSION_CODES.O && apiLevel < Build.VERSION_CODES.P) { - setActiveOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY, - LauncherInstrumentation.NavigationModel.ZERO_BUTTON); - } - if (apiLevel < Build.VERSION_CODES.O) { - setActiveOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY, - LauncherInstrumentation.NavigationModel.THREE_BUTTON); - } - } catch (Throwable e) { - e.printStackTrace(); - } - } - } - - public void setActiveOverlay(String overlayPackage, - LauncherInstrumentation.NavigationModel expectedMode) { - setOverlayPackageEnabled(NAV_BAR_MODE_3BUTTON_OVERLAY, - overlayPackage == NAV_BAR_MODE_3BUTTON_OVERLAY); - setOverlayPackageEnabled(NAV_BAR_MODE_2BUTTON_OVERLAY, - overlayPackage == NAV_BAR_MODE_2BUTTON_OVERLAY); - setOverlayPackageEnabled(NAV_BAR_MODE_GESTURAL_OVERLAY, - overlayPackage == NAV_BAR_MODE_GESTURAL_OVERLAY); - - for (int i = 0; i != 100; ++i) { - if (mLauncher.getNavigationModel() == expectedMode) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - return; - } - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - Assert.fail("Couldn't switch to " + overlayPackage); - } - - private void setOverlayPackageEnabled(String overlayPackage, boolean enable) { - Log.d(TAG, "setOverlayPackageEnabled: " + overlayPackage + " " + enable); - final String action = enable ? "enable" : "disable"; - try { - UiDevice.getInstance(getInstrumentation()).executeShellCommand( - "cmd overlay " + action + " " + overlayPackage); - } catch (IOException e) { - e.printStackTrace(); - } } @Rule From 4cd79876ac073de75c8505ee8a1adee3c2d7e8a6 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 2 May 2019 16:30:25 -0700 Subject: [PATCH 02/72] Adding support for overriding hotseat Widget Bug: 131360075 Change-Id: I94151cb2987d7decea283790ab9e87cb4627d40c --- res/layout/hotseat.xml | 24 ++++++++++++++++++++++++ res/layout/launcher.xml | 8 ++------ 2 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 res/layout/hotseat.xml diff --git a/res/layout/hotseat.xml b/res/layout/hotseat.xml new file mode 100644 index 0000000000..82b0b8d74e --- /dev/null +++ b/res/layout/hotseat.xml @@ -0,0 +1,24 @@ + + + \ No newline at end of file diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml index 6ecc1f55d2..cca899bda8 100644 --- a/res/layout/launcher.xml +++ b/res/layout/launcher.xml @@ -39,13 +39,9 @@ launcher:pageIndicator="@+id/page_indicator" /> - + layout="@layout/hotseat" /> Date: Fri, 3 May 2019 11:14:16 -0700 Subject: [PATCH 03/72] Fixing FloatingIconView position and rotation in rotated layout Bug: 131360075 Change-Id: I4e39f35df99a07e00f3e86865b330d91e9461da0 --- src/com/android/launcher3/Utilities.java | 11 ++- .../launcher3/views/FloatingIconView.java | 99 ++++++++----------- 2 files changed, 50 insertions(+), 60 deletions(-) diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 26364be062..35b967fda4 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -167,7 +167,7 @@ public final class Utilities { public static float getDescendantCoordRelativeToAncestor( View descendant, View ancestor, float[] coord, boolean includeRootScroll) { return getDescendantCoordRelativeToAncestor(descendant, ancestor, coord, includeRootScroll, - false); + false, null); } /** @@ -180,12 +180,15 @@ 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[] coord, boolean includeRootScroll, boolean ignoreTransform, + float[] outRotation) { float scale = 1.0f; View v = descendant; while(v != ancestor && v != null) { @@ -201,6 +204,10 @@ public final class Utilities { if (m.isTransposed) { sMatrix.setRotate(m.surfaceRotation, v.getPivotX(), v.getPivotY()); sMatrix.mapPoints(coord); + + if (outRotation != null) { + outRotation[0] += m.surfaceRotation; + } } } } else { diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 24a8be5723..020e3809e1 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -15,8 +15,6 @@ */ package com.android.launcher3.views; -import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP; -import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT; 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; @@ -86,6 +84,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, private @Nullable Drawable mForeground; private @Nullable Drawable mBackground; + private float mRotation; private ValueAnimator mRevealAnimator; private final Rect mStartRevealRect = new Rect(); private final Rect mEndRevealRect = new Rect(); @@ -190,7 +189,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, * @param positionOut Rect that will hold the size and position of v. */ private void matchPositionOf(Launcher launcher, View v, RectF positionOut) { - getLocationBoundsForView(launcher, v, positionOut); + mRotation = getLocationBoundsForView(launcher, v, positionOut); final LayoutParams lp = new LayoutParams( Math.round(positionOut.width()), Math.round(positionOut.height())); @@ -207,54 +206,43 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, } /** - * Returns the location bounds of a view. + * Gets the location bounds of a view and returns the overall rotation. * - For DeepShortcutView, we return the bounds of the icon view. * - For BubbleTextView, we return the icon bounds. */ - private void getLocationBoundsForView(Launcher launcher, View v, RectF outRect) { - final boolean isBubbleTextView = v instanceof BubbleTextView; - final boolean isFolderIcon = v instanceof FolderIcon; - - // Deep shortcut views have their icon drawn in a separate view. - final boolean fromDeepShortcutView = v.getParent() instanceof DeepShortcutView; - - final View targetView; - boolean ignoreTransform = false; - + private float getLocationBoundsForView(Launcher launcher, View v, RectF outRect) { + boolean ignoreTransform = true; if (v instanceof DeepShortcutView) { - targetView = ((DeepShortcutView) v).getIconView(); - } else if (fromDeepShortcutView) { - DeepShortcutView view = (DeepShortcutView) v.getParent(); - targetView = view.getIconView(); - } else if ((isBubbleTextView || isFolderIcon) && v.getTag() instanceof ItemInfo - && (((ItemInfo) v.getTag()).container == CONTAINER_DESKTOP - || ((ItemInfo) v.getTag()).container == CONTAINER_HOTSEAT)) { - targetView = v; - ignoreTransform = true; - } else { - targetView = v; + v = ((DeepShortcutView) v).getBubbleText(); + ignoreTransform = false; + } else if (v.getParent() instanceof DeepShortcutView) { + v = ((DeepShortcutView) v.getParent()).getIconView(); + ignoreTransform = false; + } + if (v == null) { + return 0; } - float[] points = new float[] {0, 0, targetView.getWidth(), targetView.getHeight()}; - Utilities.getDescendantCoordRelativeToAncestor(targetView, launcher.getDragLayer(), points, - false, ignoreTransform); - - float viewLocationLeft = Math.min(points[0], points[2]); - float viewLocationTop = Math.min(points[1], points[3]); - - final Rect iconRect = new Rect(); - if (isBubbleTextView && !fromDeepShortcutView) { - ((BubbleTextView) v).getIconBounds(iconRect); - } else if (isFolderIcon) { - ((FolderIcon) v).getPreviewBounds(iconRect); + Rect iconBounds = new Rect(); + if (v instanceof BubbleTextView) { + ((BubbleTextView) v).getIconBounds(iconBounds); + } else if (v instanceof FolderIcon) { + ((FolderIcon) v).getPreviewBounds(iconBounds); } else { - iconRect.set(0, 0, Math.abs(Math.round(points[2] - points[0])), - Math.abs(Math.round(points[3] - points[1]))); + iconBounds.set(0, 0, v.getWidth(), v.getHeight()); } - viewLocationLeft += iconRect.left; - viewLocationTop += iconRect.top; - outRect.set(viewLocationLeft, viewLocationTop, viewLocationLeft + iconRect.width(), - viewLocationTop + iconRect.height()); + + 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); + 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]; } @WorkerThread @@ -425,27 +413,22 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, invalidate(); } - private void drawAdaptiveIconIfExists(Canvas canvas) { + @Override + public void draw(Canvas canvas) { + int count = canvas.save(); + canvas.rotate(mRotation, + mFinalDrawableBounds.exactCenterX(), mFinalDrawableBounds.exactCenterY()); + if (mClipPath != null) { + canvas.clipPath(mClipPath); + } + super.draw(canvas); if (mBackground != null) { mBackground.draw(canvas); } if (mForeground != null) { mForeground.draw(canvas); } - } - - @Override - public void draw(Canvas canvas) { - if (mClipPath == null) { - super.draw(canvas); - drawAdaptiveIconIfExists(canvas); - } else { - int count = canvas.save(); - canvas.clipPath(mClipPath); - super.draw(canvas); - drawAdaptiveIconIfExists(canvas); - canvas.restoreToCount(count); - } + canvas.restoreToCount(count); } public void onListenerViewClosed() { From 0030834239abec5dbe2ba31fd467f816c87d431e Mon Sep 17 00:00:00 2001 From: vadimt Date: Fri, 3 May 2019 11:42:43 -0700 Subject: [PATCH 04/72] Temporary hacks to make tests pass on Taimen Change-Id: I66a81a0407be13c15596c63f21773f8e2dea5d2d --- .../android/launcher3/ui/TestViewHelpers.java | 22 ++++++++++++------- .../com/android/launcher3/tapl/AllApps.java | 2 +- .../com/android/launcher3/tapl/Widgets.java | 2 +- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/tests/src/com/android/launcher3/ui/TestViewHelpers.java b/tests/src/com/android/launcher3/ui/TestViewHelpers.java index 6fa28f1c91..2116f5e2d5 100644 --- a/tests/src/com/android/launcher3/ui/TestViewHelpers.java +++ b/tests/src/com/android/launcher3/ui/TestViewHelpers.java @@ -114,7 +114,8 @@ public class TestViewHelpers { Point center = icon.getVisibleCenter(); // Action Down - sendPointer(MotionEvent.ACTION_DOWN, center); + final long downTime = SystemClock.uptimeMillis(); + sendPointer(downTime, MotionEvent.ACTION_DOWN, center); UiObject2 dragLayer = findViewById(R.id.drag_layer); @@ -131,7 +132,7 @@ public class TestViewHelpers { } else { moveLocation.y += distanceToMove; } - movePointer(center, moveLocation); + movePointer(downTime, center, moveLocation); assertNull(findViewById(R.id.deep_shortcuts_container)); } @@ -142,19 +143,24 @@ public class TestViewHelpers { Point moveLocation = dragLayer.getVisibleCenter(); // Move to center - movePointer(center, moveLocation); - sendPointer(MotionEvent.ACTION_UP, center); + movePointer(downTime, center, moveLocation); + sendPointer(downTime, MotionEvent.ACTION_UP, moveLocation); // Wait until remove target is gone. getDevice().wait(Until.gone(getSelectorForId(R.id.delete_target_text)), AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT); } - private static void movePointer(Point from, Point to) { + private static void movePointer(long downTime, Point from, Point to) { while (!from.equals(to)) { + try { + Thread.sleep(20); + } catch (InterruptedException e) { + e.printStackTrace(); + } from.x = getNextMoveValue(to.x, from.x); from.y = getNextMoveValue(to.y, from.y); - sendPointer(MotionEvent.ACTION_MOVE, from); + sendPointer(downTime, MotionEvent.ACTION_MOVE, from); } } @@ -168,8 +174,8 @@ public class TestViewHelpers { } } - public static void sendPointer(int action, Point point) { - MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(), + public static void sendPointer(long downTime, int action, Point point) { + MotionEvent event = MotionEvent.obtain(downTime, SystemClock.uptimeMillis(), action, point.x, point.y, 0); getInstrumentation().sendPointerSync(event); event.recycle(); diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java index 19c5047e00..1ad00375f8 100644 --- a/tests/tapl/com/android/launcher3/tapl/AllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java @@ -70,7 +70,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { final UiObject2 navBar = mLauncher.waitForSystemUiObject("navigation_bar_frame"); allAppsContainer.setGestureMargins(0, 0, 0, navBar.getVisibleBounds().height() + 1); } else { - allAppsContainer.setGestureMargins(0, 0, 0, 100); + allAppsContainer.setGestureMargins(0, 0, 0, 200); } final BySelector appIconSelector = AppIcon.getAppIconSelector(appName, mLauncher); if (!hasClickableIcon(allAppsContainer, appIconSelector)) { diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java index 2de53c3f58..100a11c94c 100644 --- a/tests/tapl/com/android/launcher3/tapl/Widgets.java +++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java @@ -38,7 +38,7 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer { "want to fling forward in widgets")) { LauncherInstrumentation.log("Widgets.flingForward enter"); final UiObject2 widgetsContainer = verifyActiveContainer(); - widgetsContainer.setGestureMargin(100); + widgetsContainer.setGestureMargins(0, 0, 0, 200); widgetsContainer.fling(Direction.DOWN, (int) (FLING_SPEED * mLauncher.getDisplayDensity())); try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung forward")) { From 9cc1b2f2427ba914a15f8537d1c608a8c8e02d30 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 3 May 2019 11:53:12 -0700 Subject: [PATCH 05/72] Calling onTransitionEnd when starting a user controller animation so that any transient property is cleared Bug: 80529128 Bug: 131360075 Change-Id: I613a0df4a06861be1c7195e1473c907489de1d1a --- src/com/android/launcher3/LauncherStateManager.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java index b24f660e6b..f5040b3ba8 100644 --- a/src/com/android/launcher3/LauncherStateManager.java +++ b/src/com/android/launcher3/LauncherStateManager.java @@ -553,6 +553,8 @@ public class LauncherStateManager { cancelAnimation(); if (reapplyNeeded) { reapplyState(); + // Dispatch on transition end, so that any transient property is cleared. + onStateTransitionEnd(mState); } mConfig.setAnimation(anim, null); } From 1bdb0f4046155ffb56a995aef942fc1e3367d0eb Mon Sep 17 00:00:00 2001 From: vadimt Date: Fri, 3 May 2019 12:56:13 -0700 Subject: [PATCH 06/72] Enable BindWidgetTest, RequestPinItemTest Comparing only widget provider class names, as package names (sometimes) switch to the test package, not to the ones in Launcher. Bug: 131116593 Change-Id: Ieeed69432303a86fcefb194d509cdaf9d4513f3a --- .../launcher3/ui/widget/AddConfigWidgetTest.java | 3 ++- .../android/launcher3/ui/widget/AddWidgetTest.java | 3 ++- .../android/launcher3/ui/widget/BindWidgetTest.java | 6 +++--- .../launcher3/ui/widget/RequestPinItemTest.java | 11 +++++------ 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java index 06a8bca13e..5eb5f19928 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java @@ -156,7 +156,8 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest { @Override public boolean evaluate(ItemInfo info, View view) { return info instanceof LauncherAppWidgetInfo && - ((LauncherAppWidgetInfo) info).providerName.equals(mWidgetInfo.provider) && + ((LauncherAppWidgetInfo) info).providerName.getClassName().equals( + mWidgetInfo.provider.getClassName()) && ((LauncherAppWidgetInfo) info).appWidgetId == mWidgetId; } } diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java index e802acb62c..0061568cce 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java @@ -82,7 +82,8 @@ public class AddWidgetTest extends AbstractLauncherUiTest { @Override public boolean evaluate(ItemInfo info, View view) { return info instanceof LauncherAppWidgetInfo && - ((LauncherAppWidgetInfo) info).providerName.equals(widgetInfo.provider); + ((LauncherAppWidgetInfo) info).providerName.getClassName().equals( + widgetInfo.provider.getClassName()); } }).call()); } diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java index af50190b94..874ff1995b 100644 --- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java @@ -123,7 +123,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest { verifyWidgetPresent(info); } - @Test @Ignore // b/131116593 + @Test public void testUnboundWidget_removed() { LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false); LauncherAppWidgetInfo item = createWidgetInfo(info, false); @@ -143,7 +143,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest { assertFalse(mDevice.findObject(new UiSelector().description(info.label)).exists()); } - @Test @Ignore // b/131116593 + @Test public void testPendingWidget_autoRestored() { // A non-restored widget with no config screen gets restored automatically. LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false); @@ -181,7 +181,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest { LauncherSettings.Favorites.APPWIDGET_ID)))); } - @Test @Ignore // b/131116593 + @Test public void testPendingWidget_notRestored_removed() { LauncherAppWidgetInfo item = getInvalidWidgetInfo(); item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java index 65d8a826f1..b66fa8afbc 100644 --- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java +++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java @@ -80,7 +80,7 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { @Test public void testEmpty() throws Throwable { /* needed while the broken tests are being fixed */ } - @Test @Ignore // b/131116593 + @Test public void testPinWidgetNoConfig() throws Throwable { runTest("pinWidgetNoConfig", true, new ItemOperator() { @Override @@ -93,7 +93,7 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { }); } - @Test @Ignore // b/131116593 + @Test public void testPinWidgetNoConfig_customPreview() throws Throwable { // Command to set custom preview Intent command = RequestPinItemActivity.getCommandIntent( @@ -111,7 +111,7 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { }, command); } - @Test @Ignore // b/131116593 + @Test public void testPinWidgetWithConfig() throws Throwable { runTest("pinWidgetWithConfig", true, new ItemOperator() { @Override @@ -124,7 +124,7 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { }); } - @Test @Ignore // b/131116593 + @Test public void testPinShortcut() throws Throwable { // Command to set the shortcut id Intent command = RequestPinItemActivity.getCommandIntent( @@ -181,8 +181,7 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { // Accept confirmation: BlockingBroadcastReceiver resultReceiver = new BlockingBroadcastReceiver(mCallbackAction); - mDevice.wait(Until.findObject(By.text(mTargetContext.getString( - R.string.place_automatically).toUpperCase())), DEFAULT_UI_TIMEOUT).click(); + mDevice.wait(Until.findObject(By.text("Add automatically")), DEFAULT_UI_TIMEOUT).click(); Intent result = resultReceiver.blockingGetIntent(); assertNotNull(result); mAppWidgetId = result.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); From 905262c1a73deeafdec9f12016a218b52bbabbaf Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 3 May 2019 16:50:43 -0700 Subject: [PATCH 07/72] Adding utility method to get adaptive icon scale Change-Id: I5ff190c3b794bb13309375782ccd420e85b59091 --- .../launcher3/icons/BaseIconFactory.java | 2 +- .../launcher3/icons/GraphicsUtils.java | 13 +++ .../launcher3/icons/IconNormalizer.java | 86 +++++++++++++------ .../launcher3/InvariantDeviceProfile.java | 4 +- .../launcher3/MainProcessInitializer.java | 4 +- .../folder/FolderAnimationManager.java | 2 +- .../launcher3/folder/PreviewBackground.java | 4 +- .../launcher3/graphics/DrawableFactory.java | 45 ++-------- .../IconShape.java} | 77 ++++++++++------- .../graphics/PreloadIconDrawable.java | 10 +-- .../launcher3/views/FloatingIconView.java | 4 +- 11 files changed, 142 insertions(+), 109 deletions(-) rename src/com/android/launcher3/{folder/FolderShape.java => graphics/IconShape.java} (86%) diff --git a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java index ab4b64cbfe..3c5585421a 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java +++ b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java @@ -78,7 +78,7 @@ public class BaseIconFactory implements AutoCloseable { public IconNormalizer getNormalizer() { if (mNormalizer == null) { - mNormalizer = new IconNormalizer(mContext, mIconBitmapSize); + mNormalizer = new IconNormalizer(mIconBitmapSize); } return mNormalizer; } diff --git a/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java b/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java index 11d5eef52c..3e818a5568 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java +++ b/iconloaderlib/src/com/android/launcher3/icons/GraphicsUtils.java @@ -16,6 +16,9 @@ package com.android.launcher3.icons; import android.graphics.Bitmap; +import android.graphics.Rect; +import android.graphics.Region; +import android.graphics.RegionIterator; import android.util.Log; import java.io.ByteArrayOutputStream; @@ -60,4 +63,14 @@ public class GraphicsUtils { return null; } } + + public static int getArea(Region r) { + RegionIterator itr = new RegionIterator(r); + int area = 0; + Rect tempRect = new Rect(); + while (itr.next(tempRect)) { + area += tempRect.width() * tempRect.height(); + } + return area; + } } diff --git a/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java b/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java index 05908df99b..4a2a7cf9ff 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java +++ b/iconloaderlib/src/com/android/launcher3/icons/IconNormalizer.java @@ -16,14 +16,18 @@ package com.android.launcher3.icons; +import android.annotation.TargetApi; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; +import android.graphics.Region; import android.graphics.drawable.AdaptiveIconDrawable; import android.graphics.drawable.Drawable; +import android.os.Build; import java.nio.ByteBuffer; @@ -57,7 +61,7 @@ public class IconNormalizer { private final Canvas mCanvas; private final byte[] mPixels; - private final Rect mAdaptiveIconBounds; + private final RectF mAdaptiveIconBounds; private float mAdaptiveIconScale; // for each y, stores the position of the leftmost x and the rightmost x @@ -66,7 +70,7 @@ public class IconNormalizer { private final Rect mBounds; /** package private **/ - IconNormalizer(Context context, int iconBitmapSize) { + IconNormalizer(int iconBitmapSize) { // Use twice the icon size as maximum size to avoid scaling down twice. mMaxSize = iconBitmapSize * 2; mBitmap = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ALPHA_8); @@ -75,11 +79,53 @@ public class IconNormalizer { mLeftBorder = new float[mMaxSize]; mRightBorder = new float[mMaxSize]; mBounds = new Rect(); - mAdaptiveIconBounds = new Rect(); + mAdaptiveIconBounds = new RectF(); mAdaptiveIconScale = SCALE_NOT_INITIALIZED; } + private static float getScale(float hullArea, float boundingArea, float fullArea) { + float hullByRect = hullArea / boundingArea; + float scaleRequired; + if (hullByRect < CIRCLE_AREA_BY_RECT) { + scaleRequired = MAX_CIRCLE_AREA_FACTOR; + } else { + scaleRequired = MAX_SQUARE_AREA_FACTOR + LINEAR_SCALE_SLOPE * (1 - hullByRect); + } + + float areaScale = hullArea / fullArea; + // Use sqrt of the final ratio as the images is scaled across both width and height. + return areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1; + } + + /** + * @param d Should be AdaptiveIconDrawable + * @param size Canvas size to use + */ + @TargetApi(Build.VERSION_CODES.O) + public static float normalizeAdaptiveIcon(Drawable d, int size, @Nullable RectF outBounds) { + Rect tmpBounds = new Rect(d.getBounds()); + d.setBounds(0, 0, size, size); + + Path path = ((AdaptiveIconDrawable) d).getIconMask(); + Region region = new Region(); + region.setPath(path, new Region(0, 0, size, size)); + + Rect hullBounds = region.getBounds(); + int hullArea = GraphicsUtils.getArea(region); + + if (outBounds != null) { + float sizeF = size; + outBounds.set( + hullBounds.left / sizeF, + hullBounds.top / sizeF, + 1 - (hullBounds.right / sizeF), + 1 - (hullBounds.bottom / sizeF)); + } + d.setBounds(tmpBounds); + return getScale(hullArea, hullArea, size * size); + } + /** * Returns the amount by which the {@param d} should be scaled (in both dimensions) so that it * matches the design guidelines for a launcher icon. @@ -96,12 +142,13 @@ public class IconNormalizer { */ public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds) { if (BaseIconFactory.ATLEAST_OREO && d instanceof AdaptiveIconDrawable) { - if (mAdaptiveIconScale != SCALE_NOT_INITIALIZED) { - if (outBounds != null) { - outBounds.set(mAdaptiveIconBounds); - } - return mAdaptiveIconScale; + if (mAdaptiveIconScale == SCALE_NOT_INITIALIZED) { + mAdaptiveIconScale = normalizeAdaptiveIcon(d, mMaxSize, mAdaptiveIconBounds); } + if (outBounds != null) { + outBounds.set(mAdaptiveIconBounds); + } + return mAdaptiveIconScale; } int width = d.getIntrinsicWidth(); int height = d.getIntrinsicHeight(); @@ -184,16 +231,6 @@ public class IconNormalizer { area += mRightBorder[y] - mLeftBorder[y] + 1; } - // Area of the rectangle required to fit the convex hull - float rectArea = (bottomY + 1 - topY) * (rightX + 1 - leftX); - float hullByRect = area / rectArea; - - float scaleRequired; - if (hullByRect < CIRCLE_AREA_BY_RECT) { - scaleRequired = MAX_CIRCLE_AREA_FACTOR; - } else { - scaleRequired = MAX_SQUARE_AREA_FACTOR + LINEAR_SCALE_SLOPE * (1 - hullByRect); - } mBounds.left = leftX; mBounds.right = rightX; @@ -205,15 +242,10 @@ public class IconNormalizer { 1 - ((float) mBounds.right) / width, 1 - ((float) mBounds.bottom) / height); } - float areaScale = area / (width * height); - // Use sqrt of the final ratio as the images is scaled across both width and height. - float scale = areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1; - if (BaseIconFactory.ATLEAST_OREO && d instanceof AdaptiveIconDrawable && - mAdaptiveIconScale == SCALE_NOT_INITIALIZED) { - mAdaptiveIconScale = scale; - mAdaptiveIconBounds.set(mBounds); - } - return scale; + + // Area of the rectangle required to fit the convex hull + float rectArea = (bottomY + 1 - topY) * (rightX + 1 - leftX); + return getScale(area, rectArea, width * height); } /** diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 3e7f67bc64..819a551475 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -42,7 +42,7 @@ import android.util.Xml; import android.view.Display; import android.view.WindowManager; -import com.android.launcher3.folder.FolderShape; +import com.android.launcher3.graphics.IconShape; import com.android.launcher3.util.ConfigMonitor; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.MainThreadInitializedObject; @@ -304,7 +304,7 @@ public class InvariantDeviceProfile { changeFlags |= CHANGE_FLAG_ICON_PARAMS; } if (!iconShapePath.equals(oldProfile.iconShapePath)) { - FolderShape.init(context); + IconShape.init(context); } apply(context, changeFlags); diff --git a/src/com/android/launcher3/MainProcessInitializer.java b/src/com/android/launcher3/MainProcessInitializer.java index 93df02576a..95ee687319 100644 --- a/src/com/android/launcher3/MainProcessInitializer.java +++ b/src/com/android/launcher3/MainProcessInitializer.java @@ -19,7 +19,7 @@ package com.android.launcher3; import android.content.Context; import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.folder.FolderShape; +import com.android.launcher3.graphics.IconShape; import com.android.launcher3.logging.FileLog; import com.android.launcher3.util.ResourceBasedOverride; @@ -38,6 +38,6 @@ public class MainProcessInitializer implements ResourceBasedOverride { FileLog.setDir(context.getApplicationContext().getFilesDir()); FeatureFlags.initialize(context); SessionCommitReceiver.applyDefaultUserPrefs(context); - FolderShape.init(context); + IconShape.init(context); } } diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java index 2461e284dc..2450039896 100644 --- a/src/com/android/launcher3/folder/FolderAnimationManager.java +++ b/src/com/android/launcher3/folder/FolderAnimationManager.java @@ -19,7 +19,7 @@ package com.android.launcher3.folder; import static com.android.launcher3.BubbleTextView.TEXT_ALPHA_PROPERTY; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW; -import static com.android.launcher3.folder.FolderShape.getShape; +import static com.android.launcher3.graphics.IconShape.getShape; import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; import android.animation.Animator; diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java index 46df77a424..09e827649f 100644 --- a/src/com/android/launcher3/folder/PreviewBackground.java +++ b/src/com/android/launcher3/folder/PreviewBackground.java @@ -16,7 +16,7 @@ package com.android.launcher3.folder; -import static com.android.launcher3.folder.FolderShape.getShape; +import static com.android.launcher3.graphics.IconShape.getShape; import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; import android.animation.Animator; @@ -311,7 +311,7 @@ public class PreviewBackground { public Path getClipPath() { mPath.reset(); - getShape().addShape(mPath, getOffsetX(), getOffsetY(), getScaledRadius()); + getShape().addToPath(mPath, getOffsetX(), getOffsetY(), getScaledRadius()); return mPath; } diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java index ce83a177a8..c9566cb145 100644 --- a/src/com/android/launcher3/graphics/DrawableFactory.java +++ b/src/com/android/launcher3/graphics/DrawableFactory.java @@ -16,15 +16,15 @@ package com.android.launcher3.graphics; +import static com.android.launcher3.graphics.IconShape.getShapePath; + import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.Path; import android.graphics.Rect; -import android.graphics.drawable.AdaptiveIconDrawable; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Process; @@ -34,7 +34,6 @@ import android.util.ArrayMap; import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.R; -import com.android.launcher3.Utilities; import com.android.launcher3.icons.BitmapInfo; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.ResourceBasedOverride; @@ -47,16 +46,8 @@ import androidx.annotation.UiThread; public class DrawableFactory implements ResourceBasedOverride { public static final MainThreadInitializedObject INSTANCE = - new MainThreadInitializedObject<>(c -> { - DrawableFactory factory = Overrides.getObject(DrawableFactory.class, - c.getApplicationContext(), R.string.drawable_factory_class); - factory.mContext = c; - return factory; - }); - - - private Context mContext; - private Path mPreloadProgressPath; + new MainThreadInitializedObject<>(c -> Overrides.getObject(DrawableFactory.class, + c.getApplicationContext(), R.string.drawable_factory_class)); protected final UserHandle mMyUser = Process.myUserHandle(); protected final ArrayMap mUserBadges = new ArrayMap<>(); @@ -66,7 +57,7 @@ public class DrawableFactory implements ResourceBasedOverride { */ public FastBitmapDrawable newIcon(Context context, ItemInfoWithIcon info) { FastBitmapDrawable drawable = info.usingLowResIcon() - ? new PlaceHolderIconDrawable(info, getPreloadProgressPath(), context) + ? new PlaceHolderIconDrawable(info, getShapePath(), context) : new FastBitmapDrawable(info); drawable.setIsDisabled(info.isDisabled()); return drawable; @@ -74,7 +65,7 @@ public class DrawableFactory implements ResourceBasedOverride { public FastBitmapDrawable newIcon(Context context, BitmapInfo info, ActivityInfo target) { return info.isLowRes() - ? new PlaceHolderIconDrawable(info, getPreloadProgressPath(), context) + ? new PlaceHolderIconDrawable(info, getShapePath(), context) : new FastBitmapDrawable(info); } @@ -82,29 +73,7 @@ public class DrawableFactory implements ResourceBasedOverride { * Returns a FastBitmapDrawable with the icon. */ public PreloadIconDrawable newPendingIcon(Context context, ItemInfoWithIcon info) { - return new PreloadIconDrawable(info, getPreloadProgressPath(), context); - } - - protected Path getPreloadProgressPath() { - if (mPreloadProgressPath != null) { - return mPreloadProgressPath; - } - if (Utilities.ATLEAST_OREO) { - // Load the path from Mask Icon - AdaptiveIconDrawable icon = (AdaptiveIconDrawable) - mContext.getDrawable(R.drawable.adaptive_icon_drawable_wrapper); - icon.setBounds(0, 0, - PreloadIconDrawable.PATH_SIZE, PreloadIconDrawable.PATH_SIZE); - mPreloadProgressPath = icon.getIconMask(); - } else { - - // Create a circle static from top center and going clockwise. - Path p = new Path(); - p.moveTo(PreloadIconDrawable.PATH_SIZE / 2, 0); - p.addArc(0, 0, PreloadIconDrawable.PATH_SIZE, PreloadIconDrawable.PATH_SIZE, -90, 360); - mPreloadProgressPath = p; - } - return mPreloadProgressPath; + return new PreloadIconDrawable(info, getShapePath(), context); } /** diff --git a/src/com/android/launcher3/folder/FolderShape.java b/src/com/android/launcher3/graphics/IconShape.java similarity index 86% rename from src/com/android/launcher3/folder/FolderShape.java rename to src/com/android/launcher3/graphics/IconShape.java index ec6078e83b..88e4452e17 100644 --- a/src/com/android/launcher3/folder/FolderShape.java +++ b/src/com/android/launcher3/graphics/IconShape.java @@ -13,7 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.launcher3.folder; +package com.android.launcher3.graphics; + +import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -31,7 +33,6 @@ import android.graphics.Path; import android.graphics.Rect; import android.graphics.Region; import android.graphics.Region.Op; -import android.graphics.RegionIterator; import android.graphics.drawable.AdaptiveIconDrawable; import android.graphics.drawable.ColorDrawable; import android.os.Build; @@ -45,6 +46,8 @@ import android.view.ViewOutlineProvider; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.RoundedRectRevealOutlineProvider; +import com.android.launcher3.icons.GraphicsUtils; +import com.android.launcher3.icons.IconNormalizer; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.Themes; import com.android.launcher3.views.ClipPathView; @@ -59,22 +62,39 @@ import java.util.List; import androidx.annotation.Nullable; /** - * Abstract representation of the shape of a folder icon + * Abstract representation of the shape of an icon shape */ -public abstract class FolderShape { +public abstract class IconShape { - private static FolderShape sInstance = new Circle(); + private static IconShape sInstance = new Circle(); + private static Path sShapePath; + private static float sNormalizationScale = ICON_VISIBLE_AREA_FACTOR; - public static FolderShape getShape() { + public static final int DEFAULT_PATH_SIZE = 100; + + public static IconShape getShape() { return sInstance; } + public static Path getShapePath() { + if (sShapePath == null) { + Path p = new Path(); + getShape().addToPath(p, 0, 0, DEFAULT_PATH_SIZE * 0.5f); + sShapePath = p; + } + return sShapePath; + } + + public static float getNormalizationScale() { + return sNormalizationScale; + } + private SparseArray mAttrs; public abstract void drawShape(Canvas canvas, float offsetX, float offsetY, float radius, Paint paint); - public abstract void addShape(Path path, float offsetX, float offsetY, float radius); + public abstract void addToPath(Path path, float offsetX, float offsetY, float radius); public abstract Animator createRevealAnimator(T target, Rect startRect, Rect endRect, float endRadius, boolean isReversed); @@ -87,7 +107,7 @@ public abstract class FolderShape { /** * Abstract shape where the reveal animation is a derivative of a round rect animation */ - private static abstract class SimpleRectShape extends FolderShape { + private static abstract class SimpleRectShape extends IconShape { @Override public final Animator createRevealAnimator(T target, @@ -107,7 +127,7 @@ public abstract class FolderShape { /** * Abstract shape which draws using {@link Path} */ - private static abstract class PathShape extends FolderShape { + private static abstract class PathShape extends IconShape { private final Path mTmpPath = new Path(); @@ -115,7 +135,7 @@ public abstract class FolderShape { public final void drawShape(Canvas canvas, float offsetX, float offsetY, float radius, Paint paint) { mTmpPath.reset(); - addShape(mTmpPath, offsetX, offsetY, radius); + addToPath(mTmpPath, offsetX, offsetY, radius); canvas.drawPath(mTmpPath, paint); } @@ -166,7 +186,7 @@ public abstract class FolderShape { } @Override - public void addShape(Path path, float offsetX, float offsetY, float radius) { + public void addToPath(Path path, float offsetX, float offsetY, float radius) { path.addCircle(radius + offsetX, radius + offsetY, radius, Path.Direction.CW); } @@ -196,7 +216,7 @@ public abstract class FolderShape { } @Override - public void addShape(Path path, float offsetX, float offsetY, float radius) { + public void addToPath(Path path, float offsetX, float offsetY, float radius) { float cx = radius + offsetX; float cy = radius + offsetY; float cr = radius * mRadiusRatio; @@ -223,7 +243,7 @@ public abstract class FolderShape { } @Override - public void addShape(Path p, float offsetX, float offsetY, float r1) { + public void addToPath(Path p, float offsetX, float offsetY, float r1) { float r2 = r1 * mRadiusRatio; float cx = r1 + offsetX; float cy = r1 + offsetY; @@ -274,7 +294,7 @@ public abstract class FolderShape { } @Override - public void addShape(Path p, float offsetX, float offsetY, float r) { + public void addToPath(Path p, float offsetX, float offsetY, float r) { float cx = r + offsetX; float cy = r + offsetY; float control = r - r * mRadiusRatio; @@ -358,7 +378,7 @@ public abstract class FolderShape { pickBestShape(context); } - private static FolderShape getShapeDefinition(String type, float radius) { + private static IconShape getShapeDefinition(String type, float radius) { switch (type) { case "Circle": return new Circle(); @@ -373,8 +393,8 @@ public abstract class FolderShape { } } - private static List getAllShapes(Context context) { - ArrayList result = new ArrayList<>(); + private static List getAllShapes(Context context) { + ArrayList result = new ArrayList<>(); try (XmlResourceParser parser = context.getResources().getXml(R.xml.folder_shapes)) { // Find the root tag @@ -393,7 +413,7 @@ public abstract class FolderShape { if (type == XmlPullParser.START_TAG) { AttributeSet attrs = Xml.asAttributeSet(parser); TypedArray a = context.obtainStyledAttributes(attrs, radiusAttr); - FolderShape shape = getShapeDefinition(parser.getName(), a.getFloat(0, 1)); + IconShape shape = getShapeDefinition(parser.getName(), a.getFloat(0, 1)); a.recycle(); shape.mAttrs = Themes.createValueMap(context, attrs, keysToIgnore); @@ -409,7 +429,7 @@ public abstract class FolderShape { @TargetApi(Build.VERSION_CODES.O) protected static void pickBestShape(Context context) { // Pick any large size - int size = 200; + final int size = 200; Region full = new Region(0, 0, size, size); Region iconR = new Region(); @@ -420,23 +440,17 @@ public abstract class FolderShape { Path shapePath = new Path(); Region shapeR = new Region(); - Rect tempRect = new Rect(); // Find the shape with minimum area of divergent region. int minArea = Integer.MAX_VALUE; - FolderShape closestShape = null; - for (FolderShape shape : getAllShapes(context)) { + IconShape closestShape = null; + for (IconShape shape : getAllShapes(context)) { shapePath.reset(); - shape.addShape(shapePath, 0, 0, size / 2f); + shape.addToPath(shapePath, 0, 0, size / 2f); shapeR.setPath(shapePath, full); shapeR.op(iconR, Op.XOR); - RegionIterator itr = new RegionIterator(shapeR); - int area = 0; - - while (itr.next(tempRect)) { - area += tempRect.width() * tempRect.height(); - } + int area = GraphicsUtils.getArea(shapeR); if (area < minArea) { minArea = area; closestShape = shape; @@ -446,5 +460,10 @@ public abstract class FolderShape { if (closestShape != null) { sInstance = closestShape; } + + // Initialize shape properties + drawable.setBounds(0, 0, DEFAULT_PATH_SIZE, DEFAULT_PATH_SIZE); + sShapePath = new Path(drawable.getIconMask()); + sNormalizationScale = IconNormalizer.normalizeAdaptiveIcon(drawable, size, null); } } diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java index d3a7955b2f..cc4c2efafb 100644 --- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java +++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java @@ -17,6 +17,8 @@ package com.android.launcher3.graphics; +import static com.android.launcher3.graphics.IconShape.DEFAULT_PATH_SIZE; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -55,8 +57,6 @@ public class PreloadIconDrawable extends FastBitmapDrawable { } }; - public static final int PATH_SIZE = 100; - private static final float PROGRESS_WIDTH = 7; private static final float PROGRESS_GAP = 2; private static final int MAX_PAINT_ALPHA = 255; @@ -123,14 +123,14 @@ public class PreloadIconDrawable extends FastBitmapDrawable { protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); mTmpMatrix.setScale( - (bounds.width() - 2 * PROGRESS_WIDTH - 2 * PROGRESS_GAP) / PATH_SIZE, - (bounds.height() - 2 * PROGRESS_WIDTH - 2 * PROGRESS_GAP) / PATH_SIZE); + (bounds.width() - 2 * PROGRESS_WIDTH - 2 * PROGRESS_GAP) / DEFAULT_PATH_SIZE, + (bounds.height() - 2 * PROGRESS_WIDTH - 2 * PROGRESS_GAP) / DEFAULT_PATH_SIZE); mTmpMatrix.postTranslate( bounds.left + PROGRESS_WIDTH + PROGRESS_GAP, bounds.top + PROGRESS_WIDTH + PROGRESS_GAP); mProgressPath.transform(mTmpMatrix, mScaledTrackPath); - float scale = bounds.width() / PATH_SIZE; + float scale = bounds.width() / DEFAULT_PATH_SIZE; mProgressPaint.setStrokeWidth(PROGRESS_WIDTH * scale); mShadowBitmap = getShadowBitmap(bounds.width(), bounds.height(), diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 020e3809e1..e2c8c149c0 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -54,7 +54,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.FolderAdaptiveIcon; import com.android.launcher3.folder.FolderIcon; -import com.android.launcher3.folder.FolderShape; +import com.android.launcher3.graphics.IconShape; import com.android.launcher3.graphics.ShiftedBitmapDrawable; import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.popup.SystemShortcut; @@ -144,7 +144,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, mTaskCornerRadius = cornerRadius; if (mIsAdaptiveIcon && shapeRevealProgress >= 0) { if (mRevealAnimator == null) { - mRevealAnimator = (ValueAnimator) FolderShape.getShape().createRevealAnimator(this, + mRevealAnimator = (ValueAnimator) IconShape.getShape().createRevealAnimator(this, mStartRevealRect, mEndRevealRect, mTaskCornerRadius / scale, !isOpening); mRevealAnimator.addListener(new AnimatorListenerAdapter() { @Override From abb55950f6bba310816fdbf95f9f9ebd53e611f2 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 3 May 2019 13:20:30 -0700 Subject: [PATCH 08/72] Updating floating animation target if layout changes during the animation Bug: 131360075 Change-Id: Id71e154aa59177955baa9daab00ecf11ee7d8c43 --- .../WindowTransformSwipeHandler.java | 4 +- .../quickstep/util/RectFSpringAnim.java | 9 ++ .../launcher3/anim/FlingSpringAnim.java | 19 +++- .../launcher3/views/FloatingIconView.java | 94 ++++++++++++++----- 4 files changed, 101 insertions(+), 25 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 9c6102e22c..0caef6fae7 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -1061,7 +1061,9 @@ public class WindowTransformSwipeHandler RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect); if (isFloatingIconView) { - anim.addAnimatorListener((FloatingIconView) floatingView); + FloatingIconView fiv = (FloatingIconView) floatingView; + anim.addAnimatorListener(fiv); + fiv.setOnTargetChangeListener(anim::onTargetPositionChanged); } AnimatorPlaybackController homeAnim = homeAnimationFactory.createActivityAnimationToHome(); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java index 7159e7c721..40b9c4dcec 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java @@ -113,6 +113,15 @@ public class RectFSpringAnim { mCurrentCenterY = mStartRect.centerY(); } + public void onTargetPositionChanged() { + if (mRectXAnim != null && mRectXAnim.getTargetPosition() != mTargetRect.centerX()) { + mRectXAnim.updatePosition(mCurrentCenterX, mTargetRect.centerX()); + } + if (mRectYAnim != null && mRectYAnim.getTargetPosition() != mTargetRect.centerY()) { + mRectYAnim.updatePosition(mCurrentCenterY, mTargetRect.centerY()); + } + } + public void addOnUpdateListener(OnUpdateListener onUpdateListener) { mOnUpdateListeners.add(onUpdateListener); } diff --git a/src/com/android/launcher3/anim/FlingSpringAnim.java b/src/com/android/launcher3/anim/FlingSpringAnim.java index 45d49e8706..bb0f855d8c 100644 --- a/src/com/android/launcher3/anim/FlingSpringAnim.java +++ b/src/com/android/launcher3/anim/FlingSpringAnim.java @@ -36,6 +36,8 @@ public class FlingSpringAnim { private final FlingAnimation mFlingAnim; private SpringAnimation mSpringAnim; + private float mTargetPosition; + public FlingSpringAnim(K object, FloatPropertyCompat property, float startPosition, float targetPosition, float startVelocity, OnAnimationEndListener onEndListener) { mFlingAnim = new FlingAnimation(object, property) @@ -44,10 +46,12 @@ public class FlingSpringAnim { .setStartVelocity(startVelocity) .setMinValue(Math.min(startPosition, targetPosition)) .setMaxValue(Math.max(startPosition, targetPosition)); + mTargetPosition = targetPosition; + mFlingAnim.addEndListener(((animation, canceled, value, velocity) -> { mSpringAnim = new SpringAnimation(object, property) .setStartVelocity(velocity) - .setSpring(new SpringForce(targetPosition) + .setSpring(new SpringForce(mTargetPosition) .setStiffness(SPRING_STIFFNESS) .setDampingRatio(SPRING_DAMPING)); mSpringAnim.addEndListener(onEndListener); @@ -55,6 +59,19 @@ public class FlingSpringAnim { })); } + public float getTargetPosition() { + return mTargetPosition; + } + + public void updatePosition(float startPosition, float targetPosition) { + mFlingAnim.setMinValue(Math.min(startPosition, targetPosition)) + .setMaxValue(Math.max(startPosition, targetPosition)); + mTargetPosition = targetPosition; + if (mSpringAnim != null) { + mSpringAnim.animateToFinalPosition(mTargetPosition); + } + } + public void start() { mFlingAnim.start(); } diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 020e3809e1..3c874cced2 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -25,7 +25,6 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.TargetApi; -import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Outline; @@ -42,6 +41,7 @@ import android.os.Looper; import android.view.View; import android.view.ViewGroup; import android.view.ViewOutlineProvider; +import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.ImageView; import com.android.launcher3.BubbleTextView; @@ -67,16 +67,19 @@ import androidx.annotation.WorkerThread; * A view that is created to look like another view with the purpose of creating fluid animations. */ @TargetApi(Build.VERSION_CODES.Q) -public class FloatingIconView extends View implements Animator.AnimatorListener, ClipPathView { +public class FloatingIconView extends View implements + Animator.AnimatorListener, ClipPathView, OnGlobalLayoutListener { public static final float SHAPE_PROGRESS_DURATION = 0.15f; private static final int FADE_DURATION_MS = 200; private static final Rect sTmpRect = new Rect(); + private static final RectF sTmpRectF = new RectF(); private static final Object[] sTmpObjArray = new Object[1]; private Runnable mEndRunnable; private CancellationSignal mLoadIconSignal; + private final Launcher mLauncher; private final int mBlurSizeOutline; private boolean mIsVerticalBarLayout = false; @@ -91,6 +94,10 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, private Path mClipPath; private float mTaskCornerRadius; + private View mOriginalIcon; + private RectF mPositionOut; + private Runnable mOnTargetChangeRunnable; + private final Rect mFinalDrawableBounds = new Rect(); private final Rect mBgDrawableBounds = new Rect(); private float mBgDrawableStartScale = 1f; @@ -99,12 +106,24 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, private AnimatorSet mFadeAnimatorSet; private ListenerView mListenerView; - private FloatingIconView(Context context) { - super(context); - - mBlurSizeOutline = context.getResources().getDimensionPixelSize( + private FloatingIconView(Launcher launcher) { + super(launcher); + mLauncher = launcher; + mBlurSizeOutline = getResources().getDimensionPixelSize( R.dimen.blur_size_medium_outline); - mListenerView = new ListenerView(context, null); + mListenerView = new ListenerView(launcher, null); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + getViewTreeObserver().addOnGlobalLayoutListener(this); + } + + @Override + protected void onDetachedFromWindow() { + getViewTreeObserver().removeOnGlobalLayoutListener(this); + super.onDetachedFromWindow(); } /** @@ -188,21 +207,28 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, * @param v The view to copy * @param positionOut Rect that will hold the size and position of v. */ - private void matchPositionOf(Launcher launcher, View v, RectF positionOut) { - mRotation = getLocationBoundsForView(launcher, v, positionOut); + private void matchPositionOf(View v, RectF positionOut) { + float rotation = getLocationBoundsForView(v, positionOut); final LayoutParams lp = new LayoutParams( Math.round(positionOut.width()), Math.round(positionOut.height())); - lp.ignoreInsets = true; - - // Position the floating view exactly on top of the original - lp.leftMargin = Math.round(positionOut.left); - lp.topMargin = Math.round(positionOut.top); + updatePosition(rotation, positionOut, lp); setLayoutParams(lp); + } + + private void updatePosition(float rotation, RectF position, LayoutParams lp) { + mRotation = rotation; + mPositionOut.set(position); + lp.ignoreInsets = true; + // Position the floating view exactly on top of the original + lp.leftMargin = Math.round(position.left); + lp.topMargin = Math.round(position.top); + // Set the properties here already to make sure they are available when running the first // animation frame. layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin + lp.height); + } /** @@ -210,7 +236,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, * - For DeepShortcutView, we return the bounds of the icon view. * - For BubbleTextView, we return the icon bounds. */ - private float getLocationBoundsForView(Launcher launcher, View v, RectF outRect) { + private float getLocationBoundsForView(View v, RectF outRect) { boolean ignoreTransform = true; if (v instanceof DeepShortcutView) { v = ((DeepShortcutView) v).getBubbleText(); @@ -235,7 +261,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, float[] points = new float[] {iconBounds.left, iconBounds.top, iconBounds.right, iconBounds.bottom}; float[] rotation = new float[] {0}; - Utilities.getDescendantCoordRelativeToAncestor(v, launcher.getDragLayer(), points, + Utilities.getDescendantCoordRelativeToAncestor(v, mLauncher.getDragLayer(), points, false, ignoreTransform, rotation); outRect.set( Math.min(points[0], points[2]), @@ -246,7 +272,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, } @WorkerThread - private void getIcon(Launcher launcher, View v, ItemInfo info, boolean isOpening, + private void getIcon(View v, ItemInfo info, boolean isOpening, Runnable onIconLoadedRunnable, CancellationSignal loadIconSignal) { final LayoutParams lp = (LayoutParams) getLayoutParams(); Drawable drawable = null; @@ -263,7 +289,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, } } else { if (supportsAdaptiveIcons) { - drawable = Utilities.getFullDrawable(launcher, info, lp.width, lp.height, + drawable = Utilities.getFullDrawable(mLauncher, info, lp.width, lp.height, false, sTmpObjArray); if (!(drawable instanceof AdaptiveIconDrawable)) { // The drawable we get back is not an adaptive icon, so we need to use the @@ -275,7 +301,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, // Similar to DragView, we simply use the BubbleTextView icon here. drawable = btvIcon; } else { - drawable = Utilities.getFullDrawable(launcher, info, lp.width, lp.height, + drawable = Utilities.getFullDrawable(mLauncher, info, lp.width, lp.height, false, sTmpObjArray); } } @@ -328,7 +354,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, mStartRevealRect.inset(mBlurSizeOutline, mBlurSizeOutline); } - float aspectRatio = launcher.getDeviceProfile().aspectRatio; + float aspectRatio = mLauncher.getDeviceProfile().aspectRatio; if (mIsVerticalBarLayout) { lp.width = (int) Math.max(lp.width, lp.height * aspectRatio); } else { @@ -395,7 +421,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, Rect bounds = new Rect(0, 0, lp.width + mBlurSizeOutline, lp.height + mBlurSizeOutline); bounds.inset(mBlurSizeOutline / 2, mBlurSizeOutline / 2); - try (LauncherIcons li = LauncherIcons.obtain(Launcher.fromContext(getContext()))) { + try (LauncherIcons li = LauncherIcons.obtain(getContext())) { Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(drawable, null)); } @@ -452,6 +478,23 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, @Override public void onAnimationRepeat(Animator animator) {} + @Override + public void onGlobalLayout() { + if (mOriginalIcon.isAttachedToWindow() && mPositionOut != null) { + float rotation = getLocationBoundsForView(mOriginalIcon, sTmpRectF); + if (rotation != mRotation || !sTmpRectF.equals(mPositionOut)) { + updatePosition(rotation, sTmpRectF, (LayoutParams) getLayoutParams()); + if (mOnTargetChangeRunnable != null) { + mOnTargetChangeRunnable.run(); + } + } + } + } + + public void setOnTargetChangeListener(Runnable onTargetChangeListener) { + mOnTargetChangeRunnable = onTargetChangeListener; + } + /** * Creates a floating icon view for {@param originalView}. * @param originalView The view to copy @@ -468,8 +511,10 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, FloatingIconView view = recycle != null ? recycle : new FloatingIconView(launcher); view.mIsVerticalBarLayout = launcher.getDeviceProfile().isVerticalBarLayout(); + view.mOriginalIcon = originalView; + view.mPositionOut = positionOut; // Match the position of the original view. - view.matchPositionOf(launcher, originalView, positionOut); + view.matchPositionOf(originalView, positionOut); // Get the drawable on the background thread // Must be called after matchPositionOf so that we know what size to load. @@ -482,7 +527,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, }; CancellationSignal loadIconSignal = view.mLoadIconSignal; new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(() -> { - view.getIcon(launcher, originalView, (ItemInfo) originalView.getTag(), isOpening, + view.getIcon(originalView, (ItemInfo) originalView.getTag(), isOpening, onIconLoaded, loadIconSignal); }); } @@ -580,7 +625,10 @@ public class FloatingIconView extends View implements Animator.AnimatorListener, if (mFadeAnimatorSet != null) { mFadeAnimatorSet.cancel(); } + mPositionOut = null; mFadeAnimatorSet = null; mListenerView.setListener(null); + mOriginalIcon = null; + mOnTargetChangeRunnable = null; } } From 5bc79a2f6bd7fc5292c2b2a8f31e00b0f92f2f84 Mon Sep 17 00:00:00 2001 From: Tony Date: Sun, 5 May 2019 16:19:26 -0700 Subject: [PATCH 09/72] Fix recents attached state when quick switching Don't wait for drag slop before determining whether we are likely to quick switch (xDisplacement > yDisplacement). One glaring issue is that we only pass drag slop when swiping up, not sideways... so to pass drag slop going sideways we actually had to pass touch slop (24dp vs 10dp) and by that point the adjacent task was probably visible if you swiped fast, and thus we faded it in. Now we only look at raw displacement for purposes of determining whether we are likelyToStartNewTask, which should be much more consistent. Bug: 129985827 Change-Id: I31f8a9830681851093de2ce159da1a1dc4f7ef6a --- .../quickstep/OtherActivityInputConsumer.java | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java index 7fc5d502f9..af5afab29e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java @@ -21,7 +21,6 @@ import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_POINTER_UP; import static android.view.MotionEvent.ACTION_UP; import static android.view.MotionEvent.INVALID_POINTER_ID; - import static com.android.launcher3.Utilities.EDGE_NAV_BAR; import static com.android.launcher3.util.RaceConditionTracker.ENTER; import static com.android.launcher3.util.RaceConditionTracker.EXIT; @@ -44,6 +43,8 @@ import android.view.VelocityTracker; import android.view.ViewConfiguration; import android.view.WindowManager; +import androidx.annotation.UiThread; + import com.android.launcher3.R; import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.util.Preconditions; @@ -62,8 +63,6 @@ import com.android.systemui.shared.system.QuickStepContract; import java.util.function.Consumer; -import androidx.annotation.UiThread; - /** * Input consumer for handling events originating from an activity other than Launcher */ @@ -108,7 +107,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC // Might be displacement in X or Y, depending on the direction we are swiping from the nav bar. private float mStartDisplacement; - private float mStartDisplacementX; private Handler mMainThreadHandler; private Runnable mCancelRecentsAnimationRunnable = () -> { @@ -227,7 +225,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC if (Math.abs(displacement) > mDragSlop) { mPassedDragSlop = true; mStartDisplacement = displacement; - mStartDisplacementX = displacementX; } } } @@ -244,19 +241,20 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC if (!mPassedDragSlop) { mPassedDragSlop = true; mStartDisplacement = displacement; - mStartDisplacementX = displacementX; } notifyGestureStarted(); } } - if (mPassedDragSlop && mInteractionHandler != null) { - // Move - mInteractionHandler.updateDisplacement(displacement - mStartDisplacement); + if (mInteractionHandler != null) { + if (mPassedDragSlop) { + // Move + mInteractionHandler.updateDisplacement(displacement - mStartDisplacement); + } if (mMode == Mode.NO_BUTTON) { - float horizontalDist = Math.abs(displacementX - mStartDisplacementX); - float upDist = -(displacement - mStartDisplacement); + float horizontalDist = Math.abs(displacementX); + float upDist = -displacement; boolean isLikelyToStartNewTask = horizontalDist > upDist; mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement || isLikelyToStartNewTask); From fd329d19f96c05377bdb2e3c64fdd7950d67f435 Mon Sep 17 00:00:00 2001 From: Amin Shaikh Date: Fri, 3 May 2019 12:44:50 -0400 Subject: [PATCH 10/72] Add overlayable test resource for launcher3 Test: make RunLauncherRoboTests Change-Id: I36b4869d44e8896ef59dce6c554ef94a1fe4d9ec --- robolectric_tests/Android.mk | 2 ++ .../res/values/overlayable_icons_test.xml | 36 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 robolectric_tests/res/values/overlayable_icons_test.xml diff --git a/robolectric_tests/Android.mk b/robolectric_tests/Android.mk index a57b97a9ca..62915f2552 100644 --- a/robolectric_tests/Android.mk +++ b/robolectric_tests/Android.mk @@ -46,6 +46,8 @@ LOCAL_SDK_VERSION := current LOCAL_JAVA_LIBRARIES := \ LauncherRoboTests +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + LOCAL_TEST_PACKAGE := Launcher3 LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))../src \ diff --git a/robolectric_tests/res/values/overlayable_icons_test.xml b/robolectric_tests/res/values/overlayable_icons_test.xml new file mode 100644 index 0000000000..5144e52501 --- /dev/null +++ b/robolectric_tests/res/values/overlayable_icons_test.xml @@ -0,0 +1,36 @@ + + + + + @drawable/ic_corp + @drawable/ic_drag_handle + @drawable/ic_hourglass_top + @drawable/ic_info_no_shadow + @drawable/ic_install_no_shadow + @drawable/ic_palette + @drawable/ic_pin + @drawable/ic_remove_no_shadow + @drawable/ic_setting + @drawable/ic_smartspace_preferences + @drawable/ic_split_screen + @drawable/ic_uninstall_no_shadow + @drawable/ic_warning + @drawable/ic_widget + + From 0e44900863f34e167a20f4c4c9c0fb227fe9a741 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 26 Apr 2019 11:09:58 -0700 Subject: [PATCH 11/72] Continue gestures on interaction after recents animation finishes - Detect when start of the next task is interrupted with another gesture (after finishing the recents animation but before the next task is launched), and ensure that the next gesture is continued with another other activity input consumer (but without actual remote animation targets) Bug: 128376812 Test: Introduce artificial delay in recents animation finish, try to quick switch quickly Change-Id: I057a0b2c4b7e8636467e37f5bbc8800b46c24345 --- .../AppToOverviewAnimationProvider.java | 2 +- .../quickstep/OtherActivityInputConsumer.java | 2 +- .../quickstep/RecentsAnimationWrapper.java | 4 + .../android/quickstep/SwipeSharedState.java | 15 +++ .../com/android/quickstep/TaskViewUtils.java | 1 + .../quickstep/TouchInteractionService.java | 12 +- .../WindowTransformSwipeHandler.java | 71 +++++++---- .../util/SwipeAnimationTargetSet.java | 14 +++ .../android/quickstep/views/RecentsView.java | 117 ++++++++++++------ src/com/android/launcher3/PagedView.java | 4 + 10 files changed, 177 insertions(+), 65 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java index 624b3dc779..5e77e0adee 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java @@ -68,7 +68,7 @@ final class AppToOverviewAnimationProvider imple * @param wasVisible true if it was visible before */ boolean onActivityReady(T activity, Boolean wasVisible) { - activity.getOverviewPanel().setCurrentTask(mTargetTaskId); + activity.getOverviewPanel().showCurrentTask(mTargetTaskId); AbstractFloatingView.closeAllOpenViews(activity, wasVisible); ActivityControlHelper.AnimationFactory factory = mHelper.prepareRecentsUI(activity, wasVisible, diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java index 7fc5d502f9..f8721744cb 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java @@ -382,7 +382,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC mSwipeSharedState.canGestureBeContinued = endTarget != null && endTarget.canBeContinued; mSwipeSharedState.goingToLauncher = endTarget != null && endTarget.isLauncher; if (mSwipeSharedState.canGestureBeContinued) { - mInteractionHandler.cancelCurrentAnimation(); + mInteractionHandler.cancelCurrentAnimation(mSwipeSharedState); } else { mInteractionHandler.reset(); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java index ced9afab78..96d2dca971 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java @@ -59,6 +59,10 @@ public class RecentsAnimationWrapper { mInputProxySupplier = inputProxySupplier; } + public boolean hasTargets() { + return targetSet != null && targetSet.hasTargets(); + } + @UiThread public synchronized void setController(SwipeAnimationTargetSet targetSet) { Preconditions.assertUIThread(); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java index f393387a7f..194d073359 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java @@ -38,6 +38,8 @@ public class SwipeSharedState implements SwipeAnimationListener { public boolean canGestureBeContinued; public boolean goingToLauncher; + public boolean recentsAnimationFinishInterrupted; + public int nextRunningTaskId = -1; public SwipeSharedState(OverviewComponentObserver overviewComponentObserver) { mOverviewComponentObserver = overviewComponentObserver; @@ -114,9 +116,22 @@ public class SwipeSharedState implements SwipeAnimationListener { } } + /** + * Called when a recents animation has finished, but was interrupted before the next task was + * launched. The given {@param runningTaskId} should be used as the running task for the + * continuing input consumer. + */ + public void setRecentsAnimationFinishInterrupted(int runningTaskId) { + recentsAnimationFinishInterrupted = true; + nextRunningTaskId = runningTaskId; + mLastAnimationTarget = mLastAnimationTarget.cloneWithoutTargets(); + } + public void clearAllState() { clearListenerState(); canGestureBeContinued = false; + recentsAnimationFinishInterrupted = false; + nextRunningTaskId = -1; goingToLauncher = false; } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java index f95f9c27e5..d0ea73a6fe 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java @@ -150,6 +150,7 @@ public final class TaskViewUtils { @Override public void onUpdate(float percent) { + // TODO: Take into account the current fullscreen progress for animating the insets params.setProgress(1 - percent); RectF taskBounds = inOutHelper.applyTransform(targetSet, params); if (!skipViewChanges) { 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 b25865e640..61ae880cef 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -26,6 +26,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_N import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; import android.annotation.TargetApi; +import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.KeyguardManager; import android.app.Service; @@ -456,7 +457,7 @@ public class TouchInteractionService extends Service implements return new DeviceLockedInputConsumer(this); } - RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0); + final RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0); if (!useSharedState) { mSwipeSharedState.clearAllState(); } @@ -465,8 +466,15 @@ public class TouchInteractionService extends Service implements mOverviewComponentObserver.getActivityControlHelper(); InputConsumer base; - if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher) { + if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher + && !mSwipeSharedState.recentsAnimationFinishInterrupted) { base = InputConsumer.NO_OP; + } else if (mSwipeSharedState.recentsAnimationFinishInterrupted) { + // If the finish animation was interrupted, then continue using the other activity input + // consumer but with the next task as the running task + RunningTaskInfo info = new ActivityManager.RunningTaskInfo(); + info.id = mSwipeSharedState.nextRunningTaskId; + base = createOtherActivityInputConsumer(event, info); } else if (mSwipeSharedState.goingToLauncher || activityControl.isResumed()) { base = OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false); } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() && 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 0caef6fae7..936f0aad06 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -256,7 +256,9 @@ public class WindowTransformSwipeHandler private AnimationFactory mAnimationFactory = (t) -> { }; private LiveTileOverlay mLiveTileOverlay = new LiveTileOverlay(); + private boolean mCanceled; private boolean mWasLauncherAlreadyVisible; + private int mFinishingRecentsAnimationForNewTaskId = -1; private boolean mPassedOverviewThreshold; private boolean mGestureStarted; @@ -412,7 +414,6 @@ public class WindowTransformSwipeHandler mRecentsAnimationWrapper.runOnInit(() -> mRecentsAnimationWrapper.targetSet.addDependentTransactionApplier(applier)); }); - mRecentsView.setEnableFreeScroll(false); mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> { if (mGestureEndTarget != HOME) { @@ -500,10 +501,7 @@ public class WindowTransformSwipeHandler if (mContinuingLastGesture) { return; } - mRecentsView.setEnableDrawingLiveTile(false); - mRecentsView.showTask(mRunningTaskId); - mRecentsView.setRunningTaskHidden(true); - mRecentsView.setRunningTaskIconScaledDown(true); + mRecentsView.onGestureAnimationStart(mRunningTaskId); } private void launcherFrameDrawn() { @@ -679,7 +677,7 @@ public class WindowTransformSwipeHandler int runningTaskIndex = mRecentsView == null ? -1 : mRecentsView.getRunningTaskIndex(); if (runningTaskIndex >= 0) { for (int i = 0; i < mRecentsView.getTaskViewCount(); i++) { - if (i != runningTaskIndex) { + if (i != runningTaskIndex || !mRecentsAnimationWrapper.hasTargets()) { mRecentsView.getTaskViewAt(i).setFullscreenProgress(1 - mCurrentShift.value); } } @@ -838,9 +836,15 @@ public class WindowTransformSwipeHandler Interpolator interpolator = DEACCEL; final boolean goingToNewTask; if (mRecentsView != null) { - final int runningTaskIndex = mRecentsView.getRunningTaskIndex(); - final int taskToLaunch = mRecentsView.getNextPage(); - goingToNewTask = runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex; + if (!mRecentsAnimationWrapper.hasTargets()) { + // If there are no running tasks, then we can assume that this is a continuation of + // the last gesture, but after the recents animation has finished + goingToNewTask = true; + } else { + final int runningTaskIndex = mRecentsView.getRunningTaskIndex(); + final int taskToLaunch = mRecentsView.getNextPage(); + goingToNewTask = runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex; + } } else { goingToNewTask = false; } @@ -1124,14 +1128,22 @@ public class WindowTransformSwipeHandler mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false /* animate */, true /* freezeTaskList */); } else { + int taskId = mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).getTask().key.id; + mFinishingRecentsAnimationForNewTaskId = taskId; mRecentsAnimationWrapper.finish(true /* toRecents */, () -> { - mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask( - false /* animate */, true /* freezeTaskList */); + if (!mCanceled) { + TaskView nextTask = mRecentsView.getTaskView(taskId); + if (nextTask != null) { + nextTask.launchTask(false /* animate */, true /* freezeTaskList */); + doLogGesture(NEW_TASK); + } + reset(); + } + mCanceled = false; + mFinishingRecentsAnimationForNewTaskId = -1; }); } TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true); - doLogGesture(NEW_TASK); - reset(); } public void reset() { @@ -1142,12 +1154,26 @@ public class WindowTransformSwipeHandler * Cancels any running animation so that the active target can be overriden by a new swipe * handle (in case of quick switch). */ - public void cancelCurrentAnimation() { + public void cancelCurrentAnimation(SwipeSharedState sharedState) { + mCanceled = true; mCurrentShift.cancelAnimation(); if (mLauncherTransitionController != null && mLauncherTransitionController .getAnimationPlayer().isStarted()) { mLauncherTransitionController.getAnimationPlayer().cancel(); } + + if (mFinishingRecentsAnimationForNewTaskId != -1) { + // If we are canceling mid-starting a new task, switch to the screenshot since the + // recents animation has finished + switchToScreenshot(); + TaskView newRunningTaskView = mRecentsView.getTaskView( + mFinishingRecentsAnimationForNewTaskId); + int newRunningTaskId = newRunningTaskView != null + ? newRunningTaskView.getTask().key.id + : -1; + mRecentsView.setCurrentTask(newRunningTaskId); + sharedState.setRecentsAnimationFinishInterrupted(newRunningTaskId); + } } private void invalidateHandler() { @@ -1169,11 +1195,7 @@ public class WindowTransformSwipeHandler mLauncherTransitionController = null; } - mRecentsView.setEnableFreeScroll(true); - mRecentsView.setRunningTaskIconScaledDown(false); - mRecentsView.setOnScrollChangeListener(null); - mRecentsView.setRunningTaskHidden(false); - mRecentsView.setEnableDrawingLiveTile(true); + mRecentsView.onGestureAnimationEnd(); mActivity.getRootView().setOnApplyWindowInsetsListener(null); mActivity.getRootView().getOverlay().remove(mLiveTileOverlay); @@ -1194,6 +1216,9 @@ public class WindowTransformSwipeHandler private void switchToScreenshot() { if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { setStateOnUiThread(STATE_SCREENSHOT_CAPTURED); + } else if (!mRecentsAnimationWrapper.hasTargets()) { + // If there are no targets, then we don't need to capture anything + setStateOnUiThread(STATE_SCREENSHOT_CAPTURED); } else { boolean finishTransitionPosted = false; SwipeAnimationTargetSet controller = mRecentsAnimationWrapper.getController(); @@ -1241,6 +1266,9 @@ public class WindowTransformSwipeHandler private void finishCurrentTransitionToRecents() { if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { setStateOnUiThread(STATE_CURRENT_TASK_FINISHED); + } else if (!mRecentsAnimationWrapper.hasTargets()) { + // If there are no targets, then there is nothing to finish + setStateOnUiThread(STATE_CURRENT_TASK_FINISHED); } else { synchronized (mRecentsAnimationWrapper) { mRecentsAnimationWrapper.finish(true /* toRecents */, @@ -1267,10 +1295,7 @@ public class WindowTransformSwipeHandler } mActivityControlHelper.onSwipeUpComplete(mActivity); mRecentsAnimationWrapper.setCancelWithDeferredScreenshot(true); - - // Animate the first icon. - mRecentsView.animateUpRunningTaskIconScale(mLiveTileOverlay.cancelIconAnimation()); - mRecentsView.setSwipeDownShouldLaunchApp(true); + mRecentsView.onSwipeUpAnimationSuccess(); RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java index 83973fa7ec..f5a9e8a058 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java @@ -53,6 +53,20 @@ public class SwipeAnimationTargetSet extends RemoteAnimationTargetSet { this.mOnFinishListener = onFinishListener; } + public boolean hasTargets() { + return unfilteredApps.length != 0; + } + + /** + * Clones the target set without any actual targets. Used only when continuing a gesture after + * the actual recents animation has finished. + */ + public SwipeAnimationTargetSet cloneWithoutTargets() { + return new SwipeAnimationTargetSet(controller, new RemoteAnimationTargetCompat[0], + homeContentInsets, minimizedHomeBounds, mShouldMinimizeSplitScreen, + mOnFinishListener); + } + public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint) { mOnFinishListener.accept(this); BACKGROUND_EXECUTOR.execute(() -> { 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 b5f90a5550..525ead836a 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 @@ -744,9 +744,7 @@ public abstract class RecentsView extends PagedView impl public abstract void startHome(); public void reset() { - setRunningTaskViewShowScreenshot(false); - mRunningTaskId = -1; - mRunningTaskTileHidden = false; + setCurrentTask(-1); mIgnoreResetTaskId = -1; mTaskListChangeId = -1; @@ -757,6 +755,15 @@ public abstract class RecentsView extends PagedView impl setCurrentPage(0); } + public @Nullable TaskView getRunningTaskView() { + return getTaskView(mRunningTaskId); + } + + public int getRunningTaskIndex() { + TaskView tv = getRunningTaskView(); + return tv == null ? -1 : indexOfChild(tv); + } + /** * Reloads the view if anything in recents changed. */ @@ -767,14 +774,50 @@ public abstract class RecentsView extends PagedView impl } /** - * Ensures that the first task in the view represents {@param task} and reloads the view - * if needed. This allows the swipe-up gesture to assume that the first tile always - * corresponds to the correct task. - * All subsequent calls to reload will keep the task as the first item until {@link #reset()} - * is called. - * Also scrolls the view to this task + * Called when a gesture from an app is starting. */ - public void showTask(int runningTaskId) { + public void onGestureAnimationStart(int runningTaskId) { + // This needs to be called before the other states are set since it can create the task view + showCurrentTask(runningTaskId); + setEnableFreeScroll(false); + setEnableDrawingLiveTile(false); + setRunningTaskHidden(true); + setRunningTaskIconScaledDown(true); + } + + /** + * Called only when a swipe-up gesture from an app has completed. Only called after + * {@link #onGestureAnimationStart} and {@link #onGestureAnimationEnd()}. + */ + public void onSwipeUpAnimationSuccess() { + if (getRunningTaskView() != null) { + float startProgress = ENABLE_QUICKSTEP_LIVE_TILE.get() + ? mLiveTileOverlay.cancelIconAnimation() + : 0f; + animateUpRunningTaskIconScale(startProgress); + } + setSwipeDownShouldLaunchApp(true); + } + + /** + * Called when a gesture from an app has finished. + */ + public void onGestureAnimationEnd() { + setEnableFreeScroll(true); + setEnableDrawingLiveTile(true); + setOnScrollChangeListener(null); + setRunningTaskViewShowScreenshot(true); + setRunningTaskHidden(false); + animateUpRunningTaskIconScale(); + } + + /** + * Creates a task view (if necessary) to represent the task with the {@param runningTaskId}. + * + * All subsequent calls to reload will keep the task as the first item until {@link #reset()} + * is called. Also scrolls the view to this task. + */ + public void showCurrentTask(int runningTaskId) { if (getChildCount() == 0) { // Add an empty view for now until the task plan is loaded and applied final TaskView taskView = mTaskViewPool.getView(); @@ -789,16 +832,33 @@ public abstract class RecentsView extends PagedView impl new ComponentName("", ""), false); taskView.bind(mTmpRunningTask); } + + boolean runningTaskTileHidden = mRunningTaskTileHidden; setCurrentTask(runningTaskId); + setCurrentPage(getRunningTaskIndex()); + setRunningTaskViewShowScreenshot(false); + setRunningTaskHidden(runningTaskTileHidden); + + // Reload the task list + mTaskListChangeId = mModel.getTasks(this::applyLoadPlan); } - public @Nullable TaskView getRunningTaskView() { - return getTaskView(mRunningTaskId); - } + /** + * Sets the running task id, cleaning up the old running task if necessary. + * @param runningTaskId + */ + public void setCurrentTask(int runningTaskId) { + if (mRunningTaskId == runningTaskId) { + return; + } - public int getRunningTaskIndex() { - TaskView tv = getRunningTaskView(); - return tv == null ? -1 : indexOfChild(tv); + if (mRunningTaskId != -1) { + // Reset the state on the old running task view + setRunningTaskIconScaledDown(false); + setRunningTaskViewShowScreenshot(true); + setRunningTaskHidden(false); + } + mRunningTaskId = runningTaskId; } /** @@ -812,27 +872,6 @@ public abstract class RecentsView extends PagedView impl } } - /** - * Similar to {@link #showTask(int)} but does not put any restrictions on the first tile. - */ - public void setCurrentTask(int runningTaskId) { - boolean runningTaskTileHidden = mRunningTaskTileHidden; - boolean runningTaskIconScaledDown = mRunningTaskIconScaledDown; - - setRunningTaskIconScaledDown(false); - setRunningTaskHidden(false); - setRunningTaskViewShowScreenshot(true); - mRunningTaskId = runningTaskId; - setRunningTaskViewShowScreenshot(false); - setRunningTaskIconScaledDown(runningTaskIconScaledDown); - setRunningTaskHidden(runningTaskTileHidden); - - setCurrentPage(getRunningTaskIndex()); - - // Load the tasks (if the loading is already - mTaskListChangeId = mModel.getTasks(this::applyLoadPlan); - } - private void setRunningTaskViewShowScreenshot(boolean showScreenshot) { if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { TaskView runningTaskView = getRunningTaskView(); @@ -1520,7 +1559,9 @@ public abstract class RecentsView extends PagedView impl public abstract boolean shouldUseMultiWindowTaskSizeStrategy(); protected void onTaskLaunched(boolean success) { - resetTaskVisuals(); + if (success) { + resetTaskVisuals(); + } } @Override diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index b6404301dd..80e17c9ff1 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -1072,6 +1072,10 @@ public abstract class PagedView extends ViewGrou public void setEnableFreeScroll(boolean freeScroll) { + if (mFreeScroll == freeScroll) { + return; + } + boolean wasFreeScroll = mFreeScroll; mFreeScroll = freeScroll; From 16f2cd6cad2d71b9c8b155a0fd7a0109e0647a3a Mon Sep 17 00:00:00 2001 From: vadimt Date: Mon, 6 May 2019 10:38:08 -0700 Subject: [PATCH 12/72] Removing pressing home button twice Bug: 124239413 Change-Id: I8c14c7e939bb4442cc830dcff3ec266ed8ed7186 --- .../launcher3/tapl/LauncherInstrumentation.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index f5c5a8d0b2..87ef0448b8 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -374,16 +374,6 @@ public final class LauncherInstrumentation { event -> true, "Pressing Home didn't produce any events"); mDevice.waitForIdle(); - - // Temporarily press home twice as the first click sometimes gets ignored (b/124239413) - executeAndWaitForEvent( - () -> { - log("LauncherInstrumentation.pressHome before clicking"); - waitForSystemUiObject("home").click(); - }, - event -> true, - "Pressing Home didn't produce any events"); - mDevice.waitForIdle(); } try (LauncherInstrumentation.Closable c = addContextLayer( "performed action to switch to Home - " + action)) { From e1cb93f95744e5d2be5c48fc5bd3025f1453006b Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Fri, 3 May 2019 11:27:32 -0700 Subject: [PATCH 13/72] Always place notification dots directly on adaptive icon path - Calculate point on icon path nearest to top right corner, and use that as center for the dot - Cleanup code related to dot offset Test: Set each style (different icon shape) and verify dot is in correct placement for each of: - Folders - Icons in folders - Icons in all apps - Icons on workspace Bug: 124414511 Change-Id: I036ed3677e8af222f00d4fad4a36a7e4d9b49ad9 --- .../android/launcher3/icons/DotRenderer.java | 73 ++++++++++++------- src/com/android/launcher3/BubbleTextView.java | 4 +- src/com/android/launcher3/DeviceProfile.java | 7 +- .../android/launcher3/folder/FolderIcon.java | 9 ++- 4 files changed, 59 insertions(+), 34 deletions(-) diff --git a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java index 6bc859aa64..af07aa3a99 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java +++ b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java @@ -23,8 +23,10 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; -import android.graphics.Point; +import android.graphics.Path; +import android.graphics.PathMeasure; import android.graphics.Rect; +import android.graphics.RectF; import android.util.Log; import android.view.ViewDebug; @@ -36,33 +38,51 @@ public class DotRenderer { private static final String TAG = "DotRenderer"; // The dot size is defined as a percentage of the app icon size. - private static final float SIZE_PERCENTAGE = 0.38f; + private static final float SIZE_PERCENTAGE = 0.228f; - // Extra scale down of the dot - private static final float DOT_SCALE = 0.6f; - - // Offset the dot slightly away from the icon if there's space. - private static final float OFFSET_PERCENTAGE = 0.02f; - - private final float mDotCenterOffset; - private final int mOffset; private final float mCircleRadius; private final Paint mCirclePaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG); private final Bitmap mBackgroundWithShadow; private final float mBitmapOffset; - public DotRenderer(int iconSizePx) { - mDotCenterOffset = SIZE_PERCENTAGE * iconSizePx; - mOffset = (int) (OFFSET_PERCENTAGE * iconSizePx); + // Stores the center x and y position as a percentage (0 to 1) of the icon size + private final float[] mRightDotPosition; + private final float[] mLeftDotPosition; - int size = (int) (DOT_SCALE * mDotCenterOffset); + public DotRenderer(int iconSizePx, Path iconShapePath, int pathSize) { + int size = Math.round(SIZE_PERCENTAGE * iconSizePx); ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.TRANSPARENT); builder.ambientShadowAlpha = 88; mBackgroundWithShadow = builder.setupBlurForSize(size).createPill(size, size); mCircleRadius = builder.radius; mBitmapOffset = -mBackgroundWithShadow.getHeight() * 0.5f; // Same as width. + + // Find the points on the path that are closest to the top left and right corners. + mLeftDotPosition = getPathPoint(iconShapePath, pathSize, -1); + mRightDotPosition = getPathPoint(iconShapePath, pathSize, 1); + } + + private static float[] getPathPoint(Path path, float size, float direction) { + float halfSize = size / 2; + // Small delta so that we don't get a zero size triangle + float delta = 1; + + float x = halfSize + direction * halfSize; + Path trianglePath = new Path(); + trianglePath.moveTo(halfSize, halfSize); + trianglePath.lineTo(x + delta * direction, 0); + trianglePath.lineTo(x, -delta); + trianglePath.close(); + + trianglePath.op(path, Path.Op.INTERSECT); + float[] pos = new float[2]; + new PathMeasure(trianglePath, false).getPosTan(0, pos, null); + + pos[0] = pos[0] / size; + pos[1] = pos[1] / size; + return pos; } /** @@ -74,15 +94,21 @@ public class DotRenderer { return; } canvas.save(); - // We draw the dot relative to its center. - float dotCenterX = params.leftAlign - ? params.iconBounds.left + mDotCenterOffset / 2 - : params.iconBounds.right - mDotCenterOffset / 2; - float dotCenterY = params.iconBounds.top + mDotCenterOffset / 2; - int offsetX = Math.min(mOffset, params.spaceForOffset.x); - int offsetY = Math.min(mOffset, params.spaceForOffset.y); - canvas.translate(dotCenterX + offsetX, dotCenterY - offsetY); + Rect iconBounds = params.iconBounds; + float[] dotPosition = params.leftAlign ? mLeftDotPosition : mRightDotPosition; + float dotCenterX = iconBounds.left + iconBounds.width() * dotPosition[0]; + float dotCenterY = iconBounds.top + iconBounds.height() * dotPosition[1]; + + // Ensure dot fits entirely in canvas clip bounds. + Rect canvasBounds = canvas.getClipBounds(); + float offsetX = params.leftAlign + ? Math.max(0, canvasBounds.left - (dotCenterX + mBitmapOffset)) + : Math.min(0, canvasBounds.right - (dotCenterX - mBitmapOffset)); + float offsetY = Math.max(0, canvasBounds.top - (dotCenterY + mBitmapOffset)); + + // We draw the dot relative to its center. + canvas.translate(dotCenterX + offsetX, dotCenterY + offsetY); canvas.scale(params.scale, params.scale); mCirclePaint.setColor(Color.BLACK); @@ -102,9 +128,6 @@ public class DotRenderer { /** The progress of the animation, from 0 to 1. */ @ViewDebug.ExportedProperty(category = "notification dot") public float scale; - /** Overrides internally calculated offset if specified value is smaller. */ - @ViewDebug.ExportedProperty(category = "notification dot") - public Point spaceForOffset = new Point(); /** Whether the dot should align to the top left of the icon rather than the top right. */ @ViewDebug.ExportedProperty(category = "notification dot") public boolean leftAlign; diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 8291acc590..bff7f42131 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -46,10 +46,12 @@ import com.android.launcher3.dot.DotInfo; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.graphics.DrawableFactory; import com.android.launcher3.graphics.IconPalette; +import com.android.launcher3.graphics.IconShape; import com.android.launcher3.graphics.PreloadIconDrawable; import com.android.launcher3.icons.DotRenderer; import com.android.launcher3.icons.IconCache.IconLoadRequest; import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver; +import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.model.PackageItemInfo; import com.android.launcher3.views.ActivityContext; @@ -388,7 +390,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, protected void drawDotIfNecessary(Canvas canvas) { if (!mForceHideDot && (hasDot() || mDotParams.scale > 0)) { getIconBounds(mDotParams.iconBounds); - mDotParams.spaceForOffset.set((getWidth() - mIconSize) / 2, getPaddingTop()); + Utilities.scaleRectAboutCenter(mDotParams.iconBounds, IconShape.getNormalizationScale()); final int scrollX = getScrollX(); final int scrollY = getScrollY(); canvas.translate(scrollX, scrollY); diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index c0affb9445..3d2d7cf9e2 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -24,15 +24,13 @@ import android.graphics.PointF; import android.graphics.Rect; import android.util.DisplayMetrics; import android.view.Surface; -import android.view.View; import android.view.WindowManager; import com.android.launcher3.CellLayout.ContainerType; +import com.android.launcher3.graphics.IconShape; import com.android.launcher3.icons.DotRenderer; import com.android.launcher3.icons.IconNormalizer; -import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT; - public class DeviceProfile { public final InvariantDeviceProfile inv; @@ -240,7 +238,8 @@ public class DeviceProfile { updateWorkspacePadding(); // This is done last, after iconSizePx is calculated above. - mDotRenderer = new DotRenderer(iconSizePx); + mDotRenderer = new DotRenderer(iconSizePx, IconShape.getShapePath(), + IconShape.DEFAULT_PATH_SIZE); } public DeviceProfile copy(Context context) { diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 6fa9ba9ae5..8d9c52065b 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -36,6 +36,8 @@ import android.view.ViewDebug; import android.view.ViewGroup; import android.widget.FrameLayout; +import androidx.annotation.NonNull; + import com.android.launcher3.Alarm; import com.android.launcher3.AppInfo; import com.android.launcher3.BubbleTextView; @@ -50,11 +52,11 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherSettings; import com.android.launcher3.OnAlarmListener; import com.android.launcher3.R; -import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.SimpleOnStylusPressListener; import com.android.launcher3.StylusEventHelper; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; +import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.dot.FolderDotInfo; import com.android.launcher3.dragndrop.BaseItemDragListener; @@ -68,8 +70,6 @@ import com.android.launcher3.widget.PendingAddShortcutInfo; import java.util.ArrayList; import java.util.List; -import androidx.annotation.NonNull; - /** * An icon that can appear on in the workspace representing an {@link Folder}. */ @@ -510,10 +510,11 @@ public class FolderIcon extends FrameLayout implements FolderListener { Rect iconBounds = mDotParams.iconBounds; BubbleTextView.getIconBounds(this, iconBounds, mLauncher.getWallpaperDeviceProfile().iconSizePx); + float iconScale = (float) mBackground.previewSize / iconBounds.width(); + Utilities.scaleRectAboutCenter(iconBounds, iconScale); // If we are animating to the accepting state, animate the dot out. mDotParams.scale = Math.max(0, mDotScale - mBackground.getScaleProgress()); - mDotParams.spaceForOffset.set(getWidth() - iconBounds.right, iconBounds.top); mDotParams.color = mBackground.getDotColor(); mDotRenderer.draw(canvas, mDotParams); } From 0c947f97703b90a1cd9c5a20cdd316fa9e305d1d Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Mon, 6 May 2019 14:45:59 -0700 Subject: [PATCH 14/72] Update sysui flags - Launcher always controlls sysui flags during quickstep gestures. - If we passed window threshold (i.e. window isn't under system bars), use same flags as home screen - Else use sysui flags of the centermost TaskView - Update sysui flags based on home animation progress as well (otherwise we weren't applying home screen sysui flags until the end of the animation if you ended the swipe before crossing the window threshold). - Update sysui flags for quick switch from home - Specify that the activity controller animation is user-controlled so we don't reapply state when re-setting it. This happens when we get window insets, for instance, which we get when changing sysui flags. Bug: 131360249 Bug: 130020567 Change-Id: If82dbf75fe663fd690b816cdc1c4366cc2bf75b1 --- .../QuickSwitchTouchController.java | 10 ++++++++-- .../LauncherActivityControllerHelper.java | 2 +- .../quickstep/WindowTransformSwipeHandler.java | 17 +++++++++++++++-- 3 files changed, 24 insertions(+), 5 deletions(-) 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 81090c1c90..3b664b79dd 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 @@ -28,9 +28,13 @@ import static com.android.launcher3.anim.Interpolators.ACCEL_2; import static com.android.launcher3.anim.Interpolators.DEACCEL_2; import static com.android.launcher3.anim.Interpolators.INSTANT; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW; +import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD; import android.view.MotionEvent; +import androidx.annotation.Nullable; + import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager; @@ -45,8 +49,6 @@ import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; -import androidx.annotation.Nullable; - /** * Handles quick switching to a recent task from the home screen. */ @@ -128,6 +130,10 @@ public class QuickSwitchTouchController extends AbstractStateChangeTouchControll private void updateFullscreenProgress(float progress) { if (mTaskToLaunch != null) { mTaskToLaunch.setFullscreenProgress(progress); + int sysuiFlags = progress > UPDATE_SYSUI_FLAGS_THRESHOLD + ? mTaskToLaunch.getThumbnail().getSysUiStatusNavFlags() + : 0; + mLauncher.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, sysuiFlags); } } 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..e932452edd 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java @@ -270,9 +270,9 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe playScaleDownAnim(anim, activity, endState); anim.setDuration(transitionLength * 2); - activity.getStateManager().setCurrentAnimation(anim); AnimatorPlaybackController controller = AnimatorPlaybackController.wrap(anim, transitionLength * 2); + activity.getStateManager().setCurrentUserControlledAnimation(controller); // Since we are changing the start position of the UI, reapply the state, at the end controller.setEndAction(() -> { 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 936f0aad06..58ae5eb739 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -27,6 +27,7 @@ import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TI import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; import static com.android.launcher3.util.RaceConditionTracker.ENTER; import static com.android.launcher3.util.RaceConditionTracker.EXIT; +import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW; import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.HIDE; import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.PEEK; @@ -654,8 +655,7 @@ public class WindowTransformSwipeHandler mTransformParams.setProgress(shift).setOffsetX(offsetX).setOffsetScale(offsetScale); mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet, mTransformParams); - mRecentsAnimationWrapper.setWindowThresholdCrossed( - shift > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD); + updateSysUiFlags(shift); } if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { @@ -700,6 +700,18 @@ public class WindowTransformSwipeHandler ? 0 : (progress - mShiftAtGestureStart) / (1 - mShiftAtGestureStart)); } + private void updateSysUiFlags(float windowProgress) { + if (mRecentsView != null) { + // We will handle the sysui flags based on the centermost task view. + mRecentsAnimationWrapper.setWindowThresholdCrossed(true); + int sysuiFlags = windowProgress > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD + ? 0 + : mRecentsView.getTaskViewAt(mRecentsView.getPageNearestToCenterOfScreen()) + .getThumbnail().getSysUiStatusNavFlags(); + mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, sysuiFlags); + } + } + @Override public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) { DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext); @@ -1091,6 +1103,7 @@ public class WindowTransformSwipeHandler windowAlphaThreshold, mClipAnimationHelper.getCurrentCornerRadius(), false); } + updateSysUiFlags(Math.max(progress, mCurrentShift.value)); }); anim.addAnimatorListener(new AnimationSuccessListener() { @Override From 3539f32f97861e977f15324be351f17341ad93a8 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 6 May 2019 12:07:49 -0700 Subject: [PATCH 15/72] Enabling assistant and accessibility gesture while lock screen is showing This also enables the gesture when notification shade is down, as we do not have a separate shate for that right now Bug: 131847153 Change-Id: Ia1edaad197266fe01b5de7b2a6b46ca0ec6428bb --- .../quickstep/TouchInteractionService.java | 75 +++++++++++-------- 1 file changed, 44 insertions(+), 31 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 61ae880cef..a343a363d7 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -429,8 +429,7 @@ public class TouchInteractionService extends Service implements MotionEvent event = (MotionEvent) ev; TOUCH_INTERACTION_LOG.addLog("onMotionEvent", event.getActionMasked()); if (event.getAction() == ACTION_DOWN) { - if (isInValidSystemUiState() - && mSwipeTouchRegion.contains(event.getX(), event.getY())) { + if (mSwipeTouchRegion.contains(event.getX(), event.getY())) { boolean useSharedState = mConsumer.useSharedSwipeState(); mConsumer.onConsumerAboutToBeSwitched(); mConsumer = newConsumer(useSharedState, event); @@ -443,15 +442,45 @@ public class TouchInteractionService extends Service implements mUncheckedConsumer.onMotionEvent(event); } - private boolean isInValidSystemUiState() { - return (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0 - && (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0 - && !ActivityManagerWrapper.getInstance().isLockToAppActive(); - } private InputConsumer newConsumer(boolean useSharedState, MotionEvent event) { - // TODO: this makes a binder call every touch down. we should move to a listener pattern. - if (!mIsUserUnlocked || mKM.isDeviceLocked()) { + boolean validSystemUIFlags = (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0 + && (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0; + boolean topTaskLocked = ActivityManagerWrapper.getInstance().isLockToAppActive(); + boolean isInValidSystemUiState = validSystemUIFlags && !topTaskLocked; + + if (!mIsUserUnlocked) { + if (isInValidSystemUiState) { + // This handles apps launched in direct boot mode (e.g. dialer) as well as apps + // launched while device is locked even after exiting direct boot mode (e.g. camera). + return new DeviceLockedInputConsumer(this); + } else { + return InputConsumer.NO_OP; + } + } + + InputConsumer base = isInValidSystemUiState + ? newBaseConsumer(useSharedState, event) : InputConsumer.NO_OP; + if (mMode == Mode.NO_BUTTON) { + final ActivityControlHelper activityControl = + mOverviewComponentObserver.getActivityControlHelper(); + if (mAssistantAvailable && !topTaskLocked + && AssistantTouchConsumer.withinTouchRegion(this, event)) { + base = new AssistantTouchConsumer(this, mISystemUiProxy, activityControl, base, + mInputMonitorCompat); + } + + if ((mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0) { + base = new AccessibilityInputConsumer(this, mISystemUiProxy, + (mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0, base, + mInputMonitorCompat); + } + } + return base; + } + + private InputConsumer newBaseConsumer(boolean useSharedState, MotionEvent event) { + if (mKM.isDeviceLocked()) { // This handles apps launched in direct boot mode (e.g. dialer) as well as apps launched // while device is locked even after exiting direct boot mode (e.g. camera). return new DeviceLockedInputConsumer(this); @@ -465,42 +494,26 @@ public class TouchInteractionService extends Service implements final ActivityControlHelper activityControl = mOverviewComponentObserver.getActivityControlHelper(); - InputConsumer base; if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher && !mSwipeSharedState.recentsAnimationFinishInterrupted) { - base = InputConsumer.NO_OP; + return InputConsumer.NO_OP; } else if (mSwipeSharedState.recentsAnimationFinishInterrupted) { // If the finish animation was interrupted, then continue using the other activity input // consumer but with the next task as the running task RunningTaskInfo info = new ActivityManager.RunningTaskInfo(); info.id = mSwipeSharedState.nextRunningTaskId; - base = createOtherActivityInputConsumer(event, info); + return createOtherActivityInputConsumer(event, info); } else if (mSwipeSharedState.goingToLauncher || activityControl.isResumed()) { - base = OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false); + return OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false); } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityControl.isInLiveTileMode()) { - base = OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false); + return OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false); } else if (mGestureBlockingActivity != null && runningTaskInfo != null && mGestureBlockingActivity.equals(runningTaskInfo.topActivity)) { - base = InputConsumer.NO_OP; + return InputConsumer.NO_OP; } else { - base = createOtherActivityInputConsumer(event, runningTaskInfo); + return createOtherActivityInputConsumer(event, runningTaskInfo); } - - if (mMode == Mode.NO_BUTTON) { - if (mAssistantAvailable && AssistantTouchConsumer.withinTouchRegion(this, event)) { - base = new AssistantTouchConsumer(this, mISystemUiProxy, activityControl, base, - mInputMonitorCompat); - } - - if ((mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0) { - base = new AccessibilityInputConsumer(this, mISystemUiProxy, - (mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0, base, - mInputMonitorCompat); - } - } - - return base; } private OtherActivityInputConsumer createOtherActivityInputConsumer(MotionEvent event, From f55e87c36bab24f8619b42f3425b3236a85c50b9 Mon Sep 17 00:00:00 2001 From: vadimt Date: Mon, 6 May 2019 16:13:48 -0700 Subject: [PATCH 16/72] Fixing AppPredictionsUITests Bug: 131772711 Bug: 131188880 Change-Id: Ic6556b26464766294ced7f85af716b0a4eb51ca1 --- .../src/com/android/quickstep/AppPredictionsUITests.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java index 1817135504..ffe3633353 100644 --- a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java +++ b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java @@ -40,7 +40,6 @@ import com.android.launcher3.model.AppLaunchTracker; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -70,6 +69,8 @@ public class AppPredictionsUITests extends AbstractQuickStepTest { // Disable app tracker AppLaunchTracker.INSTANCE.initializeForTesting(new AppLaunchTracker()); + PredictionUiStateManager.INSTANCE.initializeForTesting(null); + mCallback = PredictionUiStateManager.INSTANCE.get(mTargetContext).appPredictorCallback( Client.HOME); @@ -85,7 +86,6 @@ public class AppPredictionsUITests extends AbstractQuickStepTest { * Test that prediction UI is updated as soon as we get predictions from the system */ @Test - @Ignore // b/131772711: this really fails (when being run as a part of the whole test suite)! public void testPredictionExistsInAllApps() { mActivityMonitor.startLauncher(); mLauncher.pressHome().switchToAllApps(); @@ -118,7 +118,6 @@ public class AppPredictionsUITests extends AbstractQuickStepTest { } @Test - @Ignore // b/131772711 - this was failing in the lab public void testPredictionsDisabled() { mActivityMonitor.startLauncher(); sendPredictionUpdate(); From 808cd8dfdb10c2ba9dd6921045b6286d076947ea Mon Sep 17 00:00:00 2001 From: Miranda Kephart Date: Tue, 7 May 2019 11:48:47 -0400 Subject: [PATCH 17/72] Change haptic on Assistant gesture to '13' Fixes: 130303405 Test: manual Change-Id: Ic116e0920782c2f138740ae377c0039eaff5540d --- .../src/com/android/quickstep/AssistantTouchConsumer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java index 829e4783d8..d8c5e53bd0 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java @@ -213,9 +213,9 @@ public class AssistantTouchConsumer extends DelegateInputConsumer { BaseDraggingActivity launcherActivity = mActivityControlHelper.getCreatedActivity(); if (launcherActivity != null) { - launcherActivity.getRootView(). - performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, - HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + launcherActivity.getRootView().performHapticFeedback( + 13, // HapticFeedbackConstants.GESTURE_END + HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); } mSysUiProxy.startAssistant(args); From 141ab952e77a179661cac5d01c8e33863f47a482 Mon Sep 17 00:00:00 2001 From: Kevin Date: Mon, 6 May 2019 13:13:27 -0700 Subject: [PATCH 18/72] Ensure Recents Go layout finishes before remote anim begins We would like to assume a correct up-to-date layout for Go recents before the remote animation begins to ensure correctness of the app to overview transition and allow for animating all the newly laid out task views in sync. We do this by checking if recents is the remote target we are animating to and if so, checking if the view is ready. If it is not, then we delay the remote animation until the layout is finished and everything is attached. Bug: 132112131 Fix: 132108983 Test: Run Recents Go, have no tasks in recents, open app, press overview to go to recents Test: Test on AOSP and NexusLauncher that animations to Launcher work as before (both to recents and to home) Change-Id: Id74d90cffc9fe5db1dbb5fe63b8819c7436fef21 --- .../GoLauncherAppTransitionManagerImpl.java | 42 ++++- .../quickstep/views/IconRecentsView.java | 38 +++++ .../QuickstepAppTransitionManagerImpl.java | 144 ++++++++++-------- 3 files changed, 158 insertions(+), 66 deletions(-) diff --git a/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java b/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java index d189c504cd..bcb1f5c3b4 100644 --- a/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java +++ b/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java @@ -1,16 +1,20 @@ package com.android.launcher3; +import static com.android.launcher3.Utilities.postAsyncCallback; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.quickstep.TaskUtils.taskIsATargetWithMode; import static com.android.quickstep.views.IconRecentsView.CONTENT_ALPHA; +import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; -import android.app.ActivityOptions; import android.content.Context; +import android.os.Handler; import android.view.View; import com.android.quickstep.views.IconRecentsView; +import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; /** @@ -28,6 +32,12 @@ public final class GoLauncherAppTransitionManagerImpl extends QuickstepAppTransi return mLauncher.getStateManager().getState().overviewUi; } + @Override + RemoteAnimationRunnerCompat getWallpaperOpenRunner(boolean fromUnlock) { + return new GoWallpaperOpenLauncherAnimationRunner(mHandler, + false /* startAtFrontOfQueue */, fromUnlock); + } + @Override protected void composeRecentsLaunchAnimator(AnimatorSet anim, View v, RemoteAnimationTargetCompat[] targets, boolean launcherClosing) { @@ -51,4 +61,34 @@ public final class GoLauncherAppTransitionManagerImpl extends QuickstepAppTransi return mLauncher.getStateManager()::reapplyState; } + + /** + * Remote animation runner for animation from app to Launcher. For Go, when going to recents, + * we need to ensure that the recents view is ready for remote animation before starting. + */ + private final class GoWallpaperOpenLauncherAnimationRunner extends + WallpaperOpenLauncherAnimationRunner { + public GoWallpaperOpenLauncherAnimationRunner(Handler handler, boolean startAtFrontOfQueue, + boolean fromUnlock) { + super(handler, startAtFrontOfQueue, fromUnlock); + } + + @Override + public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats, + AnimationResult result) { + boolean isGoingToRecents = + taskIsATargetWithMode(targetCompats, mLauncher.getTaskId(), MODE_OPENING) + && (mLauncher.getStateManager().getState() == LauncherState.OVERVIEW); + if (isGoingToRecents) { + IconRecentsView recentsView = mLauncher.getOverviewPanel(); + if (!recentsView.isReadyForRemoteAnim()) { + recentsView.setOnReadyForRemoteAnimCallback(() -> + postAsyncCallback(mHandler, () -> onCreateAnimation(targetCompats, result)) + ); + return; + } + } + super.onCreateAnimation(targetCompats, result); + } + } } diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index 7225e572ba..07faa4bbdd 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -378,6 +378,36 @@ public final class IconRecentsView extends FrameLayout implements Insettable { return view.getThumbnailView(); } + /** + * Whether this view has processed all data changes and is ready to animate from the app to + * the overview. + * + * @return true if ready to animate app to overview, false otherwise + */ + public boolean isReadyForRemoteAnim() { + return !mTaskRecyclerView.hasPendingAdapterUpdates(); + } + + /** + * Set a callback for whenever this view is ready to do a remote animation from the app to + * overview. See {@link #isReadyForRemoteAnim()}. + * + * @param callback callback to run when view is ready to animate + */ + public void setOnReadyForRemoteAnimCallback(onReadyForRemoteAnimCallback callback) { + mTaskRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener( + new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + if (isReadyForRemoteAnim()) { + callback.onReadyForRemoteAnim(); + mTaskRecyclerView.getViewTreeObserver(). + removeOnGlobalLayoutListener(this); + } + } + }); + } + /** * Clear all tasks and animate out. */ @@ -557,4 +587,12 @@ public final class IconRecentsView extends FrameLayout implements Insettable { mTaskRecyclerView.setPadding(insets.left, insets.top, insets.right, insets.bottom); mTaskRecyclerView.invalidateItemDecorations(); } + + /** + * Callback for when this view is ready for a remote animation from app to overview. + */ + public interface onReadyForRemoteAnimCallback { + + void onReadyForRemoteAnim(); + } } diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java index e1a115ad3d..3b75304b7c 100644 --- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java @@ -132,7 +132,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans private final DragLayer mDragLayer; private final AlphaProperty mDragLayerAlpha; - private final Handler mHandler; + final Handler mHandler; private final boolean mIsRtl; private final float mContentTransY; @@ -573,70 +573,9 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans * @return Runner that plays when user goes to Launcher * ie. pressing home, swiping up from nav bar. */ - private RemoteAnimationRunnerCompat getWallpaperOpenRunner(boolean fromUnlock) { - return new LauncherAnimationRunner(mHandler, false /* startAtFrontOfQueue */) { - @Override - public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats, - AnimationResult result) { - if (!mLauncher.hasBeenResumed()) { - // If launcher is not resumed, wait until new async-frame after resume - mLauncher.setOnResumeCallback(() -> - postAsyncCallback(mHandler, () -> - onCreateAnimation(targetCompats, result))); - return; - } - - if (mLauncher.hasSomeInvisibleFlag(PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION)) { - mLauncher.addForceInvisibleFlag(INVISIBLE_BY_PENDING_FLAGS); - mLauncher.getStateManager().moveToRestState(); - } - - AnimatorSet anim = null; - RemoteAnimationProvider provider = mRemoteAnimationProvider; - if (provider != null) { - anim = provider.createWindowAnimation(targetCompats); - } - - if (anim == null) { - anim = new AnimatorSet(); - anim.play(fromUnlock - ? getUnlockWindowAnimator(targetCompats) - : getClosingWindowAnimators(targetCompats)); - - // Normally, we run the launcher content animation when we are transitioning - // home, but if home is already visible, then we don't want to animate the - // contents of launcher unless we know that we are animating home as a result - // of the home button press with quickstep, which will result in launcher being - // started on touch down, prior to the animation home (and won't be in the - // targets list because it is already visible). In that case, we force - // invisibility on touch down, and only reset it after the animation to home - // is initialized. - if (launcherIsATargetWithMode(targetCompats, MODE_OPENING) - || mLauncher.isForceInvisible()) { - // Only register the content animation for cancellation when state changes - mLauncher.getStateManager().setCurrentAnimation(anim); - if (fromUnlock) { - Pair contentAnimator = - getLauncherContentAnimator(false /* isAppOpening */, - new float[] {mContentTransY, 0}); - contentAnimator.first.setStartDelay(0); - anim.play(contentAnimator.first); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - contentAnimator.second.run(); - } - }); - } else { - createLauncherResumeAnimation(anim); - } - } - } - - mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL); - result.setAnimation(anim); - } - }; + RemoteAnimationRunnerCompat getWallpaperOpenRunner(boolean fromUnlock) { + return new WallpaperOpenLauncherAnimationRunner(mHandler, false /* startAtFrontOfQueue */, + fromUnlock); } /** @@ -773,4 +712,79 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans return mLauncher.checkSelfPermission(CONTROL_REMOTE_APP_TRANSITION_PERMISSION) == PackageManager.PERMISSION_GRANTED; } + + /** + * Remote animation runner for animation from the app to Launcher, including recents. + */ + class WallpaperOpenLauncherAnimationRunner extends LauncherAnimationRunner { + private final boolean mFromUnlock; + + public WallpaperOpenLauncherAnimationRunner(Handler handler, boolean startAtFrontOfQueue, + boolean fromUnlock) { + super(handler, startAtFrontOfQueue); + mFromUnlock = fromUnlock; + } + + @Override + public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats, + LauncherAnimationRunner.AnimationResult result) { + if (!mLauncher.hasBeenResumed()) { + // If launcher is not resumed, wait until new async-frame after resume + mLauncher.setOnResumeCallback(() -> + postAsyncCallback(mHandler, () -> + onCreateAnimation(targetCompats, result))); + return; + } + + if (mLauncher.hasSomeInvisibleFlag(PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION)) { + mLauncher.addForceInvisibleFlag(INVISIBLE_BY_PENDING_FLAGS); + mLauncher.getStateManager().moveToRestState(); + } + + AnimatorSet anim = null; + RemoteAnimationProvider provider = mRemoteAnimationProvider; + if (provider != null) { + anim = provider.createWindowAnimation(targetCompats); + } + + if (anim == null) { + anim = new AnimatorSet(); + anim.play(mFromUnlock + ? getUnlockWindowAnimator(targetCompats) + : getClosingWindowAnimators(targetCompats)); + + // Normally, we run the launcher content animation when we are transitioning + // home, but if home is already visible, then we don't want to animate the + // contents of launcher unless we know that we are animating home as a result + // of the home button press with quickstep, which will result in launcher being + // started on touch down, prior to the animation home (and won't be in the + // targets list because it is already visible). In that case, we force + // invisibility on touch down, and only reset it after the animation to home + // is initialized. + if (launcherIsATargetWithMode(targetCompats, MODE_OPENING) + || mLauncher.isForceInvisible()) { + // Only register the content animation for cancellation when state changes + mLauncher.getStateManager().setCurrentAnimation(anim); + if (mFromUnlock) { + Pair contentAnimator = + getLauncherContentAnimator(false /* isAppOpening */, + new float[] {mContentTransY, 0}); + contentAnimator.first.setStartDelay(0); + anim.play(contentAnimator.first); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + contentAnimator.second.run(); + } + }); + } else { + createLauncherResumeAnimation(anim); + } + } + } + + mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL); + result.setAnimation(anim); + } + } } From f95e84a3852e1c85b2326b03431fd4b2ae20ed74 Mon Sep 17 00:00:00 2001 From: Kevin Date: Tue, 7 May 2019 16:56:12 -0700 Subject: [PATCH 19/72] Polish app => recents animation (1/N) As part of building out a better remote app to overview animation, we first separate out the logic so that we only do the default layout animation when coming from a Launcher state (i.e. from home or all apps) and only then, so coming from an app no longer leads to the tasks animating. We do this with a simple flag that indicates that recents will be using a remote animation. Bug: 132112131 Test: Go to app, go to recents. No layout animation. Test: Go to home, go to recents. Layout animation works as normal Change-Id: I30988d944571fd5317d0c9d13e2a99b167c1291b --- .../FallbackActivityControllerHelper.java | 1 + .../LauncherActivityControllerHelper.java | 2 ++ .../android/quickstep/views/IconRecentsView.java | 16 +++++++++++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java index bba08a40af..d39a66c707 100644 --- a/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java +++ b/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java @@ -45,6 +45,7 @@ public final class FallbackActivityControllerHelper extends @Override public AnimationFactory prepareRecentsUI(RecentsActivity activity, boolean activityVisible, boolean animateActivity, Consumer callback) { + // TODO: Logic for setting remote animation if (activityVisible) { return (transitionLength) -> { }; } diff --git a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java index 40db7ddbce..d5950077de 100644 --- a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java +++ b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java @@ -40,6 +40,7 @@ public final class LauncherActivityControllerHelper extends GoActivityControlHel boolean activityVisible, boolean animateActivity, Consumer callback) { LauncherState fromState = activity.getStateManager().getState(); + activity.getOverviewPanel().setUsingRemoteAnimation(true); //TODO: Implement this based off where the recents view needs to be for app => recents anim. return new AnimationFactory() { @Override @@ -87,6 +88,7 @@ public final class LauncherActivityControllerHelper extends GoActivityControlHel if (launcher == null) { return false; } + launcher.getOverviewPanel().setUsingRemoteAnimation(false); launcher.getUserEventDispatcher().logActionCommand( LauncherLogProto.Action.Command.RECENTS_BUTTON, getContainerType(), diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index 7225e572ba..c69e534581 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -125,6 +125,7 @@ public final class IconRecentsView extends FrameLayout implements Insettable { private View mEmptyView; private View mContentView; private boolean mTransitionedFromApp; + private boolean mUsingRemoteAnimation; private AnimatorSet mLayoutAnimation; private final ArraySet mLayingOutViews = new ArraySet<>(); private Rect mInsets; @@ -276,7 +277,9 @@ public final class IconRecentsView extends FrameLayout implements Insettable { // not be scrollable. mTaskLayoutManager.scrollToPositionWithOffset(TASKS_START_POSITION, 0 /* offset */); } - scheduleFadeInLayoutAnimation(); + if (!mUsingRemoteAnimation) { + scheduleFadeInLayoutAnimation(); + } // Load any task changes if (!mTaskLoader.needsToLoad()) { return; @@ -314,6 +317,17 @@ public final class IconRecentsView extends FrameLayout implements Insettable { mTransitionedFromApp = transitionedFromApp; } + /** + * Set whether we're using a custom remote animation. If so, we will not do the default layout + * animation when entering recents and instead wait for the remote app surface to be ready to + * use. + * + * @param usingRemoteAnimation true if doing a remote animation, false o/w + */ + public void setUsingRemoteAnimation(boolean usingRemoteAnimation) { + mUsingRemoteAnimation = usingRemoteAnimation; + } + /** * Handles input from the overview button. Launch the most recent task unless we just came from * the app. In that case, we launch the next most recent. From 080f818642ddb9b815b8472a8a54db08a36ac6bf Mon Sep 17 00:00:00 2001 From: Vadim Tryshev Date: Thu, 30 Aug 2018 16:01:47 -0700 Subject: [PATCH 20/72] Reducing flakiness of swipe gestures Swipe to home now injects a fixed number of points even if the test thread wakes up irregularly, and sends model (not actual) time in events. Bug: 132173901 Bug: 132107664 Change-Id: I0a19bbc2a0c3312f353ad49ebe496eef1f172276 --- .../android/launcher3/tapl/Background.java | 3 +- .../tapl/LauncherInstrumentation.java | 38 +++++++++++++++---- .../com/android/launcher3/tapl/Workspace.java | 3 +- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java index 3b2a7b8d13..60d2850b4f 100644 --- a/tests/tapl/com/android/launcher3/tapl/Background.java +++ b/tests/tapl/com/android/launcher3/tapl/Background.java @@ -78,7 +78,8 @@ public class Background extends LauncherInstrumentation.VisibleContainer { final long downTime = SystemClock.uptimeMillis(); mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start); - mLauncher.movePointer(downTime, ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION, start, end); + mLauncher.movePointer( + downTime, downTime, ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION, start, end); LauncherInstrumentation.sleep(ZERO_BUTTON_SWIPE_UP_HOLD_DURATION); mLauncher.sendPointer( downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, end); diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 87ef0448b8..fd2eabbb0d 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -70,6 +70,7 @@ public final class LauncherInstrumentation { private static final String TAG = "Tapl"; private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 20; + private static final int GESTURE_STEP_MS = 16; // Types for launcher containers that the user is interacting with. "Background" is a // pseudo-container corresponding to inactive launcher covered by another app. @@ -359,7 +360,7 @@ public final class LauncherInstrumentation { ? NORMAL_STATE_ORDINAL : BACKGROUND_APP_STATE_ORDINAL; final Point displaySize = getRealDisplaySize(); - swipe( + swipeViaMovePointer( displaySize.x / 2, displaySize.y - 1, displaySize.x / 2, 0, finalState, ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME); @@ -543,8 +544,28 @@ public final class LauncherInstrumentation { } void swipe(int startX, int startY, int endX, int endY, int expectedState, int steps) { + changeStateViaGesture(startX, startY, endX, endY, expectedState, + () -> mDevice.swipe(startX, startY, endX, endY, steps)); + } + + void swipeViaMovePointer( + 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); + }); + } + + private void changeStateViaGesture(int startX, int startY, int endX, int endY, + int expectedState, Runnable gesture) { final Bundle parcel = (Bundle) executeAndWaitForEvent( - () -> mDevice.swipe(startX, startY, endX, endY, steps), + gesture, event -> TestProtocol.SWITCHED_TO_STATE_MESSAGE.equals(event.getClassName()), "Swipe failed to receive an event for the swipe end: " + startX + ", " + startY + ", " + endX + ", " + endY); @@ -589,21 +610,22 @@ public final class LauncherInstrumentation { event.recycle(); } - void movePointer(long downTime, long duration, Point from, Point to) { + long movePointer(long downTime, long startTime, long duration, Point from, Point to) { final Point point = new Point(); - final long startTime = SystemClock.uptimeMillis(); - for (; ; ) { - sleep(16); + long steps = duration / GESTURE_STEP_MS; + long currentTime = startTime; + for (long i = 0; i < steps; ++i) { + sleep(GESTURE_STEP_MS); - final long currentTime = SystemClock.uptimeMillis(); + currentTime += GESTURE_STEP_MS; final float progress = (currentTime - startTime) / (float) duration; - if (progress > 1) return; point.x = from.x + (int) (progress * (to.x - from.x)); point.y = from.y + (int) (progress * (to.y - from.y)); sendPointer(downTime, currentTime, MotionEvent.ACTION_MOVE, point); } + return currentTime; } public static boolean isGesturalMode(Context context) { diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index 9a47aef309..e8a0b54686 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -150,7 +150,8 @@ public final class Workspace extends Home { LauncherInstrumentation.log("dragIconToWorkspace: sent down"); launcher.waitForLauncherObject(longPressIndicator); LauncherInstrumentation.log("dragIconToWorkspace: indicator"); - launcher.movePointer(downTime, DRAG_DURACTION, launchableCenter, dest); + launcher.movePointer( + downTime, SystemClock.uptimeMillis(), DRAG_DURACTION, launchableCenter, dest); LauncherInstrumentation.log("dragIconToWorkspace: moved pointer"); launcher.sendPointer( downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest); From 1b4b960ff113794232e3a779ab01f4450f8e3ed9 Mon Sep 17 00:00:00 2001 From: Kevin Date: Tue, 7 May 2019 17:40:32 -0700 Subject: [PATCH 21/72] Polish app => recents animation (2/N) This CL moves the core "app to overview" animation logic into the main view as the newer animation would need to animate over several of the main view's children and so the animation logic needs knowledge of those children. Bug: 132112131 Test: Build and test, functions as before Change-Id: I9cbde55a582bee62e0a97e38c5fdf1d5841502db --- .../AppToOverviewAnimationProvider.java | 107 +----------------- .../quickstep/views/IconRecentsView.java | 103 +++++++++++++++++ 2 files changed, 106 insertions(+), 104 deletions(-) diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java index c228bb94fe..2dc25546d6 100644 --- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java +++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java @@ -15,30 +15,21 @@ */ package com.android.quickstep; -import static com.android.launcher3.anim.Interpolators.ACCEL_2; -import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; -import static com.android.quickstep.util.RemoteAnimationProvider.getLayer; +import static com.android.quickstep.views.IconRecentsView.REMOTE_APP_TO_OVERVIEW_DURATION; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING; import android.animation.AnimatorSet; import android.animation.ValueAnimator; -import android.graphics.Matrix; -import android.graphics.Rect; import android.util.Log; import android.view.View; -import androidx.annotation.NonNull; - import com.android.launcher3.BaseDraggingActivity; -import com.android.quickstep.util.MultiValueUpdateListener; import com.android.quickstep.util.RemoteAnimationProvider; import com.android.quickstep.util.RemoteAnimationTargetSet; import com.android.quickstep.views.IconRecentsView; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; -import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams; /** * Provider for the atomic remote window animation from the app to the overview. @@ -47,9 +38,6 @@ import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat. */ final class AppToOverviewAnimationProvider implements RemoteAnimationProvider { - - private static final long APP_TO_THUMBNAIL_FADE_DURATION = 50; - private static final long APP_SCALE_DOWN_DURATION = 400; private static final String TAG = "AppToOverviewAnimationProvider"; private final ActivityControlHelper mHelper; @@ -131,106 +119,17 @@ final class AppToOverviewAnimationProvider imple return anim; } - View thumbnailView = mRecentsView.getBottomThumbnailView(); - if (thumbnailView == null) { - // This can be null if there were previously 0 tasks and the recycler view has not had - // enough time to take in the data change, bind a new view, and lay out the new view. - // TODO: Have a fallback to animate to - if (Log.isLoggable(TAG, Log.WARN)) { - Log.w(TAG, "No thumbnail view for running task. Using stub animation."); - } - anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration())); - return anim; - } - - playAppScaleDownAnim(anim, closingAppTarget, recentsTarget, thumbnailView); + mRecentsView.playAppScaleDownAnim(anim, closingAppTarget, recentsTarget); return anim; } - /** - * Animate a closing app to scale down to the location of the thumbnail view in recents. - * - * @param anim animator set - * @param appTarget the app surface thats closing - * @param recentsTarget the surface containing recents - * @param thumbnailView the thumbnail view to animate to - */ - private void playAppScaleDownAnim(@NonNull AnimatorSet anim, - @NonNull RemoteAnimationTargetCompat appTarget, - @NonNull RemoteAnimationTargetCompat recentsTarget, @NonNull View thumbnailView) { - - // Identify where the entering remote app should animate to. - Rect endRect = new Rect(); - thumbnailView.getGlobalVisibleRect(endRect); - - Rect appBounds = appTarget.sourceContainerBounds; - - ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 1); - valueAnimator.setDuration(APP_SCALE_DOWN_DURATION); - - SyncRtSurfaceTransactionApplierCompat surfaceApplier = - new SyncRtSurfaceTransactionApplierCompat(thumbnailView); - - // Keep recents visible throughout the animation. - SurfaceParams[] params = new SurfaceParams[2]; - // Closing app should stay on top. - int boostedMode = MODE_CLOSING; - params[0] = new SurfaceParams(recentsTarget.leash, 1f, null /* matrix */, - null /* windowCrop */, getLayer(recentsTarget, boostedMode), 0 /* cornerRadius */); - - valueAnimator.addUpdateListener(new MultiValueUpdateListener() { - private final FloatProp mScaleX; - private final FloatProp mScaleY; - private final FloatProp mTranslationX; - private final FloatProp mTranslationY; - private final FloatProp mAlpha; - - { - // Scale down and move to view location. - float endScaleX = ((float) endRect.width()) / appBounds.width(); - mScaleX = new FloatProp(1f, endScaleX, 0, APP_SCALE_DOWN_DURATION, - ACCEL_DEACCEL); - float endScaleY = ((float) endRect.height()) / appBounds.height(); - mScaleY = new FloatProp(1f, endScaleY, 0, APP_SCALE_DOWN_DURATION, - ACCEL_DEACCEL); - float endTranslationX = endRect.left - - (appBounds.width() - thumbnailView.getWidth()) / 2.0f; - mTranslationX = new FloatProp(0, endTranslationX, 0, APP_SCALE_DOWN_DURATION, - ACCEL_DEACCEL); - float endTranslationY = endRect.top - - (appBounds.height() - thumbnailView.getHeight()) / 2.0f; - mTranslationY = new FloatProp(0, endTranslationY, 0, APP_SCALE_DOWN_DURATION, - ACCEL_DEACCEL); - - // Fade out quietly near the end to be replaced by the real view. - mAlpha = new FloatProp(1.0f, 0, - APP_SCALE_DOWN_DURATION - APP_TO_THUMBNAIL_FADE_DURATION, - APP_TO_THUMBNAIL_FADE_DURATION, ACCEL_2); - } - - @Override - public void onUpdate(float percent) { - Matrix m = new Matrix(); - m.setScale(mScaleX.value, mScaleY.value, - appBounds.width() / 2.0f, appBounds.height() / 2.0f); - m.postTranslate(mTranslationX.value, mTranslationY.value); - - params[1] = new SurfaceParams(appTarget.leash, mAlpha.value, m, - null /* windowCrop */, getLayer(appTarget, boostedMode), - 0 /* cornerRadius */); - surfaceApplier.scheduleApply(params); - } - }); - anim.play(valueAnimator); - } - /** * Get duration of animation from app to overview. * * @return duration of animation */ long getRecentsLaunchDuration() { - return APP_SCALE_DOWN_DURATION; + return REMOTE_APP_TO_OVERVIEW_DURATION; } } diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index c69e534581..89d248cd3a 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -19,10 +19,14 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static androidx.recyclerview.widget.LinearLayoutManager.VERTICAL; +import static com.android.launcher3.anim.Interpolators.ACCEL_2; +import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; import static com.android.quickstep.TaskAdapter.CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT; import static com.android.quickstep.TaskAdapter.ITEM_TYPE_CLEAR_ALL; import static com.android.quickstep.TaskAdapter.ITEM_TYPE_TASK; import static com.android.quickstep.TaskAdapter.TASKS_START_POSITION; +import static com.android.quickstep.util.RemoteAnimationProvider.getLayer; +import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -32,6 +36,7 @@ import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.Resources; +import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.ArraySet; @@ -64,7 +69,11 @@ import com.android.quickstep.TaskAdapter; import com.android.quickstep.TaskHolder; import com.android.quickstep.TaskListLoader; import com.android.quickstep.TaskSwipeCallback; +import com.android.quickstep.util.MultiValueUpdateListener; import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.system.RemoteAnimationTargetCompat; +import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; +import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams; import java.util.ArrayList; import java.util.List; @@ -102,6 +111,11 @@ public final class IconRecentsView extends FrameLayout implements Insettable { private static final float ITEM_ANIMATE_OUT_TRANSLATION_X_RATIO = .25f; private static final long CLEAR_ALL_FADE_DELAY = 120; + private static final long APP_TO_THUMBNAIL_FADE_DURATION = 50; + private static final long APP_SCALE_DOWN_DURATION = 400; + + public static final long REMOTE_APP_TO_OVERVIEW_DURATION = APP_SCALE_DOWN_DURATION; + /** * A ratio representing the view's relative placement within its padded space. For example, 0 * is top aligned and 0.5 is centered vertically. @@ -565,6 +579,95 @@ public final class IconRecentsView extends FrameLayout implements Insettable { mLayoutAnimation.start(); } + /** + * Animate a closing app to scale down to the location of the thumbnail view in recents. + * + * @param anim animator set + * @param appTarget the app surface thats closing + * @param recentsTarget the surface containing recents + */ + public void playAppScaleDownAnim(@NonNull AnimatorSet anim, + @NonNull RemoteAnimationTargetCompat appTarget, + @NonNull RemoteAnimationTargetCompat recentsTarget) { + + View thumbnailView = getBottomThumbnailView(); + + if (thumbnailView == null) { + // This can be null if there were previously 0 tasks and the recycler view has not had + // enough time to take in the data change, bind a new view, and lay out the new view. + // TODO: Have a fallback to animate to + anim.play(ValueAnimator.ofInt(0, 1).setDuration(REMOTE_APP_TO_OVERVIEW_DURATION)); + } + + // Identify where the entering remote app should animate to. + Rect endRect = new Rect(); + thumbnailView.getGlobalVisibleRect(endRect); + + Rect appBounds = appTarget.sourceContainerBounds; + + ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 1); + valueAnimator.setDuration(APP_SCALE_DOWN_DURATION); + + SyncRtSurfaceTransactionApplierCompat surfaceApplier = + new SyncRtSurfaceTransactionApplierCompat(thumbnailView); + + // Keep recents visible throughout the animation. + SurfaceParams[] params = new SurfaceParams[2]; + // Closing app should stay on top. + int boostedMode = MODE_CLOSING; + params[0] = new SurfaceParams(recentsTarget.leash, 1f, null /* matrix */, + null /* windowCrop */, getLayer(recentsTarget, boostedMode), 0 /* cornerRadius */); + + valueAnimator.addUpdateListener(new MultiValueUpdateListener() { + private final FloatProp mScaleX; + private final FloatProp mScaleY; + private final FloatProp mTranslationX; + private final FloatProp mTranslationY; + private final FloatProp mAlpha; + + { + // Scale down and move to view location. + float endScaleX = ((float) endRect.width()) / appBounds.width(); + mScaleX = new FloatProp(1f, endScaleX, 0, APP_SCALE_DOWN_DURATION, + ACCEL_DEACCEL); + float endScaleY = ((float) endRect.height()) / appBounds.height(); + mScaleY = new FloatProp(1f, endScaleY, 0, APP_SCALE_DOWN_DURATION, + ACCEL_DEACCEL); + float endTranslationX = endRect.left - + (appBounds.width() - thumbnailView.getWidth()) / 2.0f; + mTranslationX = new FloatProp(0, endTranslationX, 0, APP_SCALE_DOWN_DURATION, + ACCEL_DEACCEL); + float endTranslationY = endRect.top - + (appBounds.height() - thumbnailView.getHeight()) / 2.0f; + mTranslationY = new FloatProp(0, endTranslationY, 0, APP_SCALE_DOWN_DURATION, + ACCEL_DEACCEL); + + // Fade out quietly near the end to be replaced by the real view. + mAlpha = new FloatProp(1.0f, 0, + APP_SCALE_DOWN_DURATION - APP_TO_THUMBNAIL_FADE_DURATION, + APP_TO_THUMBNAIL_FADE_DURATION, ACCEL_2); + } + + @Override + public void onUpdate(float percent) { + Matrix m = new Matrix(); + m.setScale(mScaleX.value, mScaleY.value, + appBounds.width() / 2.0f, appBounds.height() / 2.0f); + m.postTranslate(mTranslationX.value, mTranslationY.value); + + params[1] = new SurfaceParams(appTarget.leash, mAlpha.value, m, + null /* windowCrop */, getLayer(appTarget, boostedMode), + 0 /* cornerRadius */); + surfaceApplier.scheduleApply(params); + } + }); + anim.play(valueAnimator); + } + + public long getAppToOverviewAnimationDuration() { + return APP_SCALE_DOWN_DURATION; + } + @Override public void setInsets(Rect insets) { mInsets = insets; From 860b9208c1704b8bb1dc367ea1c2eb7b8e0d19fe Mon Sep 17 00:00:00 2001 From: Jeff Chang Date: Tue, 18 Dec 2018 19:49:52 +0800 Subject: [PATCH 22/72] Hide the split-screen request for secondary display task. Currently, we cannot support the split-screen mode on the secondary display. To prevent confused,disallowing split-screen request on non-primary display tasks first. Bug: 118266305 Test: 1. Use the ActivityViewTest to launch an application on virtual display. 2. Check if the split-screen button exist on recent app. Change-Id: Ifa6d70a1920aa4f33d260e37a3737e102949114e --- .../android/quickstep/TaskSystemShortcut.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java index e3dcadcd54..e20ef52591 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java @@ -16,6 +16,7 @@ package com.android.quickstep; +import static android.view.Display.DEFAULT_DISPLAY; import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP; import android.app.Activity; @@ -116,21 +117,22 @@ public class TaskSystemShortcut extends SystemShortcut mHandler = new Handler(Looper.getMainLooper()); } - protected abstract boolean isAvailable(BaseDraggingActivity activity); + protected abstract boolean isAvailable(BaseDraggingActivity activity, int displayId); protected abstract ActivityOptions makeLaunchOptions(Activity activity); protected abstract boolean onActivityStarted(BaseDraggingActivity activity); @Override public View.OnClickListener getOnClickListener( BaseDraggingActivity activity, TaskView taskView) { - if (!isAvailable(activity)) { - return null; - } final Task task = taskView.getTask(); final int taskId = task.key.id; + final int displayId = task.key.displayId; if (!task.isDockable) { return null; } + if (!isAvailable(activity, displayId)) { + return null; + } final RecentsView recentsView = activity.getOverviewPanel(); final TaskThumbnailView thumbnailView = taskView.getThumbnail(); @@ -218,9 +220,13 @@ public class TaskSystemShortcut extends SystemShortcut } @Override - protected boolean isAvailable(BaseDraggingActivity activity) { - // Don't show menu-item if already in multi-window - return !activity.getDeviceProfile().isMultiWindowMode; + protected boolean isAvailable(BaseDraggingActivity activity, int displayId) { + // Don't show menu-item if already in multi-window and the task is from + // the secondary display. + // TODO(b/118266305): Temporarily disable splitscreen for secondary display while new + // implementation is enabled + return !activity.getDeviceProfile().isMultiWindowMode + && displayId == DEFAULT_DISPLAY; } @Override @@ -256,7 +262,7 @@ public class TaskSystemShortcut extends SystemShortcut } @Override - protected boolean isAvailable(BaseDraggingActivity activity) { + protected boolean isAvailable(BaseDraggingActivity activity, int displayId) { return ActivityManagerWrapper.getInstance().supportsFreeformMultiWindow(activity); } From df94de93433966261614ed08b4309bd09a3726ff Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Wed, 8 May 2019 10:05:57 -0700 Subject: [PATCH 23/72] Fix SDK check to use Utilities.ATLEAST_Q. Bug: 131315856 Change-Id: Ie1d00d85dd01da7e57f28e16f51a49962d734927 --- src/com/android/launcher3/provider/RestoreDbTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java index e6e20e1630..053c493d76 100644 --- a/src/com/android/launcher3/provider/RestoreDbTask.java +++ b/src/com/android/launcher3/provider/RestoreDbTask.java @@ -201,7 +201,7 @@ public class RestoreDbTask { */ private UserHandle getUserForAncestralSerialNumber(BackupManager backupManager, long ancestralSerialNumber) { - if (Build.VERSION.SDK_INT < 29) { + if (!Utilities.ATLEAST_Q) { return null; } return backupManager.getUserForAncestralSerialNumber(ancestralSerialNumber); From 4743da3864748101c525564506c9c3b27047b008 Mon Sep 17 00:00:00 2001 From: Miranda Kephart Date: Wed, 8 May 2019 14:41:24 -0400 Subject: [PATCH 24/72] Allow fling invocation for assistant Bug: 132269601 Test: manual Change-Id: Ieb8fb6ade28d947ab8ca9ef901937dc7f3ca7a1d --- .../quickstep/AssistantTouchConsumer.java | 95 ++++++++++++------- 1 file changed, 61 insertions(+), 34 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java index d8c5e53bd0..335e8b15a2 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java @@ -21,9 +21,9 @@ import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_POINTER_UP; import static android.view.MotionEvent.ACTION_UP; - import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction.UPLEFT; import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction.UPRIGHT; +import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.FLING; import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.SWIPE; import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.SWIPE_NOOP; import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.NAVBAR; @@ -38,11 +38,11 @@ import android.os.SystemClock; import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; - import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.R; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.logging.UserEventDispatcher; +import com.android.launcher3.touch.SwipeDetector; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.InputMonitorCompat; import com.android.systemui.shared.system.QuickStepContract; @@ -50,12 +50,15 @@ import com.android.systemui.shared.system.QuickStepContract; /** * Touch consumer for handling events to launch assistant from launcher */ -public class AssistantTouchConsumer extends DelegateInputConsumer { +public class AssistantTouchConsumer extends DelegateInputConsumer + implements SwipeDetector.Listener { + private static final String TAG = "AssistantTouchConsumer"; private static final long RETRACT_ANIMATION_DURATION_MS = 300; private static final String INVOCATION_TYPE_KEY = "invocation_type"; private static final int INVOCATION_TYPE_GESTURE = 1; + private static final int INVOCATION_TYPE_FLING = 6; private final PointF mDownPos = new PointF(); private final PointF mLastPos = new PointF(); @@ -77,6 +80,7 @@ public class AssistantTouchConsumer extends DelegateInputConsumer { private final float mSlop; private final ISystemUiProxy mSysUiProxy; private final Context mContext; + private final SwipeDetector mSwipeDetector; public AssistantTouchConsumer(Context context, ISystemUiProxy systemUiProxy, ActivityControlHelper activityControlHelper, InputConsumer delegate, @@ -90,6 +94,8 @@ public class AssistantTouchConsumer extends DelegateInputConsumer { mAngleThreshold = res.getInteger(R.integer.assistant_gesture_corner_deg_threshold); mSlop = QuickStepContract.getQuickStepDragSlopPx(); mActivityControlHelper = activityControlHelper; + mSwipeDetector = new SwipeDetector(mContext, this, SwipeDetector.VERTICAL); + mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false); } @Override @@ -100,6 +106,7 @@ public class AssistantTouchConsumer extends DelegateInputConsumer { @Override public void onMotionEvent(MotionEvent ev) { // TODO add logging + mSwipeDetector.onTouchEvent(ev); switch (ev.getActionMasked()) { case ACTION_DOWN: { @@ -146,7 +153,7 @@ public class AssistantTouchConsumer extends DelegateInputConsumer { // Determine if angle is larger than threshold for assistant detection float angle = (float) Math.toDegrees( - Math.atan2(mDownPos.y - mLastPos.y, mDownPos.x - mLastPos.x)); + Math.atan2(mDownPos.y - mLastPos.y, mDownPos.x - mLastPos.x)); mDirection = angle > 90 ? UPLEFT : UPRIGHT; angle = angle > 90 ? 180 - angle : angle; @@ -159,7 +166,7 @@ public class AssistantTouchConsumer extends DelegateInputConsumer { } else { // Movement mDistance = (float) Math.hypot(mLastPos.x - mStartDragPos.x, - mLastPos.y - mStartDragPos.y); + mLastPos.y - mStartDragPos.y); if (mDistance >= 0) { final long diff = SystemClock.uptimeMillis() - mDragTime; mTimeFraction = Math.min(diff * 1f / mTimeThreshold, 1); @@ -172,18 +179,18 @@ public class AssistantTouchConsumer extends DelegateInputConsumer { case ACTION_UP: if (mState != STATE_DELEGATE_ACTIVE && !mLaunchedAssistant) { ValueAnimator animator = ValueAnimator.ofFloat(mLastProgress, 0) - .setDuration(RETRACT_ANIMATION_DURATION_MS); + .setDuration(RETRACT_ANIMATION_DURATION_MS); UserEventDispatcher.newInstance(mContext).logActionOnContainer( - SWIPE_NOOP, mDirection, NAVBAR); + SWIPE_NOOP, mDirection, NAVBAR); animator.addUpdateListener(valueAnimator -> { - float progress = (float) valueAnimator.getAnimatedValue(); - try { + float progress = (float) valueAnimator.getAnimatedValue(); + try { - mSysUiProxy.onAssistantProgress(progress); - } catch (RemoteException e) { - Log.w(TAG, "Failed to send SysUI start/send assistant progress: " - + progress, e); - } + mSysUiProxy.onAssistantProgress(progress); + } catch (RemoteException e) { + Log.w(TAG, "Failed to send SysUI start/send assistant progress: " + + progress, e); + } }); animator.setInterpolator(Interpolators.DEACCEL_2); animator.start(); @@ -200,30 +207,32 @@ public class AssistantTouchConsumer extends DelegateInputConsumer { private void updateAssistantProgress() { if (!mLaunchedAssistant) { - float progress = Math.min(mDistance * 1f / mDistThreshold, 1) * mTimeFraction; - mLastProgress = progress; - try { - mSysUiProxy.onAssistantProgress(progress); - if (mDistance >= mDistThreshold && mTimeFraction >= 1) { - UserEventDispatcher.newInstance(mContext).logActionOnContainer( - SWIPE, mDirection, NAVBAR); - Bundle args = new Bundle(); - args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE); + mLastProgress = Math.min(mDistance * 1f / mDistThreshold, 1) * mTimeFraction; + updateAssistant(SWIPE); + } + } - BaseDraggingActivity launcherActivity = - mActivityControlHelper.getCreatedActivity(); - if (launcherActivity != null) { - launcherActivity.getRootView().performHapticFeedback( - 13, // HapticFeedbackConstants.GESTURE_END - HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); - } + private void updateAssistant(int gestureType) { + try { + mSysUiProxy.onAssistantProgress(mLastProgress); + if (gestureType == FLING || (mDistance >= mDistThreshold && mTimeFraction >= 1)) { + UserEventDispatcher.newInstance(mContext) + .logActionOnContainer(gestureType, mDirection, NAVBAR); + Bundle args = new Bundle(); + args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE); - mSysUiProxy.startAssistant(args); - mLaunchedAssistant = true; + BaseDraggingActivity launcherActivity = mActivityControlHelper.getCreatedActivity(); + if (launcherActivity != null) { + launcherActivity.getRootView().performHapticFeedback( + 13, // HapticFeedbackConstants.GESTURE_END + HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); } - } catch (RemoteException e) { - Log.w(TAG, "Failed to send SysUI start/send assistant progress: " + progress, e); + + mSysUiProxy.startAssistant(args); + mLaunchedAssistant = true; } + } catch (RemoteException e) { + Log.w(TAG, "Failed to send SysUI start/send assistant progress: " + mLastProgress, e); } } @@ -234,4 +243,22 @@ public class AssistantTouchConsumer extends DelegateInputConsumer { final int size = res.getDimensionPixelSize(R.dimen.gestures_assistant_size); return (ev.getX() > width - size || ev.getX() < size) && ev.getY() > height - size; } + + @Override + public void onDragStart(boolean start) { + // do nothing + } + + @Override + public boolean onDrag(float displacement) { + return false; + } + + @Override + public void onDragEnd(float velocity, boolean fling) { + if (fling) { + mLastProgress = 1; + updateAssistant(FLING); + } + } } From e590014834f3f4704bf7801555ff6c4bb19606b2 Mon Sep 17 00:00:00 2001 From: Kevin Date: Fri, 3 May 2019 13:14:40 -0700 Subject: [PATCH 25/72] Polish app => recents animation (3/N) This CL takes the current scale down animation and updates its interpolators and durations to match the UX spec. In addition, we move the core logic to a helper method as the whole remote animation will have other animations as well in the future. Bug: 132112131 Test: Manual Change-Id: I68b1d623b1b2c980dc34e41bbb5460cc021b5887 --- .../AppToOverviewAnimationProvider.java | 3 +- .../quickstep/views/IconRecentsView.java | 78 +++++++++++-------- 2 files changed, 46 insertions(+), 35 deletions(-) diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java index 2dc25546d6..62e40d1947 100644 --- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java +++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java @@ -23,7 +23,6 @@ import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MOD import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.util.Log; -import android.view.View; import com.android.launcher3.BaseDraggingActivity; import com.android.quickstep.util.RemoteAnimationProvider; @@ -119,7 +118,7 @@ final class AppToOverviewAnimationProvider imple return anim; } - mRecentsView.playAppScaleDownAnim(anim, closingAppTarget, recentsTarget); + mRecentsView.playRemoteAppToRecentsAnimation(anim, closingAppTarget, recentsTarget); return anim; } diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index 89d248cd3a..58c6709b40 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -20,7 +20,6 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static androidx.recyclerview.widget.LinearLayoutManager.VERTICAL; import static com.android.launcher3.anim.Interpolators.ACCEL_2; -import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; import static com.android.quickstep.TaskAdapter.CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT; import static com.android.quickstep.TaskAdapter.ITEM_TYPE_CLEAR_ALL; import static com.android.quickstep.TaskAdapter.ITEM_TYPE_TASK; @@ -45,6 +44,7 @@ import android.util.FloatProperty; import android.view.View; import android.view.ViewDebug; import android.view.ViewTreeObserver; +import android.view.animation.PathInterpolator; import android.widget.FrameLayout; import androidx.annotation.NonNull; @@ -111,10 +111,15 @@ public final class IconRecentsView extends FrameLayout implements Insettable { private static final float ITEM_ANIMATE_OUT_TRANSLATION_X_RATIO = .25f; private static final long CLEAR_ALL_FADE_DELAY = 120; - private static final long APP_TO_THUMBNAIL_FADE_DURATION = 50; - private static final long APP_SCALE_DOWN_DURATION = 400; + private static final long REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION = 300; - public static final long REMOTE_APP_TO_OVERVIEW_DURATION = APP_SCALE_DOWN_DURATION; + private static final PathInterpolator FAST_OUT_SLOW_IN_1 = + new PathInterpolator(.4f, 0f, 0f, 1f); + private static final PathInterpolator FAST_OUT_SLOW_IN_2 = + new PathInterpolator(.5f, 0f, 0f, 1f); + + public static final long REMOTE_APP_TO_OVERVIEW_DURATION = + REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION; /** * A ratio representing the view's relative placement within its padded space. For example, 0 @@ -397,7 +402,7 @@ public final class IconRecentsView extends FrameLayout implements Insettable { * * @return the thumbnail view if laid out */ - public @Nullable View getBottomThumbnailView() { + private @Nullable View getBottomThumbnailView() { ArrayList taskViews = getTaskViews(); if (taskViews.isEmpty()) { return null; @@ -580,18 +585,17 @@ public final class IconRecentsView extends FrameLayout implements Insettable { } /** - * Animate a closing app to scale down to the location of the thumbnail view in recents. + * Play remote animation from app to recents. This should scale the currently closing app down + * to the recents thumbnail. * * @param anim animator set * @param appTarget the app surface thats closing * @param recentsTarget the surface containing recents */ - public void playAppScaleDownAnim(@NonNull AnimatorSet anim, + public void playRemoteAppToRecentsAnimation(@NonNull AnimatorSet anim, @NonNull RemoteAnimationTargetCompat appTarget, @NonNull RemoteAnimationTargetCompat recentsTarget) { - View thumbnailView = getBottomThumbnailView(); - if (thumbnailView == null) { // This can be null if there were previously 0 tasks and the recycler view has not had // enough time to take in the data change, bind a new view, and lay out the new view. @@ -599,15 +603,28 @@ public final class IconRecentsView extends FrameLayout implements Insettable { anim.play(ValueAnimator.ofInt(0, 1).setDuration(REMOTE_APP_TO_OVERVIEW_DURATION)); } + // TODO: Play other animations in app => recents + playRemoteAppScaleDownAnim(anim, appTarget, recentsTarget, thumbnailView); + } + + /** + * Play the scale down animation for the remote app to recents animation where the app surface + * scales down to where the thumbnail is. + * + * @param anim animator set to play on + * @param appTarget closing app target + * @param recentsTarget opening recents target + * @param thumbnailView thumbnail view to animate to + */ + private void playRemoteAppScaleDownAnim(@NonNull AnimatorSet anim, + @NonNull RemoteAnimationTargetCompat appTarget, + @NonNull RemoteAnimationTargetCompat recentsTarget, + @NonNull View thumbnailView) { // Identify where the entering remote app should animate to. Rect endRect = new Rect(); thumbnailView.getGlobalVisibleRect(endRect); - Rect appBounds = appTarget.sourceContainerBounds; - ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 1); - valueAnimator.setDuration(APP_SCALE_DOWN_DURATION); - SyncRtSurfaceTransactionApplierCompat surfaceApplier = new SyncRtSurfaceTransactionApplierCompat(thumbnailView); @@ -618,7 +635,9 @@ public final class IconRecentsView extends FrameLayout implements Insettable { params[0] = new SurfaceParams(recentsTarget.leash, 1f, null /* matrix */, null /* windowCrop */, getLayer(recentsTarget, boostedMode), 0 /* cornerRadius */); - valueAnimator.addUpdateListener(new MultiValueUpdateListener() { + ValueAnimator remoteAppAnim = ValueAnimator.ofInt(0, 1); + remoteAppAnim.setDuration(REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION); + remoteAppAnim.addUpdateListener(new MultiValueUpdateListener() { private final FloatProp mScaleX; private final FloatProp mScaleY; private final FloatProp mTranslationX; @@ -628,30 +647,27 @@ public final class IconRecentsView extends FrameLayout implements Insettable { { // Scale down and move to view location. float endScaleX = ((float) endRect.width()) / appBounds.width(); - mScaleX = new FloatProp(1f, endScaleX, 0, APP_SCALE_DOWN_DURATION, - ACCEL_DEACCEL); + mScaleX = new FloatProp(1f, endScaleX, 0, REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION, + FAST_OUT_SLOW_IN_1); float endScaleY = ((float) endRect.height()) / appBounds.height(); - mScaleY = new FloatProp(1f, endScaleY, 0, APP_SCALE_DOWN_DURATION, - ACCEL_DEACCEL); + mScaleY = new FloatProp(1f, endScaleY, 0, REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION, + FAST_OUT_SLOW_IN_1); float endTranslationX = endRect.left - (appBounds.width() - thumbnailView.getWidth()) / 2.0f; - mTranslationX = new FloatProp(0, endTranslationX, 0, APP_SCALE_DOWN_DURATION, - ACCEL_DEACCEL); + mTranslationX = new FloatProp(0, endTranslationX, 0, + REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION, FAST_OUT_SLOW_IN_1); float endTranslationY = endRect.top - (appBounds.height() - thumbnailView.getHeight()) / 2.0f; - mTranslationY = new FloatProp(0, endTranslationY, 0, APP_SCALE_DOWN_DURATION, - ACCEL_DEACCEL); - - // Fade out quietly near the end to be replaced by the real view. - mAlpha = new FloatProp(1.0f, 0, - APP_SCALE_DOWN_DURATION - APP_TO_THUMBNAIL_FADE_DURATION, - APP_TO_THUMBNAIL_FADE_DURATION, ACCEL_2); + mTranslationY = new FloatProp(0, endTranslationY, 0, + REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION, FAST_OUT_SLOW_IN_2); + mAlpha = new FloatProp(1.0f, 0, 0, REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION, + ACCEL_2); } @Override public void onUpdate(float percent) { Matrix m = new Matrix(); - m.setScale(mScaleX.value, mScaleY.value, + m.preScale(mScaleX.value, mScaleY.value, appBounds.width() / 2.0f, appBounds.height() / 2.0f); m.postTranslate(mTranslationX.value, mTranslationY.value); @@ -661,11 +677,7 @@ public final class IconRecentsView extends FrameLayout implements Insettable { surfaceApplier.scheduleApply(params); } }); - anim.play(valueAnimator); - } - - public long getAppToOverviewAnimationDuration() { - return APP_SCALE_DOWN_DURATION; + anim.play(remoteAppAnim); } @Override From 07e12ee573b852487120a818cf271503f84b441e Mon Sep 17 00:00:00 2001 From: Kevin Date: Fri, 3 May 2019 13:14:40 -0700 Subject: [PATCH 26/72] Polish app => recents animation (4/5) Add the vertical ease in component to the app => recents animation. In addition to animating over all task views, this should also animate over the closing app surface so that the surface movement matches the thumbnail as it moves to cover it. As a result, we take in the transformation matrix thats passed in to the app surface and apply the vertical motion to it. Bug: 132112131 Test: Manual Change-Id: I77c6716082af6ee722b96a2c362e16030cec8b8b --- .../quickstep/views/IconRecentsView.java | 84 ++++++++++++++----- 1 file changed, 64 insertions(+), 20 deletions(-) diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index 58c6709b40..4c91a59661 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -23,6 +23,7 @@ import static com.android.launcher3.anim.Interpolators.ACCEL_2; import static com.android.quickstep.TaskAdapter.CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT; import static com.android.quickstep.TaskAdapter.ITEM_TYPE_CLEAR_ALL; import static com.android.quickstep.TaskAdapter.ITEM_TYPE_TASK; +import static com.android.quickstep.TaskAdapter.MAX_TASKS_TO_DISPLAY; import static com.android.quickstep.TaskAdapter.TASKS_START_POSITION; import static com.android.quickstep.util.RemoteAnimationProvider.getLayer; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; @@ -112,6 +113,7 @@ public final class IconRecentsView extends FrameLayout implements Insettable { private static final long CLEAR_ALL_FADE_DELAY = 120; private static final long REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION = 300; + private static final long REMOTE_TO_RECENTS_VERTICAL_EASE_IN_DURATION = 400; private static final PathInterpolator FAST_OUT_SLOW_IN_1 = new PathInterpolator(.4f, 0f, 0f, 1f); @@ -119,7 +121,7 @@ public final class IconRecentsView extends FrameLayout implements Insettable { new PathInterpolator(.5f, 0f, 0f, 1f); public static final long REMOTE_APP_TO_OVERVIEW_DURATION = - REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION; + REMOTE_TO_RECENTS_VERTICAL_EASE_IN_DURATION; /** * A ratio representing the view's relative placement within its padded space. For example, 0 @@ -398,17 +400,19 @@ public final class IconRecentsView extends FrameLayout implements Insettable { } /** - * Get the bottom most thumbnail view to animate to. + * Get the bottom most task view to animate to. * - * @return the thumbnail view if laid out + * @return the task view */ - private @Nullable View getBottomThumbnailView() { - ArrayList taskViews = getTaskViews(); - if (taskViews.isEmpty()) { - return null; + private @Nullable TaskItemView getBottomTaskView() { + int childCount = mTaskRecyclerView.getChildCount(); + for (int i = 0; i < childCount; i++) { + View view = mTaskRecyclerView.getChildAt(i); + if (mTaskRecyclerView.getChildViewHolder(view).getItemViewType() == ITEM_TYPE_TASK) { + return (TaskItemView) view; + } } - TaskItemView view = taskViews.get(0); - return view.getThumbnailView(); + return null; } /** @@ -595,16 +599,55 @@ public final class IconRecentsView extends FrameLayout implements Insettable { public void playRemoteAppToRecentsAnimation(@NonNull AnimatorSet anim, @NonNull RemoteAnimationTargetCompat appTarget, @NonNull RemoteAnimationTargetCompat recentsTarget) { - View thumbnailView = getBottomThumbnailView(); - if (thumbnailView == null) { + TaskItemView bottomView = getBottomTaskView(); + if (bottomView == null) { // This can be null if there were previously 0 tasks and the recycler view has not had // enough time to take in the data change, bind a new view, and lay out the new view. // TODO: Have a fallback to animate to anim.play(ValueAnimator.ofInt(0, 1).setDuration(REMOTE_APP_TO_OVERVIEW_DURATION)); } + final Matrix appMatrix = new Matrix(); + playRemoteTransYAnim(anim, appMatrix); + playRemoteAppScaleDownAnim(anim, appMatrix, appTarget, recentsTarget, + bottomView.getThumbnailView()); + } - // TODO: Play other animations in app => recents - playRemoteAppScaleDownAnim(anim, appTarget, recentsTarget, thumbnailView); + /** + * Play translation Y animation for the remote app to recents animation. Animates over all task + * views as well as the closing app, easing them into their final vertical positions. + * + * @param anim animator set to play on + * @param appMatrix transformation matrix for the closing app surface + */ + private void playRemoteTransYAnim(@NonNull AnimatorSet anim, @NonNull Matrix appMatrix) { + final ArrayList views = getTaskViews(); + + // Start Y translation from about halfway through the tasks list to the bottom thumbnail. + float taskHeight = getResources().getDimension(R.dimen.task_item_height); + float totalTransY = -(MAX_TASKS_TO_DISPLAY / 2.0f - 1) * taskHeight; + for (int i = 0, size = views.size(); i < size; i++) { + views.get(i).setTranslationY(totalTransY); + } + + ValueAnimator transYAnim = ValueAnimator.ofFloat(totalTransY, 0); + transYAnim.setDuration(REMOTE_TO_RECENTS_VERTICAL_EASE_IN_DURATION); + transYAnim.setInterpolator(FAST_OUT_SLOW_IN_2); + transYAnim.addUpdateListener(valueAnimator -> { + float transY = (float) valueAnimator.getAnimatedValue(); + for (int i = 0, size = views.size(); i < size; i++) { + views.get(i).setTranslationY(transY); + } + appMatrix.postTranslate(0, transY - totalTransY); + }); + transYAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + for (int i = 0, size = views.size(); i < size; i++) { + views.get(i).setTranslationY(0); + } + } + }); + anim.play(transYAnim); } /** @@ -612,11 +655,12 @@ public final class IconRecentsView extends FrameLayout implements Insettable { * scales down to where the thumbnail is. * * @param anim animator set to play on + * @param appMatrix transformation matrix for the app surface * @param appTarget closing app target * @param recentsTarget opening recents target * @param thumbnailView thumbnail view to animate to */ - private void playRemoteAppScaleDownAnim(@NonNull AnimatorSet anim, + private void playRemoteAppScaleDownAnim(@NonNull AnimatorSet anim, @NonNull Matrix appMatrix, @NonNull RemoteAnimationTargetCompat appTarget, @NonNull RemoteAnimationTargetCompat recentsTarget, @NonNull View thumbnailView) { @@ -626,7 +670,7 @@ public final class IconRecentsView extends FrameLayout implements Insettable { Rect appBounds = appTarget.sourceContainerBounds; SyncRtSurfaceTransactionApplierCompat surfaceApplier = - new SyncRtSurfaceTransactionApplierCompat(thumbnailView); + new SyncRtSurfaceTransactionApplierCompat(this); // Keep recents visible throughout the animation. SurfaceParams[] params = new SurfaceParams[2]; @@ -636,7 +680,7 @@ public final class IconRecentsView extends FrameLayout implements Insettable { null /* windowCrop */, getLayer(recentsTarget, boostedMode), 0 /* cornerRadius */); ValueAnimator remoteAppAnim = ValueAnimator.ofInt(0, 1); - remoteAppAnim.setDuration(REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION); + remoteAppAnim.setDuration(REMOTE_TO_RECENTS_VERTICAL_EASE_IN_DURATION); remoteAppAnim.addUpdateListener(new MultiValueUpdateListener() { private final FloatProp mScaleX; private final FloatProp mScaleY; @@ -666,15 +710,15 @@ public final class IconRecentsView extends FrameLayout implements Insettable { @Override public void onUpdate(float percent) { - Matrix m = new Matrix(); - m.preScale(mScaleX.value, mScaleY.value, + appMatrix.preScale(mScaleX.value, mScaleY.value, appBounds.width() / 2.0f, appBounds.height() / 2.0f); - m.postTranslate(mTranslationX.value, mTranslationY.value); + appMatrix.postTranslate(mTranslationX.value, mTranslationY.value); - params[1] = new SurfaceParams(appTarget.leash, mAlpha.value, m, + params[1] = new SurfaceParams(appTarget.leash, mAlpha.value, appMatrix, null /* windowCrop */, getLayer(appTarget, boostedMode), 0 /* cornerRadius */); surfaceApplier.scheduleApply(params); + appMatrix.reset(); } }); anim.play(remoteAppAnim); From 41d3a2a1d7d249f42aef5dbb443b10f6337fd87c Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 8 May 2019 15:21:21 -0700 Subject: [PATCH 27/72] Fix bug skipping certain animations from ending endAnimations forces an end to all running animations. This also removes it from the list which changes the list while we're iterating over it. This fixes the issue. Bug: 132285006 Test: Manual test w/ build forcing endAnimations Change-Id: I0ee9917866dafe00ef3122dd9a43259d19fb9f79 --- .../src/com/android/quickstep/ContentFillItemAnimator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java b/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java index 9282345532..87ae6955e6 100644 --- a/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java +++ b/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java @@ -250,7 +250,7 @@ public final class ContentFillItemAnimator extends SimpleItemAnimator { } mPendingAnims.remove(i); } - for (int i = 0; i < mRunningAnims.size(); i++) { + for (int i = mRunningAnims.size() - 1; i >= 0; i--) { ObjectAnimator anim = mRunningAnims.get(i); anim.end(); } From b9300ef8494264143534674b73f4d1dd12e0a941 Mon Sep 17 00:00:00 2001 From: vadimt Date: Wed, 8 May 2019 15:59:30 -0700 Subject: [PATCH 28/72] Not starting calculator from TaplTestsLauncher3.testAppIconLaunchFromAllAppsFromHome Not sure why, but starting calculator sometimes fails. Let's see if this changes anything. Change-Id: I2934a388afe7e95bdaeb38151a2676b3297b6e94 --- tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index 581e886b6e..7578dfff2e 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -24,7 +24,6 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import android.content.Intent; import android.util.Log; import androidx.test.filters.LargeTest; @@ -241,9 +240,8 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { } public static void runIconLaunchFromAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) { - final AppIcon app = allApps.getAppIcon("Calculator"); - assertNotNull("AppIcon.launch returned null", app.launch( - test.resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR))); + final AppIcon app = allApps.getAppIcon("TestActivity7"); + assertNotNull("AppIcon.launch returned null", app.launch(getAppPackageName())); test.executeOnLauncher(launcher -> assertTrue( "Launcher activity is the top activity; expecting another activity to be the top " + "one", From fe634af0c4e0981d9e3d15bd87433601a742634b Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 2 May 2019 14:53:08 -0700 Subject: [PATCH 29/72] Apply insets correctly to fallback Recents Go Insets were not being correctly consumed by the root view in the fallback recents activity. This CL properly passes on the system insets to the recents view so that tasks flow under the system insets. Bug: 131853975 Test: Have separate default launcher, go to recents, go to landscape, tasks scroll under status bar Change-Id: I16f78da896522c5cf41951817747a24cd5cfe32c --- .../android/quickstep/RecentsActivity.java | 2 +- .../fallback/GoRecentsActivityRootView.java | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/go/quickstep/src/com/android/quickstep/RecentsActivity.java b/go/quickstep/src/com/android/quickstep/RecentsActivity.java index 9fb80679e3..078f3a5ff8 100644 --- a/go/quickstep/src/com/android/quickstep/RecentsActivity.java +++ b/go/quickstep/src/com/android/quickstep/RecentsActivity.java @@ -42,7 +42,7 @@ public final class RecentsActivity extends BaseRecentsActivity { @Override protected void reapplyUi() { - //TODO: Implement this depending on how insets will affect the view. + // No-op. Insets are automatically re-applied in the root view. } @Override diff --git a/go/quickstep/src/com/android/quickstep/fallback/GoRecentsActivityRootView.java b/go/quickstep/src/com/android/quickstep/fallback/GoRecentsActivityRootView.java index c0ebcb5a43..b550011f50 100644 --- a/go/quickstep/src/com/android/quickstep/fallback/GoRecentsActivityRootView.java +++ b/go/quickstep/src/com/android/quickstep/fallback/GoRecentsActivityRootView.java @@ -16,7 +16,10 @@ package com.android.quickstep.fallback; import android.content.Context; +import android.graphics.Insets; +import android.graphics.Rect; import android.util.AttributeSet; +import android.view.WindowInsets; import com.android.launcher3.util.TouchController; import com.android.launcher3.views.BaseDragLayer; @@ -30,5 +33,23 @@ public final class GoRecentsActivityRootView extends BaseDragLayer Date: Wed, 8 May 2019 15:29:37 -0700 Subject: [PATCH 30/72] Not using magic constants for navbar height Change-Id: I6783b8a2ebb059ce35748d8c3e818cbe732ff40d --- Android.bp | 1 + .../quickstep/TouchInteractionService.java | 24 +++------- src/com/android/launcher3/DeviceProfile.java | 6 +-- .../launcher3/InvariantDeviceProfile.java | 2 +- src/com/android/launcher3/ResourceUtils.java | 45 +++++++++++++++++++ src/com/android/launcher3/Utilities.java | 5 +-- .../folder/FolderAnimationManager.java | 3 +- .../graphics/WorkspaceAndHotseatScrim.java | 8 ++-- .../launcher3/widget/WidgetsBottomSheet.java | 4 +- tests/Android.mk | 1 + .../launcher3/ui/AbstractLauncherUiTest.java | 8 +++- .../com/android/launcher3/tapl/AllApps.java | 10 ++--- .../tapl/LauncherInstrumentation.java | 4 ++ .../com/android/launcher3/tapl/Widgets.java | 6 ++- 14 files changed, 86 insertions(+), 41 deletions(-) create mode 100644 src/com/android/launcher3/ResourceUtils.java diff --git a/Android.bp b/Android.bp index 5acec37583..b80282eb54 100644 --- a/Android.bp +++ b/Android.bp @@ -24,6 +24,7 @@ android_library { srcs: [ "tests/tapl/**/*.java", "src/com/android/launcher3/util/SecureSettingsObserver.java", + "src/com/android/launcher3/ResourceUtils.java", "src/com/android/launcher3/TestProtocol.java", ], manifest: "tests/tapl/AndroidManifest.xml", 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 a343a363d7..ee9876ac54 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -35,7 +35,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.res.Resources; import android.graphics.Point; import android.graphics.RectF; import android.graphics.Region; @@ -58,6 +57,7 @@ import android.view.WindowManager; import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.R; +import com.android.launcher3.ResourceUtils; import com.android.launcher3.Utilities; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.logging.EventLogArray; @@ -90,9 +90,6 @@ public class TouchInteractionService extends Service implements public static final LooperExecutor BACKGROUND_EXECUTOR = new LooperExecutor(UiThreadHelper.getBackgroundLooper()); - private static final String NAVBAR_VERTICAL_SIZE = "navigation_bar_frame_height"; - private static final String NAVBAR_HORIZONTAL_SIZE = "navigation_bar_width"; - public static final EventLogArray TOUCH_INTERACTION_LOG = new EventLogArray("touch_interaction_log", 40); @@ -291,15 +288,7 @@ public class TouchInteractionService extends Service implements } private int getNavbarSize(String resName) { - int frameSize; - Resources res = getResources(); - int frameSizeResID = res.getIdentifier(resName, "dimen", "android"); - if (frameSizeResID != 0) { - frameSize = res.getDimensionPixelSize(frameSizeResID); - } else { - frameSize = Utilities.pxFromDp(48, res.getDisplayMetrics()); - } - return frameSize; + return ResourceUtils.getNavbarSize(resName, getResources()); } private void initTouchBounds() { @@ -312,20 +301,21 @@ public class TouchInteractionService extends Service implements defaultDisplay.getRealSize(realSize); mSwipeTouchRegion.set(0, 0, realSize.x, realSize.y); if (mMode == Mode.NO_BUTTON) { - mSwipeTouchRegion.top = mSwipeTouchRegion.bottom - getNavbarSize(NAVBAR_VERTICAL_SIZE); + mSwipeTouchRegion.top = mSwipeTouchRegion.bottom - getNavbarSize( + ResourceUtils.NAVBAR_VERTICAL_SIZE); } else { switch (defaultDisplay.getRotation()) { case Surface.ROTATION_90: mSwipeTouchRegion.left = mSwipeTouchRegion.right - - getNavbarSize(NAVBAR_HORIZONTAL_SIZE); + - getNavbarSize(ResourceUtils.NAVBAR_HORIZONTAL_SIZE); break; case Surface.ROTATION_270: mSwipeTouchRegion.right = mSwipeTouchRegion.left - + getNavbarSize(NAVBAR_HORIZONTAL_SIZE); + + getNavbarSize(ResourceUtils.NAVBAR_HORIZONTAL_SIZE); break; default: mSwipeTouchRegion.top = mSwipeTouchRegion.bottom - - getNavbarSize(NAVBAR_VERTICAL_SIZE); + - getNavbarSize(ResourceUtils.NAVBAR_VERTICAL_SIZE); } } } diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 3d2d7cf9e2..d098b8cd39 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -213,7 +213,7 @@ public class DeviceProfile { // Add a bit of space between nav bar and hotseat in multi-window vertical bar layout. hotseatBarSidePaddingStartPx = isMultiWindowMode && isVerticalBarLayout() ? edgeMarginPx : 0; - hotseatBarSizePx = Utilities.pxFromDp(inv.iconSize, dm) + (isVerticalBarLayout() + hotseatBarSizePx = ResourceUtils.pxFromDp(inv.iconSize, dm) + (isVerticalBarLayout() ? (hotseatBarSidePaddingStartPx + hotseatBarSidePaddingEndPx) : (res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size) + hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx)); @@ -319,7 +319,7 @@ public class DeviceProfile { // Workspace final boolean isVerticalLayout = isVerticalBarLayout(); float invIconSizePx = isVerticalLayout ? inv.landscapeIconSize : inv.iconSize; - iconSizePx = Math.max(1, (int) (Utilities.pxFromDp(invIconSizePx, dm) * scale)); + iconSizePx = Math.max(1, (int) (ResourceUtils.pxFromDp(invIconSizePx, dm) * scale)); iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale); iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale); @@ -399,7 +399,7 @@ public class DeviceProfile { } private void updateFolderCellSize(float scale, DisplayMetrics dm, Resources res) { - folderChildIconSizePx = (int) (Utilities.pxFromDp(inv.iconSize, dm) * scale); + folderChildIconSizePx = (int) (ResourceUtils.pxFromDp(inv.iconSize, dm) * scale); folderChildTextSizePx = (int) (res.getDimensionPixelSize(R.dimen.folder_child_text_size) * scale); diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index 819a551475..8a8a2fbe55 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -210,7 +210,7 @@ public class InvariantDeviceProfile { iconSize = interpolatedDisplayOption.iconSize; iconShapePath = getIconShapePath(context); landscapeIconSize = interpolatedDisplayOption.landscapeIconSize; - iconBitmapSize = Utilities.pxFromDp(iconSize, dm); + iconBitmapSize = ResourceUtils.pxFromDp(iconSize, dm); iconTextSize = interpolatedDisplayOption.iconTextSize; fillResIconDpi = getLauncherIconDensity(iconBitmapSize); diff --git a/src/com/android/launcher3/ResourceUtils.java b/src/com/android/launcher3/ResourceUtils.java new file mode 100644 index 0000000000..8df3290fd0 --- /dev/null +++ b/src/com/android/launcher3/ResourceUtils.java @@ -0,0 +1,45 @@ +/* + * 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; + +import android.content.res.Resources; +import android.util.DisplayMetrics; +import android.util.TypedValue; + +public class ResourceUtils { + public static final String NAVBAR_VERTICAL_SIZE = "navigation_bar_frame_height"; + public static final String NAVBAR_HORIZONTAL_SIZE = "navigation_bar_width"; + + public static int getNavbarSize(String resName, Resources res) { + return getDimenByName(resName, res, 48); + } + + private static int getDimenByName(String resName, Resources res, int defaultValue) { + final int frameSize; + final int frameSizeResID = res.getIdentifier(resName, "dimen", "android"); + if (frameSizeResID != 0) { + frameSize = res.getDimensionPixelSize(frameSizeResID); + } else { + frameSize = pxFromDp(defaultValue, res.getDisplayMetrics()); + } + return frameSize; + } + + public static int pxFromDp(float size, DisplayMetrics metrics) { + return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, size, metrics)); + } +} diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 35b967fda4..5cfd95cd07 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -477,10 +477,7 @@ public final class Utilities { float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT; return (size / densityRatio); } - public static int pxFromDp(float size, DisplayMetrics metrics) { - return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - size, metrics)); - } + public static int pxFromSp(float size, DisplayMetrics metrics) { return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, size, metrics)); diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java index 2450039896..0e2ddd4645 100644 --- a/src/com/android/launcher3/folder/FolderAnimationManager.java +++ b/src/com/android/launcher3/folder/FolderAnimationManager.java @@ -39,6 +39,7 @@ import com.android.launcher3.BubbleTextView; import com.android.launcher3.CellLayout; import com.android.launcher3.Launcher; import com.android.launcher3.R; +import com.android.launcher3.ResourceUtils; import com.android.launcher3.ShortcutAndWidgetContainer; import com.android.launcher3.Utilities; import com.android.launcher3.anim.PropertyResetListener; @@ -165,7 +166,7 @@ public class FolderAnimationManager { Math.round((totalOffsetX + initialSize) / initialScale), Math.round((paddingOffsetY + initialSize) / initialScale)); Rect endRect = new Rect(0, 0, lp.width, lp.height); - float finalRadius = Utilities.pxFromDp(2, mContext.getResources().getDisplayMetrics()); + float finalRadius = ResourceUtils.pxFromDp(2, mContext.getResources().getDisplayMetrics()); // Create the animators. AnimatorSet a = new AnimatorSet(); diff --git a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java index 66f9dbfcdc..c0aa75f288 100644 --- a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java +++ b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java @@ -43,7 +43,7 @@ import android.view.View; import com.android.launcher3.CellLayout; import com.android.launcher3.Launcher; import com.android.launcher3.R; -import com.android.launcher3.Utilities; +import com.android.launcher3.ResourceUtils; import com.android.launcher3.Workspace; import com.android.launcher3.uioverrides.WallpaperColorInfo; import com.android.launcher3.util.Themes; @@ -148,7 +148,7 @@ public class WorkspaceAndHotseatScrim implements mLauncher = Launcher.getLauncher(view.getContext()); mWallpaperColorInfo = WallpaperColorInfo.getInstance(mLauncher); - mMaskHeight = Utilities.pxFromDp(ALPHA_MASK_BITMAP_DP, + mMaskHeight = ResourceUtils.pxFromDp(ALPHA_MASK_BITMAP_DP, view.getResources().getDisplayMetrics()); mTopScrim = Themes.getAttrDrawable(view.getContext(), R.attr.workspaceStatusBarScrim); mBottomMask = mTopScrim == null ? null : createDitheredAlphaMask(); @@ -297,8 +297,8 @@ public class WorkspaceAndHotseatScrim implements public Bitmap createDitheredAlphaMask() { DisplayMetrics dm = mLauncher.getResources().getDisplayMetrics(); - int width = Utilities.pxFromDp(ALPHA_MASK_WIDTH_DP, dm); - int gradientHeight = Utilities.pxFromDp(ALPHA_MASK_HEIGHT_DP, dm); + int width = ResourceUtils.pxFromDp(ALPHA_MASK_WIDTH_DP, dm); + int gradientHeight = ResourceUtils.pxFromDp(ALPHA_MASK_HEIGHT_DP, dm); Bitmap dst = Bitmap.createBitmap(width, mMaskHeight, Bitmap.Config.ALPHA_8); Canvas c = new Canvas(dst); Paint paint = new Paint(Paint.DITHER_FLAG); diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java index 3e2c0aef43..a7078a2f59 100644 --- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java @@ -36,7 +36,7 @@ import com.android.launcher3.Insettable; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; -import com.android.launcher3.Utilities; +import com.android.launcher3.ResourceUtils; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.model.WidgetItem; import com.android.launcher3.util.PackageUserKey; @@ -126,7 +126,7 @@ public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable { // Otherwise, add an empty view to the start as padding (but still scroll edge to edge). View leftPaddingView = LayoutInflater.from(getContext()).inflate( R.layout.widget_list_divider, widgetRow, false); - leftPaddingView.getLayoutParams().width = Utilities.pxFromDp( + leftPaddingView.getLayoutParams().width = ResourceUtils.pxFromDp( 16, getResources().getDisplayMetrics()); widgetCells.addView(leftPaddingView, 0); } diff --git a/tests/Android.mk b/tests/Android.mk index 080c98bb34..0991a0439d 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -30,6 +30,7 @@ else LOCAL_STATIC_JAVA_LIBRARIES += libSharedSystemUI LOCAL_SRC_FILES := $(call all-java-files-under, tapl) \ + ../src/com/android/launcher3/ResourceUtils.java \ ../src/com/android/launcher3/util/SecureSettingsObserver.java \ ../src/com/android/launcher3/TestProtocol.java endif diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 75db2f19e8..b6878bba67 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -51,6 +51,7 @@ import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherState; import com.android.launcher3.MainThreadExecutor; +import com.android.launcher3.ResourceUtils; import com.android.launcher3.Utilities; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.tapl.LauncherInstrumentation; @@ -208,7 +209,9 @@ public abstract class AbstractLauncherUiTest { * @return the matching object. */ protected UiObject2 scrollAndFind(UiObject2 container, BySelector condition) { - container.setGestureMargins(0, 0, 0, 200); + final int margin = ResourceUtils.getNavbarSize( + ResourceUtils.NAVBAR_VERTICAL_SIZE, mLauncher.getResources()) + 1; + container.setGestureMargins(0, 0, 0, margin); int i = 0; for (; ; ) { @@ -216,7 +219,8 @@ public abstract class AbstractLauncherUiTest { mDevice.wait(Until.findObject(condition), SHORT_UI_TIMEOUT); UiObject2 widget = container.findObject(condition); if (widget != null && widget.getVisibleBounds().intersects( - 0, 0, mDevice.getDisplayWidth(), mDevice.getDisplayHeight() - 200)) { + 0, 0, mDevice.getDisplayWidth(), + mDevice.getDisplayHeight() - margin)) { return widget; } if (++i > 40) fail("Too many attempts"); diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java index 1ad00375f8..68bdfe3270 100644 --- a/tests/tapl/com/android/launcher3/tapl/AllApps.java +++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java @@ -23,6 +23,7 @@ import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.Direction; import androidx.test.uiautomator.UiObject2; +import com.android.launcher3.ResourceUtils; import com.android.launcher3.TestProtocol; /** @@ -66,12 +67,9 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer { try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "want to get app icon on all apps")) { final UiObject2 allAppsContainer = verifyActiveContainer(); - if (mLauncher.getNavigationModel() != ZERO_BUTTON) { - final UiObject2 navBar = mLauncher.waitForSystemUiObject("navigation_bar_frame"); - allAppsContainer.setGestureMargins(0, 0, 0, navBar.getVisibleBounds().height() + 1); - } else { - allAppsContainer.setGestureMargins(0, 0, 0, 200); - } + allAppsContainer.setGestureMargins(0, 0, 0, + ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_VERTICAL_SIZE, + mLauncher.getResources()) + 1); final BySelector appIconSelector = AppIcon.getAppIconSelector(appName, mLauncher); if (!hasClickableIcon(allAppsContainer, appIconSelector)) { scrollBackToBeginning(); diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index fd2eabbb0d..4d8ff1bb2d 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -586,6 +586,10 @@ public final class LauncherInstrumentation { return ViewConfiguration.get(getContext()).getScaledTouchSlop(); } + public Resources getResources() { + return getContext().getResources(); + } + private static MotionEvent getMotionEvent(long downTime, long eventTime, int action, float x, float y) { MotionEvent.PointerProperties properties = new MotionEvent.PointerProperties(); diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java index 100a11c94c..f7e0b6c4e8 100644 --- a/tests/tapl/com/android/launcher3/tapl/Widgets.java +++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java @@ -19,6 +19,8 @@ package com.android.launcher3.tapl; import androidx.test.uiautomator.Direction; import androidx.test.uiautomator.UiObject2; +import com.android.launcher3.ResourceUtils; + /** * All widgets container. */ @@ -38,7 +40,9 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer { "want to fling forward in widgets")) { LauncherInstrumentation.log("Widgets.flingForward enter"); final UiObject2 widgetsContainer = verifyActiveContainer(); - widgetsContainer.setGestureMargins(0, 0, 0, 200); + widgetsContainer.setGestureMargins(0, 0, 0, + ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_VERTICAL_SIZE, + mLauncher.getResources()) + 1); widgetsContainer.fling(Direction.DOWN, (int) (FLING_SPEED * mLauncher.getDisplayDensity())); try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung forward")) { From 249e19fde9f19e757f54531be2e8053657a47e8c Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 8 May 2019 17:41:37 -0700 Subject: [PATCH 31/72] Fix 1.5x layout values Some of the values in the 1.5x scale layout dimens were incorrect. This fixes them so that they are correctly roughly 1.5x the 1.0x dimens while still being aligned to the 4 dp grid. Test: Test on walleye_svelte, more closely aligns mocks Change-Id: I26d2e216c67c523799e3a159d864718da211cec1 --- go/quickstep/res/values-sw480dp/dimens.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/go/quickstep/res/values-sw480dp/dimens.xml b/go/quickstep/res/values-sw480dp/dimens.xml index b48dafbafb..571b8a1b84 100644 --- a/go/quickstep/res/values-sw480dp/dimens.xml +++ b/go/quickstep/res/values-sw480dp/dimens.xml @@ -18,13 +18,13 @@ 480dp 90dp - 16dp - 20dp + 24dp + 24dp 4dp - 48dp + 52dp 28dp 28dp - 140dp + 160dp \ No newline at end of file From cde60e24b0b73dee989f5b7d3ae7e2000d77f822 Mon Sep 17 00:00:00 2001 From: Kevin Date: Wed, 8 May 2019 18:21:36 -0700 Subject: [PATCH 32/72] Fix fallback recents not updating list from app When launching an from recents, onStop is not called until the app is fully taken over and the remote animation finishes. As a result, if the user hits the overview button right before the animation finishes, we animate back to recents but never call onStart which is where we hook in to update the recents list. The fix naturally is to move this to onResume instead as once the animation begins, the activity is paused. Bug: 132293341 Test: Repro from bug, correctly updates Change-Id: Ie6cfdc4d5aa6b1742a3bce3e14ab5ab6a4f05526 --- go/quickstep/src/com/android/quickstep/RecentsActivity.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go/quickstep/src/com/android/quickstep/RecentsActivity.java b/go/quickstep/src/com/android/quickstep/RecentsActivity.java index 9fb80679e3..34315f3b70 100644 --- a/go/quickstep/src/com/android/quickstep/RecentsActivity.java +++ b/go/quickstep/src/com/android/quickstep/RecentsActivity.java @@ -67,8 +67,8 @@ public final class RecentsActivity extends BaseRecentsActivity { } @Override - protected void onStart() { + protected void onResume() { mIconRecentsView.onBeginTransitionToOverview(); - super.onStart(); + super.onResume(); } } From 9aaa92672d1a10c1d72a171f3310e1dee6acd72f Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 9 May 2019 11:42:12 -0500 Subject: [PATCH 33/72] Move InputConsumers to new inputconsumers package Change-Id: I3318784523b8ff1b9468cbfed3a2682985274ea8 --- .../quickstep/RecentsAnimationWrapper.java | 1 + .../quickstep/TouchInteractionService.java | 6 ++++++ .../quickstep/WindowTransformSwipeHandler.java | 18 ++++++++++++++---- .../AccessibilityInputConsumer.java | 2 +- .../AssistantTouchConsumer.java | 5 +++-- .../DelegateInputConsumer.java | 2 +- .../DeviceLockedInputConsumer.java | 2 +- .../{ => inputconsumers}/InputConsumer.java | 2 +- .../OtherActivityInputConsumer.java | 10 ++++++++-- .../OverviewInputConsumer.java | 4 +++- .../StartLauncherViaGestureTests.java | 1 + 11 files changed, 40 insertions(+), 13 deletions(-) rename quickstep/recents_ui_overrides/src/com/android/quickstep/{ => inputconsumers}/AccessibilityInputConsumer.java (99%) rename quickstep/recents_ui_overrides/src/com/android/quickstep/{ => inputconsumers}/AssistantTouchConsumer.java (98%) rename quickstep/recents_ui_overrides/src/com/android/quickstep/{ => inputconsumers}/DelegateInputConsumer.java (96%) rename quickstep/recents_ui_overrides/src/com/android/quickstep/{ => inputconsumers}/DeviceLockedInputConsumer.java (97%) rename quickstep/recents_ui_overrides/src/com/android/quickstep/{ => inputconsumers}/InputConsumer.java (97%) rename quickstep/recents_ui_overrides/src/com/android/quickstep/{ => inputconsumers}/OtherActivityInputConsumer.java (97%) rename quickstep/recents_ui_overrides/src/com/android/quickstep/{ => inputconsumers}/OverviewInputConsumer.java (96%) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java index 96d2dca971..5eecf1713d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java @@ -24,6 +24,7 @@ import android.view.KeyEvent; import android.view.MotionEvent; import com.android.launcher3.util.Preconditions; +import com.android.quickstep.inputconsumers.InputConsumer; import com.android.quickstep.util.SwipeAnimationTargetSet; import com.android.systemui.shared.system.InputConsumerController; 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 ee9876ac54..cad6074239 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -66,6 +66,12 @@ import com.android.launcher3.util.LooperExecutor; import com.android.launcher3.util.UiThreadHelper; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener; +import com.android.quickstep.inputconsumers.AccessibilityInputConsumer; +import com.android.quickstep.inputconsumers.AssistantTouchConsumer; +import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer; +import com.android.quickstep.inputconsumers.InputConsumer; +import com.android.quickstep.inputconsumers.OtherActivityInputConsumer; +import com.android.quickstep.inputconsumers.OverviewInputConsumer; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; 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 58ae5eb739..49c95f12c9 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -67,6 +67,7 @@ import android.view.WindowManager; import android.view.animation.Interpolator; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.UiThread; import com.android.launcher3.AbstractFloatingView; @@ -91,6 +92,8 @@ import com.android.quickstep.ActivityControlHelper.AnimationFactory; import com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState; import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory; import com.android.quickstep.SysUINavigationMode.Mode; +import com.android.quickstep.inputconsumers.InputConsumer; +import com.android.quickstep.inputconsumers.OverviewInputConsumer; import com.android.quickstep.util.ClipAnimationHelper; import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.RemoteAnimationTargetSet; @@ -166,7 +169,7 @@ public class WindowTransformSwipeHandler private static final int LAUNCHER_UI_STATES = STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED; - enum GestureEndTarget { + public enum GestureEndTarget { HOME(1, STATE_SCALED_CONTROLLER_HOME, true, false, ContainerType.WORKSPACE, false), RECENTS(1, STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT @@ -220,8 +223,8 @@ public class WindowTransformSwipeHandler private final ClipAnimationHelper mClipAnimationHelper; private final ClipAnimationHelper.TransformParams mTransformParams; - protected Runnable mGestureEndCallback; - protected GestureEndTarget mGestureEndTarget; + private Runnable mGestureEndCallback; + private GestureEndTarget mGestureEndTarget; // Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise private RunningWindowAnim mRunningWindowAnim; private boolean mIsShelfPeeking; @@ -273,7 +276,7 @@ public class WindowTransformSwipeHandler private final long mTouchTimeMs; private long mLauncherFrameDrawnTime; - WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context, + public WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context, long touchTimeMs, ActivityControlHelper controller, boolean continuingLastGesture, InputConsumerController inputConsumer) { mContext = context; @@ -1125,6 +1128,13 @@ public class WindowTransformSwipeHandler return anim; } + /** + * @return The GestureEndTarget if the gesture has ended, else null. + */ + public @Nullable GestureEndTarget getGestureEndTarget() { + return mGestureEndTarget; + } + @UiThread private void resumeLastTask() { mRecentsAnimationWrapper.finish(false /* toRecents */, null); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AccessibilityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java similarity index 99% rename from quickstep/recents_ui_overrides/src/com/android/quickstep/AccessibilityInputConsumer.java rename to quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java index 8f8cd18fea..f8475ca182 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AccessibilityInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.quickstep; +package com.android.quickstep.inputconsumers; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java similarity index 98% rename from quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java rename to quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java index 335e8b15a2..0448fd14ac 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.quickstep; +package com.android.quickstep.inputconsumers; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; @@ -43,6 +43,7 @@ import com.android.launcher3.R; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.touch.SwipeDetector; +import com.android.quickstep.ActivityControlHelper; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.InputMonitorCompat; import com.android.systemui.shared.system.QuickStepContract; @@ -236,7 +237,7 @@ public class AssistantTouchConsumer extends DelegateInputConsumer } } - static boolean withinTouchRegion(Context context, MotionEvent ev) { + public static boolean withinTouchRegion(Context context, MotionEvent ev) { final Resources res = context.getResources(); final int width = res.getDisplayMetrics().widthPixels; final int height = res.getDisplayMetrics().heightPixels; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/DelegateInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java similarity index 96% rename from quickstep/recents_ui_overrides/src/com/android/quickstep/DelegateInputConsumer.java rename to quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java index d36162f8be..311ddd27ca 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/DelegateInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java @@ -1,4 +1,4 @@ -package com.android.quickstep; +package com.android.quickstep.inputconsumers; import android.view.MotionEvent; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java similarity index 97% rename from quickstep/recents_ui_overrides/src/com/android/quickstep/DeviceLockedInputConsumer.java rename to quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java index 7fd09f7dee..b1d175df85 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/DeviceLockedInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.quickstep; +package com.android.quickstep.inputconsumers; import android.content.Context; import android.content.Intent; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java similarity index 97% rename from quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java rename to quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java index 37b728871c..3593d162d4 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.quickstep; +package com.android.quickstep.inputconsumers; import android.annotation.TargetApi; import android.os.Build; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java similarity index 97% rename from quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java rename to quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java index db377b0c0c..e862fa6c88 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.quickstep; +package com.android.quickstep.inputconsumers; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; @@ -50,7 +50,13 @@ import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.RaceConditionTracker; import com.android.launcher3.util.TraceHelper; +import com.android.quickstep.ActivityControlHelper; +import com.android.quickstep.OverviewCallbacks; +import com.android.quickstep.RecentsModel; +import com.android.quickstep.SwipeSharedState; +import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode.Mode; +import com.android.quickstep.WindowTransformSwipeHandler; import com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget; import com.android.quickstep.util.CachedEventDispatcher; import com.android.quickstep.util.MotionPauseDetector; @@ -376,7 +382,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC // The consumer is being switched while we are active. Set up the shared state to be // used by the next animation removeListener(); - GestureEndTarget endTarget = mInteractionHandler.mGestureEndTarget; + GestureEndTarget endTarget = mInteractionHandler.getGestureEndTarget(); mSwipeSharedState.canGestureBeContinued = endTarget != null && endTarget.canBeContinued; mSwipeSharedState.goingToLauncher = endTarget != null && endTarget.isLauncher; if (mSwipeSharedState.canGestureBeContinued) { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java similarity index 96% rename from quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java rename to quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java index bafc367c80..bab3c71c60 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.quickstep; +package com.android.quickstep.inputconsumers; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG; @@ -27,6 +27,8 @@ import androidx.annotation.Nullable; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.Utilities; import com.android.launcher3.views.BaseDragLayer; +import com.android.quickstep.ActivityControlHelper; +import com.android.quickstep.OverviewCallbacks; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputMonitorCompat; diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java index 8bdb90db79..2111e2ca27 100644 --- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java +++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java @@ -28,6 +28,7 @@ import com.android.launcher3.Launcher; import com.android.launcher3.util.RaceConditionReproducer; import com.android.quickstep.NavigationModeSwitchRule.Mode; import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; +import com.android.quickstep.inputconsumers.OtherActivityInputConsumer; import org.junit.Before; import org.junit.Ignore; From 90137b035c8e9f0537dec2f056d0a1423c50241b Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 9 May 2019 10:52:16 -0700 Subject: [PATCH 34/72] Add task launch logging to Recents Go Add logging points for launching tasks in Recents Go to match up with non-Go Recents. Bug: 131840601 Test: Log calls to confirm writing to stats log on task launch Change-Id: I3d7b1ed30f99ce9f551c3481628ca81540e366b5 --- .../android/quickstep/TaskActionController.java | 17 +++++++++++++---- .../quickstep/views/IconRecentsView.java | 3 ++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/go/quickstep/src/com/android/quickstep/TaskActionController.java b/go/quickstep/src/com/android/quickstep/TaskActionController.java index 0e921c0db1..f49fa3ef4f 100644 --- a/go/quickstep/src/com/android/quickstep/TaskActionController.java +++ b/go/quickstep/src/com/android/quickstep/TaskActionController.java @@ -16,14 +16,17 @@ package com.android.quickstep; import static com.android.quickstep.TaskAdapter.TASKS_START_POSITION; +import static com.android.quickstep.TaskUtils.getLaunchComponentKeyForTask; import android.app.ActivityOptions; import android.view.View; import androidx.annotation.NonNull; +import com.android.launcher3.logging.StatsLogManager; import com.android.quickstep.views.TaskItemView; import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.Task.TaskKey; import com.android.systemui.shared.system.ActivityManagerWrapper; /** @@ -34,10 +37,13 @@ public final class TaskActionController { private final TaskListLoader mLoader; private final TaskAdapter mAdapter; + private final StatsLogManager mStatsLogManager; - public TaskActionController(TaskListLoader loader, TaskAdapter adapter) { + public TaskActionController(TaskListLoader loader, TaskAdapter adapter, + StatsLogManager logManager) { mLoader = loader; mAdapter = adapter; + mStatsLogManager = logManager; } /** @@ -56,10 +62,11 @@ public final class TaskActionController { int width = v.getMeasuredWidth(); int height = v.getMeasuredHeight(); + TaskKey key = viewHolder.getTask().get().key; ActivityOptions opts = ActivityOptions.makeClipRevealAnimation(v, left, top, width, height); - ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync( - viewHolder.getTask().get().key, opts, null /* resultCallback */, - null /* resultCallbackHandler */); + ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(key, opts, + null /* resultCallback */, null /* resultCallbackHandler */); + mStatsLogManager.logTaskLaunch(null /* view */, getLaunchComponentKeyForTask(key)); } /** @@ -71,6 +78,7 @@ public final class TaskActionController { ActivityOptions opts = ActivityOptions.makeBasic(); ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(task.key, opts, null /* resultCallback */, null /* resultCallbackHandler */); + mStatsLogManager.logTaskLaunch(null /* view */, getLaunchComponentKeyForTask(task.key)); } /** @@ -87,6 +95,7 @@ public final class TaskActionController { ActivityManagerWrapper.getInstance().removeTask(task.key.id); mLoader.removeTask(task); mAdapter.notifyItemRemoved(position); + // TODO(b/131840601): Add logging point for removal. } /** diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index 07faa4bbdd..79e0bbed17 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -154,7 +154,8 @@ public final class IconRecentsView extends FrameLayout implements Insettable { mTaskLoader = new TaskListLoader(mContext); mTaskAdapter = new TaskAdapter(mTaskLoader); mTaskAdapter.setOnClearAllClickListener(view -> animateClearAllTasks()); - mTaskActionController = new TaskActionController(mTaskLoader, mTaskAdapter); + mTaskActionController = new TaskActionController(mTaskLoader, mTaskAdapter, + mActivity.getStatsLogManager()); mTaskAdapter.setActionController(mTaskActionController); mTaskLayoutManager = new LinearLayoutManager(mContext, VERTICAL, true /* reverseLayout */); RecentsModel.INSTANCE.get(context).addThumbnailChangeListener(listener); From 7cf4b40a9777d710a064564dd6e47dee19c278fc Mon Sep 17 00:00:00 2001 From: George Hodulik Date: Thu, 9 May 2019 14:23:23 -0700 Subject: [PATCH 35/72] Use strings "home" and "overview" rather than GEL Bug:129421797 Test: Flashed to QP1A.190508.001, installed launcher with change. Observed both home and overview predictions were as expected. Change-Id: I127d489eae382f4cee75afdd6d615d6d7a8227ee --- .../launcher3/appprediction/PredictionUiStateManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java index 48a163d870..6dad9afe57 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java @@ -67,8 +67,8 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf // TODO (b/129421797): Update the client constants public enum Client { - HOME("GEL"), - OVERVIEW("OVERVIEW_GEL"); + HOME("home"), + OVERVIEW("overview"); public final String id; From 68c67540cd740a5fd94f237791ece42067b1e02f Mon Sep 17 00:00:00 2001 From: Alex Mang Date: Mon, 6 May 2019 21:20:07 -0700 Subject: [PATCH 36/72] Adding query length parameter to protobuf Bug: 131940434 Change-Id: Iaa7ca520406df882e154db3662a190607eb61b23 --- protos/launcher_log.proto | 1 + src/com/android/launcher3/logging/LoggerUtils.java | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto index 02c6b0f419..49fd43617e 100644 --- a/protos/launcher_log.proto +++ b/protos/launcher_log.proto @@ -56,6 +56,7 @@ message Target { optional int32 predictedRank = 15; optional TargetExtension extension = 16; optional TipType tip_type = 17; + optional int32 search_query_length = 18; } // Used to define what type of item a Target would represent. diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java index 1ffa698572..9b75b43b4a 100644 --- a/src/com/android/launcher3/logging/LoggerUtils.java +++ b/src/com/android/launcher3/logging/LoggerUtils.java @@ -144,6 +144,10 @@ public class LoggerUtils { + "), pageIdx=" + t.pageIndex; } + if (t.searchQueryLength != 0) { + typeStr += ", searchQueryLength=" + t.searchQueryLength; + } + if (t.itemType == ItemType.TASK) { typeStr += ", pageIdx=" + t.pageIndex; } From 24d31d4b6e21155a2ca2608f7acf803e6e14c5a1 Mon Sep 17 00:00:00 2001 From: vadimt Date: Thu, 9 May 2019 14:12:18 -0700 Subject: [PATCH 37/72] Disabling updating All Apps during a long-press recognition This is supposed to eliminate test flakes in the lab. When an app gets updated while all apps is opened, all-apps will be re-laid-out. If the layout happens while we are recognizing a long-press on an app icon in all Apps, the long-press won't be recognized. In the lab, this happened so frequently that it caused considerable flakiness. Even after we started running tests in airplane mode, this still keeps happening. The bug refers an example with airplane mode on. The fix will be also mildly beneficial for users. Bug: 132177321 Change-Id: Ie7c7473fe94b8af83f04cd719286bae69e2d9de0 --- .../launcher3/allapps/AllAppsContainerView.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index e2a5160af9..41252aab54 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -604,4 +604,18 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo return super.performAccessibilityAction(action, arguments); } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + mAllAppsStore.setDeferUpdates(true); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mAllAppsStore.setDeferUpdates(false); + break; + } + return super.dispatchTouchEvent(ev); + } } From 63517a7a0721a5b3c176f9a80a78a2e19827dd26 Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 9 May 2019 16:01:01 -0700 Subject: [PATCH 38/72] Fix anim done callback not being called sometimes We should call dispatchFinishedWhenDone in endAnimations instead of calling dispatchAnimationsFinished as the former will run the listener. However, we only do this if we were running animations in the first place. Bug: 132285006 Test: Build and manual test endAnimations() call Change-Id: I31afdc4c9e3ef5f56eda6178569d5c264c16ae78 --- .../src/com/android/quickstep/ContentFillItemAnimator.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java b/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java index 87ae6955e6..9b4ab8e30e 100644 --- a/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java +++ b/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java @@ -235,6 +235,9 @@ public final class ContentFillItemAnimator extends SimpleItemAnimator { @Override public void endAnimations() { + if (!isRunning()) { + return; + } for (int i = mPendingAnims.size() - 1; i >= 0; i--) { PendingAnimation pendAnim = mPendingAnims.get(i); ViewHolder item = pendAnim.viewHolder; @@ -254,7 +257,7 @@ public final class ContentFillItemAnimator extends SimpleItemAnimator { ObjectAnimator anim = mRunningAnims.get(i); anim.end(); } - dispatchAnimationsFinished(); + dispatchFinishedWhenDone(); } @Override From 4ff2a278a20821fc11e301cc1db555227c8eab02 Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 9 May 2019 15:26:52 -0700 Subject: [PATCH 39/72] Use Animator cancel instead of end Animtor's cancel() skips some animator init logic that end() does so we'd like to use that instead. The on end callback will still set the animation value to its target end value so this doesn't affect correctness. Bug: 132285006 Test: Build and manual test endAnimations Change-Id: I450a888c7b37a7dc74f24035d9abc5bda1cbbbd8 --- .../src/com/android/quickstep/ContentFillItemAnimator.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java b/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java index 87ae6955e6..c5e4b839f1 100644 --- a/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java +++ b/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java @@ -180,6 +180,7 @@ public final class ContentFillItemAnimator extends SimpleItemAnimator { @Override public void onAnimationEnd(Animator animation) { + CONTENT_TRANSITION_PROGRESS.set(itemView, 1.0f); dispatchChangeFinished(viewHolder, true /* oldItem */); mRunningAnims.remove(anim); dispatchFinishedWhenDone(); @@ -252,7 +253,8 @@ public final class ContentFillItemAnimator extends SimpleItemAnimator { } for (int i = mRunningAnims.size() - 1; i >= 0; i--) { ObjectAnimator anim = mRunningAnims.get(i); - anim.end(); + // This calls the on end animation callback which will set values to their end target. + anim.cancel(); } dispatchAnimationsFinished(); } From 918863083bc6cddba9d2fbe4c1c113667e23f2d9 Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 9 May 2019 14:25:58 -0500 Subject: [PATCH 40/72] Add ScreenPinnedInputConsumer When screen pinning is active, swipe up and hold to stop it. Bug: 130828539 Change-Id: I343050d2a224ac723143cd3be4f78bc321f1a026 --- .../android/quickstep/TaskSystemShortcut.java | 4 - .../quickstep/TouchInteractionService.java | 7 ++ .../inputconsumers/InputConsumer.java | 1 + .../ScreenPinnedInputConsumer.java | 88 +++++++++++++++++++ quickstep/res/values/dimens.xml | 1 + .../quickstep/util/MotionPauseDetector.java | 42 +++++++-- 6 files changed, 134 insertions(+), 9 deletions(-) create mode 100644 quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java index e20ef52591..2c919b3c17 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java @@ -296,10 +296,6 @@ public class TaskSystemShortcut extends SystemShortcut if (sysUiProxy == null) { return null; } - if (SysUINavigationMode.getMode(activity) == SysUINavigationMode.Mode.NO_BUTTON) { - // TODO(b/130225926): Temporarily disable pinning while gesture nav is enabled - return null; - } if (!ActivityManagerWrapper.getInstance().isScreenPinningEnabled()) { return null; } 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 cad6074239..128fd45fe5 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -72,6 +72,7 @@ import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer; import com.android.quickstep.inputconsumers.InputConsumer; import com.android.quickstep.inputconsumers.OtherActivityInputConsumer; import com.android.quickstep.inputconsumers.OverviewInputConsumer; +import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -466,6 +467,12 @@ public class TouchInteractionService extends Service implements mInputMonitorCompat); } + if (ActivityManagerWrapper.getInstance().isScreenPinningActive()) { + // Note: we only allow accessibility to wrap this, and it replaces the previous + // base input consumer (which should be NO_OP anyway since topTaskLocked == true). + base = new ScreenPinnedInputConsumer(this, mISystemUiProxy, activityControl); + } + if ((mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0) { base = new AccessibilityInputConsumer(this, mISystemUiProxy, (mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0, base, diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java index 3593d162d4..2e8880dde5 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java @@ -30,6 +30,7 @@ public interface InputConsumer { int TYPE_ASSISTANT = 1 << 3; int TYPE_DEVICE_LOCKED = 1 << 4; int TYPE_ACCESSIBILITY = 1 << 5; + int TYPE_SCREEN_PINNED = 1 << 6; InputConsumer NO_OP = () -> TYPE_NO_OP; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java new file mode 100644 index 0000000000..a0e20f2cd8 --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java @@ -0,0 +1,88 @@ +/* + * 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.quickstep.inputconsumers; + +import android.content.Context; +import android.os.RemoteException; +import android.util.Log; +import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; + +import com.android.launcher3.BaseDraggingActivity; +import com.android.launcher3.R; +import com.android.quickstep.ActivityControlHelper; +import com.android.quickstep.util.MotionPauseDetector; +import com.android.systemui.shared.recents.ISystemUiProxy; + +/** + * An input consumer that detects swipe up and hold to exit screen pinning mode. + */ +public class ScreenPinnedInputConsumer implements InputConsumer { + + private static final String TAG = "ScreenPinnedConsumer"; + + private final float mMotionPauseMinDisplacement; + private final MotionPauseDetector mMotionPauseDetector; + + private float mTouchDownY; + + public ScreenPinnedInputConsumer(Context context, ISystemUiProxy sysuiProxy, + ActivityControlHelper activityControl) { + mMotionPauseMinDisplacement = context.getResources().getDimension( + R.dimen.motion_pause_detector_min_displacement_from_app); + mMotionPauseDetector = new MotionPauseDetector(context, true /* makePauseHarderToTrigger*/); + mMotionPauseDetector.setOnMotionPauseListener(isPaused -> { + if (isPaused) { + try { + sysuiProxy.stopScreenPinning(); + BaseDraggingActivity launcherActivity = activityControl.getCreatedActivity(); + if (launcherActivity != null) { + launcherActivity.getRootView().performHapticFeedback( + HapticFeedbackConstants.LONG_PRESS, + HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + } + mMotionPauseDetector.clear(); + } catch (RemoteException e) { + Log.e(TAG, "Unable to stop screen pinning ", e); + } + } + }); + } + + @Override + public int getType() { + return TYPE_SCREEN_PINNED; + } + + @Override + public void onMotionEvent(MotionEvent ev) { + float y = ev.getY(); + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + mTouchDownY = y; + break; + case MotionEvent.ACTION_MOVE: + float displacement = mTouchDownY - y; + mMotionPauseDetector.setDisallowPause(displacement < mMotionPauseMinDisplacement); + mMotionPauseDetector.addPosition(y, ev.getEventTime()); + break; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + mMotionPauseDetector.clear(); + break; + } + } +} diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 32f312fdbe..82d1aa6728 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -36,6 +36,7 @@ 0.0285dp + 0.15dp 0.285dp 0.5dp 36dp diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java index f58f0d4857..893c053565 100644 --- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java +++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java @@ -35,10 +35,18 @@ public class MotionPauseDetector { /** If no motion is added for this amount of time, assume the motion has paused. */ private static final long FORCE_PAUSE_TIMEOUT = 300; + /** + * After {@link #makePauseHarderToTrigger()}, must + * move slowly for this long to trigger a pause. + */ + private static final long HARDER_TRIGGER_TIMEOUT = 400; + private final float mSpeedVerySlow; + private final float mSpeedSlow; private final float mSpeedSomewhatFast; private final float mSpeedFast; private final Alarm mForcePauseTimeout; + private final boolean mMakePauseHarderToTrigger; private Long mPreviousTime = null; private Float mPreviousPosition = null; @@ -52,19 +60,29 @@ public class MotionPauseDetector { private boolean mHasEverBeenPaused; /** @see #setDisallowPause(boolean) */ private boolean mDisallowPause; + // Time at which speed became < mSpeedSlow (only used if mMakePauseHarderToTrigger == true). + private long mSlowStartTime; public MotionPauseDetector(Context context) { + this(context, false); + } + + /** + * @param makePauseHarderToTrigger Used for gestures that require a more explicit pause. + */ + public MotionPauseDetector(Context context, boolean makePauseHarderToTrigger) { Resources res = context.getResources(); mSpeedVerySlow = res.getDimension(R.dimen.motion_pause_detector_speed_very_slow); + mSpeedSlow = res.getDimension(R.dimen.motion_pause_detector_speed_slow); mSpeedSomewhatFast = res.getDimension(R.dimen.motion_pause_detector_speed_somewhat_fast); mSpeedFast = res.getDimension(R.dimen.motion_pause_detector_speed_fast); mForcePauseTimeout = new Alarm(); mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */)); + mMakePauseHarderToTrigger = makePauseHarderToTrigger; } /** - * Get callbacks for when motion pauses and resumes, including an - * immediate callback with the current pause state. + * Get callbacks for when motion pauses and resumes. */ public void setOnMotionPauseListener(OnMotionPauseListener listener) { mOnMotionPauseListener = listener; @@ -88,13 +106,15 @@ public class MotionPauseDetector { if (mFirstPosition == null) { mFirstPosition = position; } - mForcePauseTimeout.setAlarm(FORCE_PAUSE_TIMEOUT); + mForcePauseTimeout.setAlarm(mMakePauseHarderToTrigger + ? HARDER_TRIGGER_TIMEOUT + : FORCE_PAUSE_TIMEOUT); if (mPreviousTime != null && mPreviousPosition != null) { long changeInTime = Math.max(1, time - mPreviousTime); float changeInPosition = position - mPreviousPosition; float velocity = changeInPosition / changeInTime; if (mPreviousVelocity != null) { - checkMotionPaused(velocity, mPreviousVelocity); + checkMotionPaused(velocity, mPreviousVelocity, time); } mPreviousVelocity = velocity; } @@ -102,7 +122,7 @@ public class MotionPauseDetector { mPreviousPosition = position; } - private void checkMotionPaused(float velocity, float prevVelocity) { + private void checkMotionPaused(float velocity, float prevVelocity, long time) { float speed = Math.abs(velocity); float previousSpeed = Math.abs(prevVelocity); boolean isPaused; @@ -122,6 +142,17 @@ public class MotionPauseDetector { boolean isRapidDeceleration = speed < previousSpeed * RAPID_DECELERATION_FACTOR; isPaused = isRapidDeceleration && speed < mSpeedSomewhatFast; } + if (mMakePauseHarderToTrigger) { + if (speed < mSpeedSlow) { + if (mSlowStartTime == 0) { + mSlowStartTime = time; + } + isPaused = time - mSlowStartTime >= HARDER_TRIGGER_TIMEOUT; + } else { + mSlowStartTime = 0; + isPaused = false; + } + } } } updatePaused(isPaused); @@ -149,6 +180,7 @@ public class MotionPauseDetector { mFirstPosition = null; setOnMotionPauseListener(null); mIsPaused = mHasEverBeenPaused = false; + mSlowStartTime = 0; mForcePauseTimeout.cancelAlarm(); } From 5c0ee7ca370dcd2f47f4a557622f23332109a023 Mon Sep 17 00:00:00 2001 From: Kevin Date: Fri, 3 May 2019 13:14:40 -0700 Subject: [PATCH 41/72] Polish app => recents animation (5/5) Add the part of the animation where task views fade in from bottom to top. For the app animating in, we only fade in the label and the icon as the snapshot needs to be visible as the app window scales down to it. Bug: 132112131 Test: Manual test animation Change-Id: Ia3fb98fde0b14fa4f72b53a1941cc2ee6b9f2294 --- .../quickstep/views/IconRecentsView.java | 69 +++++++++++++++++++ .../android/quickstep/views/TaskItemView.java | 8 +++ 2 files changed, 77 insertions(+) diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index 4c91a59661..4413cc95be 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -50,6 +50,7 @@ import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.interpolator.view.animation.LinearOutSlowInInterpolator; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; @@ -114,11 +115,16 @@ public final class IconRecentsView extends FrameLayout implements Insettable { private static final long REMOTE_TO_RECENTS_APP_SCALE_DOWN_DURATION = 300; private static final long REMOTE_TO_RECENTS_VERTICAL_EASE_IN_DURATION = 400; + private static final long REMOTE_TO_RECENTS_ITEM_FADE_START_DELAY = 200; + private static final long REMOTE_TO_RECENTS_ITEM_FADE_DURATION = 217; + private static final long REMOTE_TO_RECENTS_ITEM_FADE_BETWEEN_DELAY = 33; private static final PathInterpolator FAST_OUT_SLOW_IN_1 = new PathInterpolator(.4f, 0f, 0f, 1f); private static final PathInterpolator FAST_OUT_SLOW_IN_2 = new PathInterpolator(.5f, 0f, 0f, 1f); + private static final LinearOutSlowInInterpolator OUT_SLOW_IN = + new LinearOutSlowInInterpolator(); public static final long REMOTE_APP_TO_OVERVIEW_DURATION = REMOTE_TO_RECENTS_VERTICAL_EASE_IN_DURATION; @@ -610,6 +616,7 @@ public final class IconRecentsView extends FrameLayout implements Insettable { playRemoteTransYAnim(anim, appMatrix); playRemoteAppScaleDownAnim(anim, appMatrix, appTarget, recentsTarget, bottomView.getThumbnailView()); + playRemoteTaskListFadeIn(anim, bottomView); } /** @@ -724,6 +731,68 @@ public final class IconRecentsView extends FrameLayout implements Insettable { anim.play(remoteAppAnim); } + /** + * Play task list fade in animation as part of remote app to recents animation. This animation + * ensures that the task views in the recents list fade in from bottom to top. + * + * @param anim animator set to play on + * @param appTaskView the task view associated with the remote app closing + */ + private void playRemoteTaskListFadeIn(@NonNull AnimatorSet anim, + @NonNull TaskItemView appTaskView) { + long delay = REMOTE_TO_RECENTS_ITEM_FADE_START_DELAY; + int childCount = mTaskRecyclerView.getChildCount(); + for (int i = 0; i < childCount; i++) { + ValueAnimator fadeAnim = ValueAnimator.ofFloat(0, 1.0f); + fadeAnim.setDuration(REMOTE_TO_RECENTS_ITEM_FADE_DURATION).setInterpolator(OUT_SLOW_IN); + fadeAnim.setStartDelay(delay); + View view = mTaskRecyclerView.getChildAt(i); + if (Objects.equals(view, appTaskView)) { + // Only animate icon and text for the view with snapshot animating in + final View icon = appTaskView.getIconView(); + final View label = appTaskView.getLabelView(); + + icon.setAlpha(0.0f); + label.setAlpha(0.0f); + + fadeAnim.addUpdateListener(alphaVal -> { + float val = alphaVal.getAnimatedFraction(); + + icon.setAlpha(val); + label.setAlpha(val); + }); + fadeAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + icon.setAlpha(1.0f); + label.setAlpha(1.0f); + } + }); + } else { + // Otherwise, fade in the entire view. + view.setAlpha(0.0f); + fadeAnim.addUpdateListener(alphaVal -> { + float val = alphaVal.getAnimatedFraction(); + view.setAlpha(val); + }); + fadeAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + view.setAlpha(1.0f); + } + }); + } + anim.play(fadeAnim); + + int itemType = mTaskRecyclerView.getChildViewHolder(view).getItemViewType(); + if (itemType == ITEM_TYPE_CLEAR_ALL) { + // Don't add delay. Clear all should animate at same time as next view. + continue; + } + delay += REMOTE_TO_RECENTS_ITEM_FADE_BETWEEN_DELAY; + } + } + @Override public void setInsets(Rect insets) { mInsets = insets; diff --git a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java index 6db8013224..f184ad06a0 100644 --- a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java +++ b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java @@ -137,6 +137,14 @@ public final class TaskItemView extends LinearLayout { return mThumbnailView; } + public View getIconView() { + return mIconView; + } + + public View getLabelView() { + return mLabelView; + } + /** * Start a new animation from the current task content to the specified new content. The caller * is responsible for the actual animation control via the property From b0fcded7aa9db78bba69d6476a478b7f35d840da Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 9 May 2019 17:32:11 -0700 Subject: [PATCH 42/72] Move shared end pending anim logic for reusability Some of the logic in endAnimations and endAnimation is shared and should be moved to a common helper method. Test: Builds and functions as before Change-Id: I4973d50ad8778bb397938b5d65d9f95f48ff145b --- .../quickstep/ContentFillItemAnimator.java | 43 ++++++++----------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java b/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java index c5e4b839f1..6a571ef389 100644 --- a/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java +++ b/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java @@ -216,20 +216,8 @@ public final class ContentFillItemAnimator extends SimpleItemAnimator { @Override public void endAnimation(@NonNull ViewHolder item) { for (int i = mPendingAnims.size() - 1; i >= 0; i--) { - PendingAnimation pendAnim = mPendingAnims.get(i); - if (pendAnim.viewHolder == item) { - mPendingAnims.remove(i); - switch (pendAnim.animType) { - case ANIM_TYPE_REMOVE: - dispatchRemoveFinished(item); - break; - case ANIM_TYPE_CHANGE: - dispatchChangeFinished(item, true /* oldItem */); - break; - default: - break; - } - } + endPendingAnimation(mPendingAnims.get(i)); + mPendingAnims.remove(i); } dispatchFinishedWhenDone(); } @@ -237,18 +225,7 @@ public final class ContentFillItemAnimator extends SimpleItemAnimator { @Override public void endAnimations() { for (int i = mPendingAnims.size() - 1; i >= 0; i--) { - PendingAnimation pendAnim = mPendingAnims.get(i); - ViewHolder item = pendAnim.viewHolder; - switch (pendAnim.animType) { - case ANIM_TYPE_REMOVE: - dispatchRemoveFinished(item); - break; - case ANIM_TYPE_CHANGE: - dispatchChangeFinished(item, true /* oldItem */); - break; - default: - break; - } + endPendingAnimation(mPendingAnims.get(i)); mPendingAnims.remove(i); } for (int i = mRunningAnims.size() - 1; i >= 0; i--) { @@ -259,6 +236,20 @@ public final class ContentFillItemAnimator extends SimpleItemAnimator { dispatchAnimationsFinished(); } + private void endPendingAnimation(PendingAnimation pendAnim) { + ViewHolder item = pendAnim.viewHolder; + switch (pendAnim.animType) { + case ANIM_TYPE_REMOVE: + dispatchRemoveFinished(item); + break; + case ANIM_TYPE_CHANGE: + dispatchChangeFinished(item, true /* oldItem */); + break; + default: + break; + } + } + @Override public boolean isRunning() { return !mPendingAnims.isEmpty() || !mRunningAnims.isEmpty(); From f574bd55353086c91553f3a071753afa0af06482 Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 9 May 2019 17:47:37 -0700 Subject: [PATCH 43/72] Ending pending anim should set to end values. Pending animations have not started but when ending animations, we should ensure they are set to their end values based off the documentation for ItemAnimator's endAnimations and endAnimation. Bug: 132285006 Test: Build using endAnimations and manual test Change-Id: I3039a4f192d68a0ead700cd81675b3e7b4ef51f9 --- .../src/com/android/quickstep/ContentFillItemAnimator.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java b/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java index 6a571ef389..68cbd7a3ad 100644 --- a/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java +++ b/go/quickstep/src/com/android/quickstep/ContentFillItemAnimator.java @@ -240,9 +240,11 @@ public final class ContentFillItemAnimator extends SimpleItemAnimator { ViewHolder item = pendAnim.viewHolder; switch (pendAnim.animType) { case ANIM_TYPE_REMOVE: + item.itemView.setAlpha(1.0f); dispatchRemoveFinished(item); break; case ANIM_TYPE_CHANGE: + CONTENT_TRANSITION_PROGRESS.set(item.itemView, 1.0f); dispatchChangeFinished(item, true /* oldItem */); break; default: From 229ecc4224d7748295e9401457643693e10289c1 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Thu, 9 May 2019 23:30:10 -0700 Subject: [PATCH 44/72] Import translations. DO NOT MERGE Auto-generated-cl: translation import Bug: 64712476 Change-Id: Iee3e02b60f19e7230c327b7bfe324ba72195de01 --- quickstep/res/values-af/strings.xml | 3 +++ quickstep/res/values-am/strings.xml | 3 +++ quickstep/res/values-ar/strings.xml | 3 +++ quickstep/res/values-az/strings.xml | 3 +++ quickstep/res/values-b+sr+Latn/strings.xml | 3 +++ quickstep/res/values-be/strings.xml | 3 +++ quickstep/res/values-bg/strings.xml | 3 +++ quickstep/res/values-bn/strings.xml | 3 +++ quickstep/res/values-bs/strings.xml | 3 +++ quickstep/res/values-ca/strings.xml | 3 +++ quickstep/res/values-cs/strings.xml | 3 +++ quickstep/res/values-da/strings.xml | 3 +++ quickstep/res/values-de/strings.xml | 3 +++ quickstep/res/values-el/strings.xml | 3 +++ quickstep/res/values-en-rAU/strings.xml | 3 +++ quickstep/res/values-en-rGB/strings.xml | 3 +++ quickstep/res/values-en-rIN/strings.xml | 3 +++ quickstep/res/values-es-rUS/strings.xml | 3 +++ quickstep/res/values-es/strings.xml | 3 +++ quickstep/res/values-et/strings.xml | 3 +++ quickstep/res/values-eu/strings.xml | 3 +++ quickstep/res/values-fa/strings.xml | 3 +++ quickstep/res/values-fi/strings.xml | 3 +++ quickstep/res/values-fr-rCA/strings.xml | 3 +++ quickstep/res/values-fr/strings.xml | 3 +++ quickstep/res/values-gl/strings.xml | 3 +++ quickstep/res/values-gu/strings.xml | 3 +++ quickstep/res/values-hi/strings.xml | 3 +++ quickstep/res/values-hr/strings.xml | 3 +++ quickstep/res/values-hu/strings.xml | 3 +++ quickstep/res/values-hy/strings.xml | 3 +++ quickstep/res/values-in/strings.xml | 3 +++ quickstep/res/values-is/strings.xml | 3 +++ quickstep/res/values-it/strings.xml | 3 +++ quickstep/res/values-iw/strings.xml | 3 +++ quickstep/res/values-ja/strings.xml | 3 +++ quickstep/res/values-ka/strings.xml | 3 +++ quickstep/res/values-kk/strings.xml | 3 +++ quickstep/res/values-km/strings.xml | 3 +++ quickstep/res/values-kn/strings.xml | 3 +++ quickstep/res/values-ko/strings.xml | 3 +++ quickstep/res/values-ky/strings.xml | 3 +++ quickstep/res/values-lo/strings.xml | 3 +++ quickstep/res/values-lt/strings.xml | 3 +++ quickstep/res/values-lv/strings.xml | 3 +++ quickstep/res/values-mk/strings.xml | 3 +++ quickstep/res/values-ml/strings.xml | 3 +++ quickstep/res/values-mn/strings.xml | 3 +++ quickstep/res/values-mr/strings.xml | 3 +++ quickstep/res/values-ms/strings.xml | 3 +++ quickstep/res/values-my/strings.xml | 3 +++ quickstep/res/values-nb/strings.xml | 3 +++ quickstep/res/values-ne/strings.xml | 3 +++ quickstep/res/values-nl/strings.xml | 3 +++ quickstep/res/values-pa/strings.xml | 3 +++ quickstep/res/values-pl/strings.xml | 3 +++ quickstep/res/values-pt-rPT/strings.xml | 3 +++ quickstep/res/values-pt/strings.xml | 3 +++ quickstep/res/values-ro/strings.xml | 3 +++ quickstep/res/values-ru/strings.xml | 3 +++ quickstep/res/values-si/strings.xml | 3 +++ quickstep/res/values-sk/strings.xml | 3 +++ quickstep/res/values-sl/strings.xml | 3 +++ quickstep/res/values-sq/strings.xml | 3 +++ quickstep/res/values-sr/strings.xml | 3 +++ quickstep/res/values-sv/strings.xml | 3 +++ quickstep/res/values-sw/strings.xml | 3 +++ quickstep/res/values-ta/strings.xml | 3 +++ quickstep/res/values-te/strings.xml | 3 +++ quickstep/res/values-th/strings.xml | 3 +++ quickstep/res/values-tl/strings.xml | 3 +++ quickstep/res/values-tr/strings.xml | 3 +++ quickstep/res/values-uk/strings.xml | 3 +++ quickstep/res/values-ur/strings.xml | 3 +++ quickstep/res/values-uz/strings.xml | 3 +++ quickstep/res/values-vi/strings.xml | 3 +++ quickstep/res/values-zh-rCN/strings.xml | 3 +++ quickstep/res/values-zh-rHK/strings.xml | 3 +++ quickstep/res/values-zh-rTW/strings.xml | 3 +++ quickstep/res/values-zu/strings.xml | 3 +++ 80 files changed, 240 insertions(+) diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml index 88954b2270..64b8e2c3b1 100644 --- a/quickstep/res/values-af/strings.xml +++ b/quickstep/res/values-af/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 minuut" "%1$s oor vandag" + "Programvoorstelle" + "Alle programme" + "Jou voorspelde programme" diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml index 34fb3be414..3daa92226c 100644 --- a/quickstep/res/values-am/strings.xml +++ b/quickstep/res/values-am/strings.xml @@ -31,4 +31,7 @@ "%1$s%2$s" "< 1 ደቂቃ" "ዛሬ %1$s ቀርቷል" + "የመተግበሪያ ጥቆማዎች" + "ሁሉም መተግበሪያዎች" + "የእርስዎ የሚገመቱ መተግበሪያዎች" diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml index ebdcf7363a..73c7c5c033 100644 --- a/quickstep/res/values-ar/strings.xml +++ b/quickstep/res/values-ar/strings.xml @@ -31,4 +31,7 @@ "%1$s، %2$s" "أقل من دقيقة" "يتبقى اليوم %1$s." + "اقتراحات التطبيقات" + "جميع التطبيقات" + "تطبيقاتك المتوقّعة" diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml index 02312f49e4..aa8fa536ac 100644 --- a/quickstep/res/values-az/strings.xml +++ b/quickstep/res/values-az/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 dəq" "Bu gün %1$s qaldı" + "Tətbiq təklifləri" + "Bütün tətbiqlər" + "Təklif edilən tətbiqlər" diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml index baab4a12f2..fbbe9d2dc5 100644 --- a/quickstep/res/values-b+sr+Latn/strings.xml +++ b/quickstep/res/values-b+sr+Latn/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 min" "Još %1$s danas" + "Predlozi aplikacija" + "Sve aplikacije" + "Predviđene aplikacije" diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml index b28f3771a6..c4a277267c 100644 --- a/quickstep/res/values-be/strings.xml +++ b/quickstep/res/values-be/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 хв" "Сёння засталося %1$s" + "Прапановы праграм" + "Усе праграмы" + "Вашы праграмы з падказак" diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml index 0475c0d1f6..9e8c54a9c7 100644 --- a/quickstep/res/values-bg/strings.xml +++ b/quickstep/res/values-bg/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 мин" "Оставащо време днес: %1$s" + "Предложения за приложения" + "Всички приложения" + "Предвидени приложения" diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml index e6764e0161..57f92e5a0c 100644 --- a/quickstep/res/values-bn/strings.xml +++ b/quickstep/res/values-bn/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< ১ মি." "আজকে %1$s বাকি আছে" + "অ্যাপের সাজেশন" + "সব অ্যাপ" + "আপনার প্রয়োজন হতে পারে এমন অ্যাপ" diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml index 77b4c46073..7968f7cc68 100644 --- a/quickstep/res/values-bs/strings.xml +++ b/quickstep/res/values-bs/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 min" "Preostalo vrijeme: %1$s" + "Prijedlozi za aplikacije" + "Sve aplikacije" + "Predviđene aplikacije" diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml index 484f445db7..6420aa8cb8 100644 --- a/quickstep/res/values-ca/strings.xml +++ b/quickstep/res/values-ca/strings.xml @@ -31,4 +31,7 @@ "%1$s; %2$s" "< 1 minut" "temps restant avui: %1$s" + "Suggeriments d\'aplicacions" + "Totes les aplicacions" + "Prediccions d\'aplicacions" diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml index a698d49dee..194ff87dc5 100644 --- a/quickstep/res/values-cs/strings.xml +++ b/quickstep/res/values-cs/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 minuta" "dnes zbývá: %1$s" + "Návrhy aplikací" + "Všechny aplikace" + "Vaše předpovídané aplikace" diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml index b3e8524f8d..b43a76eb94 100644 --- a/quickstep/res/values-da/strings.xml +++ b/quickstep/res/values-da/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 min" "%1$s tilbage i dag" + "Appforslag" + "Alle apps" + "Dine foreslåede apps" diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml index 10e4fd7ce6..7f4e56d260 100644 --- a/quickstep/res/values-de/strings.xml +++ b/quickstep/res/values-de/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 min" "Heute noch %1$s" + "App-Vorschläge" + "Alle Apps" + "App-Vorschläge für dich" diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml index 6ef1e9426c..87268df749 100644 --- a/quickstep/res/values-el/strings.xml +++ b/quickstep/res/values-el/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 λ." "Απομένουν %1$s σήμερα" + "Προτάσεις εφαρμογών" + "Όλες οι εφαρμογές" + "Προβλέψεις εφαρμογών" diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml index d640b63ea6..2d1418e5f4 100644 --- a/quickstep/res/values-en-rAU/strings.xml +++ b/quickstep/res/values-en-rAU/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 minute" "%1$s left today" + "App suggestions" + "All apps" + "Your predicted apps" diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml index d640b63ea6..2d1418e5f4 100644 --- a/quickstep/res/values-en-rGB/strings.xml +++ b/quickstep/res/values-en-rGB/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 minute" "%1$s left today" + "App suggestions" + "All apps" + "Your predicted apps" diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml index d640b63ea6..2d1418e5f4 100644 --- a/quickstep/res/values-en-rIN/strings.xml +++ b/quickstep/res/values-en-rIN/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 minute" "%1$s left today" + "App suggestions" + "All apps" + "Your predicted apps" diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml index c93e8fc4ad..5f5d0bdfe9 100644 --- a/quickstep/res/values-es-rUS/strings.xml +++ b/quickstep/res/values-es-rUS/strings.xml @@ -31,4 +31,7 @@ "%1$s (%2$s)" "< 1 minuto" "Tiempo restante: %1$s" + "Sugerencias de apps" + "Todas las apps" + "Predicción de tus apps" diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml index 3a588e5fc0..329286b3bf 100644 --- a/quickstep/res/values-es/strings.xml +++ b/quickstep/res/values-es/strings.xml @@ -31,4 +31,7 @@ "%1$s (%2$s)" "<1 minuto" "tiempo restante: %1$s" + "Sugerencias de aplicaciones" + "Todas las aplicaciones" + "Predicción de aplicaciones" diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml index 7032765bb0..0577b0f562 100644 --- a/quickstep/res/values-et/strings.xml +++ b/quickstep/res/values-et/strings.xml @@ -31,4 +31,7 @@ "%1$s %2$s" "< 1 minut" "Tääna jäänud %1$s" + "Rakenduste soovitused" + "Kõik rakendused" + "Teie ennustatud rakendused" diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml index 66e08b9f10..c2d149e362 100644 --- a/quickstep/res/values-eu/strings.xml +++ b/quickstep/res/values-eu/strings.xml @@ -31,4 +31,7 @@ "%1$s (%2$s)" "< 1 min" "%1$s gelditzen dira gaur" + "Iradokitako aplikazioak" + "Aplikazio guztiak" + "Lagungarri izan dakizkizukeen aplikazioak" diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml index 112d04cdef..cc26695a60 100644 --- a/quickstep/res/values-fa/strings.xml +++ b/quickstep/res/values-fa/strings.xml @@ -31,4 +31,7 @@ "%1$s، %2$s" "< ۱ دقیقه" "%1$s باقی‌مانده برای امروز" + "برنامه‌های پیشنهادی" + "همه برنامه‌ها" + "برنامه‌های پیش‌بینی‌شده" diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml index 6a0a359305..f43433e3a1 100644 --- a/quickstep/res/values-fi/strings.xml +++ b/quickstep/res/values-fi/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 min" "%1$s jäljellä tänään" + "Sovellusehdotukset" + "Kaikki sovellukset" + "Sovellusennusteet" diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml index 248a5da5ee..a9a1cffb6d 100644 --- a/quickstep/res/values-fr-rCA/strings.xml +++ b/quickstep/res/values-fr-rCA/strings.xml @@ -31,4 +31,7 @@ "%1$s : %2$s" "< 1 min" "Il reste %1$s aujourd\'hui" + "Suggestions d\'applications" + "Toutes les applications" + "Vos prédictions d\'applications" diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml index 338d9baff7..5394f495e9 100644 --- a/quickstep/res/values-fr/strings.xml +++ b/quickstep/res/values-fr/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 min" "Encore %1$s aujourd\'hui" + "Suggestions d\'applications" + "Toutes les applications" + "Vos applications prévues" diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml index d6ddf3cd68..c6698bb480 100644 --- a/quickstep/res/values-gl/strings.xml +++ b/quickstep/res/values-gl/strings.xml @@ -31,4 +31,7 @@ "%1$s (%2$s)" "<1 min" "Tempo restante hoxe %1$s" + "Suxestións de aplicacións" + "Todas as aplicacións" + "As túas aplicacións preditas" diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml index 4493e3b5dd..660ad87acc 100644 --- a/quickstep/res/values-gu/strings.xml +++ b/quickstep/res/values-gu/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 મિનિટ" "%1$s આજે બાકી" + "ઍપ સૂચનો" + "બધી ઍપ" + "તમારી પૂર્વાનુમાનિત ઍપ" diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml index 3c53cce45a..0467af4b2d 100644 --- a/quickstep/res/values-hi/strings.xml +++ b/quickstep/res/values-hi/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "<1 मिनट" "आज %1$s और चलेगा" + "ऐप्लिकेशन के सुझाव" + "सभी ऐप्लिकेशन" + "आपके काम के ऐप्लिकेशन" diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml index 103710ff91..ab56e57b50 100644 --- a/quickstep/res/values-hr/strings.xml +++ b/quickstep/res/values-hr/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 min" "Još %1$s danas" + "Predložene aplikacije" + "Sve aplikacije" + "Vaše predviđene aplikacije" diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml index 22b2380f26..dec6ea0158 100644 --- a/quickstep/res/values-hu/strings.xml +++ b/quickstep/res/values-hu/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 perc" "Ma még %1$s van hátra" + "Alkalmazásjavaslatok" + "Az összes alkalmazás" + "Várható alkalmazások" diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml index 910265acc3..1656a1444d 100644 --- a/quickstep/res/values-hy/strings.xml +++ b/quickstep/res/values-hy/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 ր" "Այսօր մնացել է՝ %1$s" + "Առաջարկվող հավելվածներ" + "Բոլոր հավելվածները" + "Ձեր կանխատեսված հավելվածները" diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml index a7749df883..6824d16b40 100644 --- a/quickstep/res/values-in/strings.xml +++ b/quickstep/res/values-in/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 menit" "%1$s tersisa hari ini" + "Saran aplikasi" + "Semua aplikasi" + "Aplikasi yang diprediksi" diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml index ba0c6723fa..f60a2c6802 100644 --- a/quickstep/res/values-is/strings.xml +++ b/quickstep/res/values-is/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 mín." "%1$s eftir í dag" + "Tillögur að forritum" + "Öll forrit" + "Spáð forrit" diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml index 746443eabd..559fdb4b6e 100644 --- a/quickstep/res/values-it/strings.xml +++ b/quickstep/res/values-it/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 min" "Rimanente oggi: %1$s" + "App suggerite" + "Tutte le app" + "Le app previste" diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml index 96a8adcaf3..58cab4e0a9 100644 --- a/quickstep/res/values-iw/strings.xml +++ b/quickstep/res/values-iw/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "‏< דקה" "הזמן שנותר להיום: %1$s" + "הצעות לאפליקציות" + "כל האפליקציות" + "האפליקציות החזויות שלך" diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml index 5484ae1fcd..d3fecde208 100644 --- a/quickstep/res/values-ja/strings.xml +++ b/quickstep/res/values-ja/strings.xml @@ -31,4 +31,7 @@ "%1$s%2$s" "1 分未満" "今日はあと %1$sです" + "アプリの候補" + "すべてのアプリ" + "予測されたアプリ" diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml index 9218fb8b8a..67b03a754f 100644 --- a/quickstep/res/values-ka/strings.xml +++ b/quickstep/res/values-ka/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 წუთი" "დღეს დარჩენილია %1$s" + "აპების შემოთავაზებები" + "ყველა აპი" + "თქვენი პროგნოზირებული აპები" diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml index 0766150226..a9fcbedb20 100644 --- a/quickstep/res/values-kk/strings.xml +++ b/quickstep/res/values-kk/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 мин" "Бүгін %1$s қалды" + "Қолданба ұсыныстары" + "Барлық қолданбалар" + "Ұсынылатын қолданбалар" diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml index 8737ae8303..c422041e7a 100644 --- a/quickstep/res/values-km/strings.xml +++ b/quickstep/res/values-km/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 នាទី" "នៅសល់ %1$s ទៀត​នៅថ្ងៃនេះ" + "ការណែនាំកម្មវិធី" + "កម្មវិធី​ទាំងអស់" + "កម្មវិធី​ដែលបាន​ព្យាករ​របស់អ្នក" diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml index 099957cb71..52782610b9 100644 --- a/quickstep/res/values-kn/strings.xml +++ b/quickstep/res/values-kn/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 ನಿ" "ಇಂದು %1$s ಸಮಯ ಉಳಿದಿದೆ" + "ಆ್ಯಪ್ ಸಲಹೆಗಳು" + "ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳು" + "ನಿಮ್ಮ ಸಂಭವನೀಯ ಆ್ಯಪ್‌ಗಳು" diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml index 9543e7987f..7a8e6a1f9a 100644 --- a/quickstep/res/values-ko/strings.xml +++ b/quickstep/res/values-ko/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1분" "오늘 %1$s 남음" + "앱 추천" + "모든 앱" + "추천 앱" diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml index d1d2d201d3..4018e57dcc 100644 --- a/quickstep/res/values-ky/strings.xml +++ b/quickstep/res/values-ky/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 мүнөт" "Бүгүн %1$s мүнөт калды" + "Колдонмо сунуштары" + "Бардык колдонмолор" + "Божомолдонгон колдонмолоруңуз" diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml index aba4156d06..e406b7083d 100644 --- a/quickstep/res/values-lo/strings.xml +++ b/quickstep/res/values-lo/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 ນາທີ" "ເຫຼືອ %1$s ມື້ນີ້" + "ການແນະນຳແອັບ" + "ແອັບທັງໝົດ" + "ແອັບທີ່ຄາດເດົາໄວ້ແລ້ວຂອງທ່ານ" diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml index 933b3f094f..ed1fc373e2 100644 --- a/quickstep/res/values-lt/strings.xml +++ b/quickstep/res/values-lt/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 min." "Šiandien liko: %1$s" + "Programų pasiūlymai" + "Visos programos" + "Numatomos programos" diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml index 1e2ed00f1a..85ce0e017d 100644 --- a/quickstep/res/values-lv/strings.xml +++ b/quickstep/res/values-lv/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "<1 minūte" "Šodien atlicis: %1$s" + "Ieteicamās lietotnes" + "Visas lietotnes" + "Jūsu prognozētās lietotnes" diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml index 7a6c094b53..9f11521df3 100644 --- a/quickstep/res/values-mk/strings.xml +++ b/quickstep/res/values-mk/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 минута" "Уште %1$s за денес" + "Предлози за апликации" + "Сите апликации" + "Вашите предвидени апликации" diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml index b5eac1dc32..2e02e80fa8 100644 --- a/quickstep/res/values-ml/strings.xml +++ b/quickstep/res/values-ml/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 മിനിറ്റ്" "ഇന്ന് %1$s ശേഷിക്കുന്നു" + "ആപ്പ് നിർദ്ദേശങ്ങൾ" + "എല്ലാ ആപ്പുകളും" + "നിങ്ങളുടെ പ്രവചിക്കപ്പെട്ട ആപ്പുകൾ" diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml index a105ef1609..5de8602022 100644 --- a/quickstep/res/values-mn/strings.xml +++ b/quickstep/res/values-mn/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 минут" "Өнөөдөр %1$s үлдсэн" + "Аппын зөвлөмж" + "Бүх апп" + "Таны таамагласан аппууд" diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml index bf725e3378..1ca558a240 100644 --- a/quickstep/res/values-mr/strings.xml +++ b/quickstep/res/values-mr/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "१मिहून कमी" "आज %1$sशिल्लक आहे" + "अ‍ॅप सूचना" + "सर्व अ‍ॅप्स" + "तुमची पूर्वानुमानीत अ‍ॅप्स" diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml index 2e3f236b1a..2542963769 100644 --- a/quickstep/res/values-ms/strings.xml +++ b/quickstep/res/values-ms/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 minit" "%1$s lagi hari ini" + "Cadangan apl" + "Semua apl" + "Apl ramalan anda" diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml index 7b931257b3..7683e05990 100644 --- a/quickstep/res/values-my/strings.xml +++ b/quickstep/res/values-my/strings.xml @@ -31,4 +31,7 @@ "%1$s%2$s" "< ၁ မိနစ်" "ယနေ့ %1$s ခု ကျန်သည်" + "အက်ပ်အကြံပြုချက်များ" + "အက်ပ်အားလုံး" + "သင်၏ ခန့်မှန်းအက်ပ်များ" diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml index 74f43d2e24..01bbb6a75d 100644 --- a/quickstep/res/values-nb/strings.xml +++ b/quickstep/res/values-nb/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 minutt" "%1$s gjenstår i dag" + "Appanbefalinger" + "Alle apper" + "Forslag til apper" diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml index 6053def168..60e9bd5313 100644 --- a/quickstep/res/values-ne/strings.xml +++ b/quickstep/res/values-ne/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< १ मिनेट" "आज: %1$s बाँकी" + "अनुप्रयोगसम्बन्धी सुझावहरू" + "सबै अनुप्रयोगहरू" + "तपाईंका पूर्वानुमानित अनुप्रयोगहरू" diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml index 4e3a34c254..8032567e1c 100644 --- a/quickstep/res/values-nl/strings.xml +++ b/quickstep/res/values-nl/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 minuut" "Nog %1$s vandaag" + "App-suggesties" + "Alle apps" + "Je voorspelde apps" diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml index 5aeeae6b3a..58c0d2afa4 100644 --- a/quickstep/res/values-pa/strings.xml +++ b/quickstep/res/values-pa/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 ਮਿੰਟ" "ਅੱਜ %1$s ਬਾਕੀ" + "ਐਪ ਸੰਬੰਧੀ ਸੁਝਾਅ" + "ਸਾਰੀਆਂ ਐਪਾਂ" + "ਤੁਹਾਡੀਆਂ ਪੂਰਵ ਅਨੁਮਾਨਿਤ ਐਪਾਂ" diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml index 210edcf011..d83160dffb 100644 --- a/quickstep/res/values-pl/strings.xml +++ b/quickstep/res/values-pl/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "> 1 min" "Na dziś zostało %1$s" + "Sugerowane aplikacje" + "Wszystkie aplikacje" + "Przewidywane aplikacje" diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml index 8a129d5ea5..2fd34d636c 100644 --- a/quickstep/res/values-pt-rPT/strings.xml +++ b/quickstep/res/values-pt-rPT/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 minuto" "Resta(m) %1$s hoje." + "Sugestões de aplicações" + "Todas as aplicações" + "As suas aplicações previstas" diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml index e5380d560c..673dfe2e73 100644 --- a/quickstep/res/values-pt/strings.xml +++ b/quickstep/res/values-pt/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 min" "%1$s restante(s) hoje" + "Sugestões de apps" + "Todos os apps" + "Suas predições de apps" diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml index 54452a0584..2ac783eb0e 100644 --- a/quickstep/res/values-ro/strings.xml +++ b/quickstep/res/values-ro/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 minut" "Au mai rămas %1$s astăzi" + "Sugestii de aplicații" + "Toate aplicațiile" + "Aplicațiile estimate" diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml index 8b2016ab2f..5dd89a644b 100644 --- a/quickstep/res/values-ru/strings.xml +++ b/quickstep/res/values-ru/strings.xml @@ -31,4 +31,7 @@ "%1$s: %2$s" "< 1 мин." "Осталось сегодня: %1$s" + "Рекомендуемые приложения" + "Все приложения" + "Ваши рекомендуемые приложения" diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml index 2163390679..f6584c4dca 100644 --- a/quickstep/res/values-si/strings.xml +++ b/quickstep/res/values-si/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 විනාඩියක්" "අද %1$sක් ඉතුරුයි" + "යෙදුම් යෝජනා" + "සියලු යෙදුම්" + "ඔබේ පුරෝකථන කළ යෙදුම්" diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml index 12983db6d6..8a9c7365f2 100644 --- a/quickstep/res/values-sk/strings.xml +++ b/quickstep/res/values-sk/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "Menej ako 1 minúta" "Dnes ešte zostáva: %1$s" + "Návrhy aplikácií" + "Všetky aplikácie" + "Vaše predpovedané aplikácie" diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml index a940f2bfc8..15f8f89cc7 100644 --- a/quickstep/res/values-sl/strings.xml +++ b/quickstep/res/values-sl/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 min" "Danes je ostalo še %1$s" + "Predlogi za aplikacije" + "Vse aplikacije" + "Predvidene aplikacije" diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml index e41bcb59aa..d8f5f28b42 100644 --- a/quickstep/res/values-sq/strings.xml +++ b/quickstep/res/values-sq/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 minutë" "%1$s të mbetura sot" + "Sugjerimet e aplikacioneve" + "Të gjitha aplikacionet" + "Aplikacionet e tua të parashikuara" diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml index 8f26c66501..b721641446 100644 --- a/quickstep/res/values-sr/strings.xml +++ b/quickstep/res/values-sr/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 мин" "Још %1$s данас" + "Предлози апликација" + "Све апликације" + "Предвиђене апликације" diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml index 70740e502c..ba7ebcdec7 100644 --- a/quickstep/res/values-sv/strings.xml +++ b/quickstep/res/values-sv/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 min" "%1$s kvar i dag" + "Appförslag" + "Alla appar" + "Föreslagna appar" diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml index c646b6a5fc..24db429093 100644 --- a/quickstep/res/values-sw/strings.xml +++ b/quickstep/res/values-sw/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< dak 1" "Umebakisha %1$s leo" + "Mapendekezo ya programu" + "Programu zote" + "Programu zako zinazopendekezwa" diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml index 19bfaa92f7..97d51cd35f 100644 --- a/quickstep/res/values-ta/strings.xml +++ b/quickstep/res/values-ta/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 நி" "இன்று %1$s மீதமுள்ளது" + "ஆப்ஸ் பரிந்துரைகள்" + "அனைத்து ஆப்ஸும்" + "நீங்கள் கணித்த ஆப்ஸ்" diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml index 071755a95c..24b37f75d4 100644 --- a/quickstep/res/values-te/strings.xml +++ b/quickstep/res/values-te/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 నిమిషం" "నేటికి %1$s మిగిలి ఉంది" + "యాప్ సూచనలు" + "అన్ని యాప్‌లు" + "మీ సూచించబడిన యాప్‌లు" diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml index c0e78cec1f..0f6821bf19 100644 --- a/quickstep/res/values-th/strings.xml +++ b/quickstep/res/values-th/strings.xml @@ -31,4 +31,7 @@ "%1$s %2$s" "<1 นาที" "วันนี้เหลืออีก %1$s" + "คำแนะนำเกี่ยวกับแอป" + "แอปทั้งหมด" + "แอปที่คาดการณ์ไว้" diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml index 76a0b250ac..491bac5bd9 100644 --- a/quickstep/res/values-tl/strings.xml +++ b/quickstep/res/values-tl/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 min" "%1$s na lang ngayon" + "Mga iminumungkahing app" + "Lahat ng app" + "Iyong mga nahulaang app" diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml index 8b59c7bd09..ec6d88405c 100644 --- a/quickstep/res/values-tr/strings.xml +++ b/quickstep/res/values-tr/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 dk." "Bugün %1$s kaldı" + "Uygulama önerileri" + "Tüm uygulamalar" + "Tahmin edilen uygulamalarınız" diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml index 39c3848bd7..77360625db 100644 --- a/quickstep/res/values-uk/strings.xml +++ b/quickstep/res/values-uk/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 хв" "Сьогодні залишилося %1$s" + "Пропозиції додатків" + "Усі додатки" + "Передбачені додатки" diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml index 4fd9e69bf1..87b303f2af 100644 --- a/quickstep/res/values-ur/strings.xml +++ b/quickstep/res/values-ur/strings.xml @@ -31,4 +31,7 @@ "%1$s،%2$s" "‏< 1 منٹ" "آج %1$s بچا ہے" + "ایپ کی تجاویز" + "تمام ایپس" + "آپ کی پیشن گوئی کردہ ایپس" diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml index 466d79ea58..67c8e91c00 100644 --- a/quickstep/res/values-uz/strings.xml +++ b/quickstep/res/values-uz/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 daqiqa" "Bugun %1$s qoldi" + "Tavsiya etiladigan ilovalar" + "Barcha ilovalar" + "Taklif qilingan ilovalaringiz" diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml index 842b22bfe1..34c89efcf1 100644 --- a/quickstep/res/values-vi/strings.xml +++ b/quickstep/res/values-vi/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 phút" "Hôm nay còn %1$s" + "Các ứng dụng đề xuất" + "Tất cả ứng dụng" + "Các ứng dụng gợi ý của bạn" diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml index 951489fff7..0e83977fa3 100644 --- a/quickstep/res/values-zh-rCN/strings.xml +++ b/quickstep/res/values-zh-rCN/strings.xml @@ -31,4 +31,7 @@ "%1$s%2$s)" "不到 1 分钟" "今天还可使用 %1$s" + "应用推荐" + "所有应用" + "您的预测应用" diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml index 361623dfad..ac7e8e9002 100644 --- a/quickstep/res/values-zh-rHK/strings.xml +++ b/quickstep/res/values-zh-rHK/strings.xml @@ -31,4 +31,7 @@ "%1$s%2$s" "少於 1 分鐘" "今天剩餘時間:%1$s" + "應用程式建議" + "所有應用程式" + "您的預測應用程式" diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml index 6938d3e816..3323bfd57d 100644 --- a/quickstep/res/values-zh-rTW/strings.xml +++ b/quickstep/res/values-zh-rTW/strings.xml @@ -31,4 +31,7 @@ "%1$s (%2$s)" "< 1 分鐘" "今天還能使用 %1$s" + "應用程式建議" + "所有應用程式" + "系統預測你會使用的應用程式" diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml index 98f7b02d9a..0f1d99d7b6 100644 --- a/quickstep/res/values-zu/strings.xml +++ b/quickstep/res/values-zu/strings.xml @@ -31,4 +31,7 @@ "%1$s, %2$s" "< 1 iminithi" "%1$s esele namhlanje" + "Iziphakamiso zohlelo lokusebenza" + "Zonke izinhlelo zokusebenza" + "Izinhlelo zakho zokusebenza eziqagelwe" From ede50522274e345638c8b7f78907f74c9039d078 Mon Sep 17 00:00:00 2001 From: Govinda Wasserman Date: Wed, 8 May 2019 08:17:28 -0400 Subject: [PATCH 45/72] Implements the new subscript proactive suggestions style Change-Id: I89575a315f1468bde2a0f7894cd19b1124afa840 --- .../res/layout/proactive_hints_container.xml | 7 ++ .../RecentsViewStateController.java | 18 ++++++ .../hints/ProactiveHintsContainer.java | 55 ++++++++++++++++ .../quickstep/views/LauncherRecentsView.java | 64 +++++++++++++++++++ res/layout/launcher.xml | 5 ++ res/layout/proactive_hints_container.xml | 20 ++++++ res/values/dimens.xml | 1 + 7 files changed, 170 insertions(+) create mode 100644 quickstep/recents_ui_overrides/res/layout/proactive_hints_container.xml create mode 100644 quickstep/recents_ui_overrides/src/com/android/quickstep/hints/ProactiveHintsContainer.java create mode 100644 res/layout/proactive_hints_container.xml diff --git a/quickstep/recents_ui_overrides/res/layout/proactive_hints_container.xml b/quickstep/recents_ui_overrides/res/layout/proactive_hints_container.xml new file mode 100644 index 0000000000..be3f17acd8 --- /dev/null +++ b/quickstep/recents_ui_overrides/res/layout/proactive_hints_container.xml @@ -0,0 +1,7 @@ + + \ No newline at end of file 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 11a1885d1a..c3a7698874 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 @@ -31,6 +31,7 @@ import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.PropertySetter; +import com.android.quickstep.hints.ProactiveHintsContainer; import com.android.quickstep.views.ClearAllButton; import com.android.quickstep.views.LauncherRecentsView; import com.android.quickstep.views.RecentsView; @@ -53,6 +54,14 @@ public final class RecentsViewStateController extends if (state.overviewUi) { mRecentsView.updateEmptyMessage(); mRecentsView.resetTaskVisuals(); + mRecentsView.setHintVisibility(1f); + } else { + mRecentsView.setHintVisibility(0f); + ProactiveHintsContainer + proactiveHintsContainer = mRecentsView.getProactiveHintsContainer(); + if (proactiveHintsContainer != null) { + proactiveHintsContainer.removeAllViews(); + } } setAlphas(PropertySetter.NO_ANIM_PROPERTY_SETTER, state.getVisibleElements(mLauncher)); } @@ -64,6 +73,14 @@ public final class RecentsViewStateController extends if (!toState.overviewUi) { builder.addOnFinishRunnable(mRecentsView::resetTaskVisuals); + mRecentsView.setHintVisibility(0f); + builder.addOnFinishRunnable(() -> { + ProactiveHintsContainer + proactiveHintsContainer = mRecentsView.getProactiveHintsContainer(); + if (proactiveHintsContainer != null) { + proactiveHintsContainer.removeAllViews(); + } + }); } if (toState.overviewUi) { @@ -75,6 +92,7 @@ public final class RecentsViewStateController extends updateAnim.setDuration(config.duration); builder.play(updateAnim); mRecentsView.updateEmptyMessage(); + builder.addOnFinishRunnable(() -> mRecentsView.setHintVisibility(1f)); } setAlphas(config.getPropertySetter(builder), toState.getVisibleElements(mLauncher)); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/ProactiveHintsContainer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/ProactiveHintsContainer.java new file mode 100644 index 0000000000..74a48517b2 --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/ProactiveHintsContainer.java @@ -0,0 +1,55 @@ +package com.android.quickstep.hints; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.FloatProperty; +import android.view.View; +import android.widget.FrameLayout; + +public class ProactiveHintsContainer extends FrameLayout { + + public static final FloatProperty HINT_VISIBILITY = + new FloatProperty("hint_visibility") { + @Override + public void setValue(ProactiveHintsContainer proactiveHintsContainer, float v) { + proactiveHintsContainer.setHintVisibility(v); + } + + @Override + public Float get(ProactiveHintsContainer proactiveHintsContainer) { + return proactiveHintsContainer.mHintVisibility; + } + }; + + private float mHintVisibility; + + public ProactiveHintsContainer(Context context) { + super(context); + } + + public ProactiveHintsContainer(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ProactiveHintsContainer(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public ProactiveHintsContainer(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public void setView(View v) { + removeAllViews(); + addView(v); + } + + public void setHintVisibility(float v) { + if (v == 1) { + setVisibility(VISIBLE); + } else { + setVisibility(GONE); + } + mHintVisibility = v; + } +} 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 d6f2235031..bdac750de7 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 @@ -34,6 +34,8 @@ import android.os.Build; import android.util.AttributeSet; import android.view.View; +import androidx.annotation.Nullable; + import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; @@ -41,8 +43,11 @@ import com.android.launcher3.R; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.appprediction.PredictionUiStateManager; import com.android.launcher3.appprediction.PredictionUiStateManager.Client; +import com.android.launcher3.util.PendingAnimation; +import com.android.launcher3.views.BaseDragLayer; import com.android.launcher3.views.ScrimView; import com.android.quickstep.SysUINavigationMode; +import com.android.quickstep.hints.ProactiveHintsContainer; import com.android.quickstep.util.ClipAnimationHelper; import com.android.quickstep.util.ClipAnimationHelper.TransformParams; import com.android.quickstep.util.LayoutUtils; @@ -54,6 +59,8 @@ import com.android.quickstep.util.LayoutUtils; public class LauncherRecentsView extends RecentsView { private final TransformParams mTransformParams = new TransformParams(); + private final int mChipOverhang; + @Nullable private ProactiveHintsContainer mProactiveHintsContainer; public LauncherRecentsView(Context context) { this(context, null); @@ -66,6 +73,16 @@ public class LauncherRecentsView extends RecentsView { public LauncherRecentsView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setContentAlpha(0); + mChipOverhang = (int) context.getResources().getDimension(R.dimen.chip_hint_overhang); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + View hintContainer = mActivity.findViewById(R.id.hints); + mProactiveHintsContainer = + hintContainer instanceof ProactiveHintsContainer + ? (ProactiveHintsContainer) hintContainer : null; } @Override @@ -84,6 +101,11 @@ public class LauncherRecentsView extends RecentsView { } } + @Nullable + public ProactiveHintsContainer getProactiveHintsContainer() { + return mProactiveHintsContainer; + } + @Override public void draw(Canvas canvas) { maybeDrawEmptyMessage(canvas); @@ -137,6 +159,48 @@ public class LauncherRecentsView extends RecentsView { @Override protected void getTaskSize(DeviceProfile dp, Rect outRect) { LayoutUtils.calculateLauncherTaskSize(getContext(), dp, outRect); + if (mProactiveHintsContainer != null) { + BaseDragLayer.LayoutParams params = (BaseDragLayer.LayoutParams) mProactiveHintsContainer.getLayoutParams(); + params.bottomMargin = getHeight() - outRect.bottom - mChipOverhang; + params.width = outRect.width(); + } + } + + @Override + public PendingAnimation createTaskLauncherAnimation(TaskView tv, long duration) { + PendingAnimation anim = super.createTaskLauncherAnimation(tv, duration); + + if (mProactiveHintsContainer != null) { + anim.anim.play(ObjectAnimator.ofFloat( + mProactiveHintsContainer, ProactiveHintsContainer.HINT_VISIBILITY, 0)); + } + + return anim; + } + + @Override + public PendingAnimation createTaskDismissAnimation(TaskView taskView, boolean animateTaskView, + boolean shouldRemoveTask, long duration) { + PendingAnimation anim = super.createTaskDismissAnimation(taskView, animateTaskView, + shouldRemoveTask, duration); + + if (mProactiveHintsContainer != null) { + anim.anim.play(ObjectAnimator.ofFloat( + mProactiveHintsContainer, ProactiveHintsContainer.HINT_VISIBILITY, 0)); + anim.addEndListener(onEndListener -> { + if (!onEndListener.isSuccess) { + mProactiveHintsContainer.setHintVisibility(1); + } + }); + } + + return anim; + } + + public void setHintVisibility(float v) { + if (mProactiveHintsContainer != null) { + mProactiveHintsContainer.setHintVisibility(v); + } } @Override diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml index cca899bda8..9cab9c2a5e 100644 --- a/res/layout/launcher.xml +++ b/res/layout/launcher.xml @@ -48,6 +48,11 @@ layout="@layout/overview_panel" android:visibility="gone" /> + + + + \ No newline at end of file diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 469b176fb4..0da56dafb9 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -235,6 +235,7 @@ 26dp 194dp + 15dp 8dp From 2da1da30760c7abb576a5fbefc6ec4d12040a2b2 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Fri, 10 May 2019 08:50:49 -0700 Subject: [PATCH 46/72] Initial app open/close polish using tuned values. * Rect and radius now match the app window size. Bug: 124510042 Bug: 122843905 Change-Id: Ibc85bfe1d75d01cefe00b6bf32557e04a0ee4716 --- .../WindowTransformSwipeHandler.java | 9 +-- .../quickstep/util/RectFSpringAnim.java | 5 +- .../QuickstepAppTransitionManagerImpl.java | 23 ++++--- .../launcher3/anim/FlingSpringAnim.java | 4 +- .../launcher3/views/FloatingIconView.java | 68 +++++++++---------- 5 files changed, 58 insertions(+), 51 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 49c95f12c9..d927d83068 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -1095,14 +1095,15 @@ public class WindowTransformSwipeHandler homeAnim.setPlayFraction(progress); - float iconAlpha = Utilities.mapToRange(interpolatedProgress, 0, - windowAlphaThreshold, 0f, 1f, Interpolators.LINEAR); - mTransformParams.setCurrentRectAndTargetAlpha(currentRect, 1f - iconAlpha); + float windowAlpha = Utilities.mapToRange(interpolatedProgress, 0, + windowAlphaThreshold, 1f, 0f, Interpolators.LINEAR); + mTransformParams.setProgress(progress) + .setCurrentRectAndTargetAlpha(currentRect, windowAlpha); mClipAnimationHelper.applyTransform(targetSet, mTransformParams, false /* launcherOnTop */); if (isFloatingIconView) { - ((FloatingIconView) floatingView).update(currentRect, iconAlpha, progress, + ((FloatingIconView) floatingView).update(currentRect, 1f, progress, windowAlphaThreshold, mClipAnimationHelper.getCurrentCornerRadius(), false); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java index 40b9c4dcec..09db6952f7 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java @@ -33,6 +33,8 @@ import com.android.launcher3.anim.FlingSpringAnim; import java.util.ArrayList; import java.util.List; +import static com.android.launcher3.anim.Interpolators.DEACCEL; + /** * Applies spring forces to animate from a starting rect to a target rect, * while providing update callbacks to the caller. @@ -45,7 +47,7 @@ public class RectFSpringAnim { * can be done in parallel at a fixed duration. Update callbacks are sent based on the progress * of this animation, while the end callback is sent after all animations finish. */ - private static final long RECT_SCALE_DURATION = 180; + private static final long RECT_SCALE_DURATION = 250; private static final FloatPropertyCompat RECT_CENTER_X = new FloatPropertyCompat("rectCenterXSpring") { @@ -148,6 +150,7 @@ public class RectFSpringAnim { mRectScaleAnim = ObjectAnimator.ofPropertyValuesHolder(this, PropertyValuesHolder.ofFloat(RECT_SCALE_PROGRESS, 1)) .setDuration(RECT_SCALE_DURATION); + mRectScaleAnim.setInterpolator(DEACCEL); mRectScaleAnim.addListener(new AnimationSuccessListener() { @Override public void onAnimationSuccess(Animator animator) { diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java index 3b75304b7c..7dd4df7d69 100644 --- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java @@ -104,16 +104,18 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION = "android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"; - private static final long APP_LAUNCH_DURATION = 500; + private static final long APP_LAUNCH_DURATION = 450; // Use a shorter duration for x or y translation to create a curve effect - private static final long APP_LAUNCH_CURVED_DURATION = APP_LAUNCH_DURATION / 2; + private static final long APP_LAUNCH_CURVED_DURATION = 250; private static final long APP_LAUNCH_ALPHA_DURATION = 50; + private static final long APP_LAUNCH_ALPHA_START_DELAY = 50; // We scale the durations for the downward app launch animations (minus the scale animation). private static final float APP_LAUNCH_DOWN_DUR_SCALE_FACTOR = 0.8f; private static final long APP_LAUNCH_DOWN_DURATION = (long) (APP_LAUNCH_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR); - private static final long APP_LAUNCH_DOWN_CURVED_DURATION = APP_LAUNCH_DOWN_DURATION / 2; + private static final long APP_LAUNCH_DOWN_CURVED_DURATION = + (long) (APP_LAUNCH_CURVED_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR); private static final long APP_LAUNCH_ALPHA_DOWN_DURATION = (long) (APP_LAUNCH_ALPHA_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR); @@ -475,17 +477,18 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans float shapeRevealDuration = APP_LAUNCH_DURATION * SHAPE_PROGRESS_DURATION; final float windowRadius = mDeviceProfile.isMultiWindowMode - ? 0 : getWindowCornerRadius(mLauncher.getResources()); - + ? 0 : getWindowCornerRadius(mLauncher.getResources()); appAnimator.addUpdateListener(new MultiValueUpdateListener() { FloatProp mDx = new FloatProp(0, dX, 0, xDuration, AGGRESSIVE_EASE); FloatProp mDy = new FloatProp(0, dY, 0, yDuration, AGGRESSIVE_EASE); FloatProp mIconScale = new FloatProp(initialStartScale, scale, 0, APP_LAUNCH_DURATION, EXAGGERATED_EASE); - FloatProp mIconAlpha = new FloatProp(1f, 0f, shapeRevealDuration, alphaDuration, - LINEAR); + FloatProp mIconAlpha = new FloatProp(1f, 0f, APP_LAUNCH_ALPHA_START_DELAY, + alphaDuration, LINEAR); FloatProp mCropHeight = new FloatProp(windowTargetBounds.width(), - windowTargetBounds.height(), 0, shapeRevealDuration, AGGRESSIVE_EASE); + windowTargetBounds.height(), 0, APP_LAUNCH_DURATION, EXAGGERATED_EASE); + FloatProp mWindowRadius = new FloatProp(windowTargetBounds.width() / 2f, + windowRadius, 0, APP_LAUNCH_DURATION, EXAGGERATED_EASE); @Override public void onUpdate(float percent) { @@ -518,6 +521,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans float transX0 = temp.left - offsetX; float transY0 = temp.top - offsetY; + float croppedHeight = (windowTargetBounds.height() - crop.height()) * scale; SurfaceParams[] params = new SurfaceParams[targets.length]; for (int i = targets.length - 1; i >= 0; i--) { RemoteAnimationTargetCompat target = targets[i]; @@ -529,8 +533,9 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans matrix.postTranslate(transX0, transY0); targetCrop = crop; alpha = 1f - mIconAlpha.value; - cornerRadius = windowRadius; + cornerRadius = mWindowRadius.value; matrix.mapRect(currentBounds, targetBounds); + currentBounds.bottom -= croppedHeight; mFloatingView.update(currentBounds, mIconAlpha.value, percent, 0f, cornerRadius * scale, true /* isOpening */); } else { diff --git a/src/com/android/launcher3/anim/FlingSpringAnim.java b/src/com/android/launcher3/anim/FlingSpringAnim.java index bb0f855d8c..3d981c136c 100644 --- a/src/com/android/launcher3/anim/FlingSpringAnim.java +++ b/src/com/android/launcher3/anim/FlingSpringAnim.java @@ -30,8 +30,8 @@ public class FlingSpringAnim { private static final float FLING_FRICTION = 1.5f; // Have the spring pull towards the target if we've slowed down too much before reaching it. private static final float FLING_END_THRESHOLD_PX = 50f; - private static final float SPRING_STIFFNESS = 350f; - private static final float SPRING_DAMPING = SpringForce.DAMPING_RATIO_LOW_BOUNCY; + private static final float SPRING_STIFFNESS = 200; + private static final float SPRING_DAMPING = 0.85f; private final FlingAnimation mFlingAnim; private SpringAnimation mSpringAnim; diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 787c93ce97..73f11b22d4 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -98,10 +98,9 @@ public class FloatingIconView extends View implements private RectF mPositionOut; private Runnable mOnTargetChangeRunnable; + private final Rect mOutline = new Rect(); private final Rect mFinalDrawableBounds = new Rect(); private final Rect mBgDrawableBounds = new Rect(); - private float mBgDrawableStartScale = 1f; - private float mBgDrawableEndScale = 1f; private AnimatorSet mFadeAnimatorSet; private ListenerView mListenerView; @@ -143,11 +142,10 @@ public class FloatingIconView extends View implements setTranslationX(dX); setTranslationY(dY); - float scaleX = rect.width() / (float) lp.width; - float scaleY = rect.height() / (float) lp.height; - float scale = mIsAdaptiveIcon && !isOpening ? Math.max(scaleX, scaleY) - : Math.min(scaleX, scaleY); - scale = Math.max(1f, scale); + float minSize = Math.min(lp.width, lp.height); + float scaleX = rect.width() / minSize; + float scaleY = rect.height() / minSize; + float scale = Math.max(1f, Math.min(scaleX, scaleY)); setPivotX(0); setPivotY(0); @@ -160,27 +158,27 @@ public class FloatingIconView extends View implements Math.max(shapeProgressStart, progress), shapeProgressStart, 1f, 0, toMax, LINEAR), 0, 1); - mTaskCornerRadius = cornerRadius; - if (mIsAdaptiveIcon && shapeRevealProgress >= 0) { - if (mRevealAnimator == null) { - mRevealAnimator = (ValueAnimator) IconShape.getShape().createRevealAnimator(this, - mStartRevealRect, mEndRevealRect, mTaskCornerRadius / scale, !isOpening); - mRevealAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mRevealAnimator = null; - } - }); - mRevealAnimator.start(); - // We pause here so we can set the current fraction ourselves. - mRevealAnimator.pause(); + mOutline.bottom = (int) (rect.height() / scale); + mTaskCornerRadius = cornerRadius / scale; + if (mIsAdaptiveIcon) { + if (!isOpening && shapeRevealProgress >= 0) { + if (mRevealAnimator == null) { + mRevealAnimator = (ValueAnimator) IconShape.getShape().createRevealAnimator( + this, mStartRevealRect, mOutline, mTaskCornerRadius, !isOpening); + mRevealAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mRevealAnimator = null; + } + }); + mRevealAnimator.start(); + // We pause here so we can set the current fraction ourselves. + mRevealAnimator.pause(); + } + mRevealAnimator.setCurrentFraction(shapeRevealProgress); } - mRevealAnimator.setCurrentFraction(shapeRevealProgress); - - float bgScale = (mBgDrawableEndScale * shapeRevealProgress) + mBgDrawableStartScale - * (1 - shapeRevealProgress); - setBackgroundDrawableBounds(bgScale); + setBackgroundDrawableBounds(mOutline.height() / minSize); } invalidate(); invalidateOutline(); @@ -363,24 +361,22 @@ public class FloatingIconView extends View implements layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin + lp.height); - Rect rectOutline = new Rect(); float scale = Math.max((float) lp.height / originalHeight, (float) lp.width / originalWidth); + float bgDrawableStartScale; if (isOpening) { - mBgDrawableStartScale = 1f; - mBgDrawableEndScale = scale; - rectOutline.set(0, 0, originalWidth, originalHeight); + bgDrawableStartScale = 1f; + mOutline.set(0, 0, originalWidth, originalHeight); } else { - mBgDrawableStartScale = scale; - mBgDrawableEndScale = 1f; - rectOutline.set(0, 0, lp.width, lp.height); + bgDrawableStartScale = scale; + mOutline.set(0, 0, lp.width, lp.height); } + setBackgroundDrawableBounds(bgDrawableStartScale); mEndRevealRect.set(0, 0, lp.width, lp.height); - setBackgroundDrawableBounds(mBgDrawableStartScale); setOutlineProvider(new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { - outline.setRoundRect(rectOutline, mTaskCornerRadius); + outline.setRoundRect(mOutline, mTaskCornerRadius); } }); setClipToOutline(true); @@ -630,5 +626,7 @@ public class FloatingIconView extends View implements mListenerView.setListener(null); mOriginalIcon = null; mOnTargetChangeRunnable = null; + mTaskCornerRadius = 0; + mOutline.setEmpty(); } } From 47fc4f13169a6e659a78ec234ff11df9a218d84f Mon Sep 17 00:00:00 2001 From: Kevin Date: Fri, 10 May 2019 09:36:56 -0700 Subject: [PATCH 47/72] Only do content fill anim after layout anim The new remote animation may start after the tasks are loaded and we start the content fill animation. This means the layout happens after and the content fill animation will conflict with the layout animation. To fix this, we should only do the content fill animation after the layout animation as intended. If the tasks load before we start the layout animation, we should just directly update to the latest UI without animating as the views won't be visible before the layout animation starts. Bug: 132381412 Test: Manual test Change-Id: Ibb55d8ef4499260ffa4bef5c6bba605ffdc85da7 --- .../quickstep/views/IconRecentsView.java | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index 82760785bd..a53a06e86b 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -153,6 +153,7 @@ public final class IconRecentsView extends FrameLayout implements Insettable { private View mContentView; private boolean mTransitionedFromApp; private boolean mUsingRemoteAnimation; + private boolean mStartedEnterAnimation; private AnimatorSet mLayoutAnimation; private final ArraySet mLayingOutViews = new ArraySet<>(); private Rect mInsets; @@ -298,6 +299,7 @@ public final class IconRecentsView extends FrameLayout implements Insettable { * becomes visible. */ public void onBeginTransitionToOverview() { + mStartedEnterAnimation = false; if (mContext.getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE) { // Scroll to bottom of task in landscape mode. This is a non-issue in portrait mode as // all tasks should be visible to fill up the screen in portrait mode and the view will @@ -322,16 +324,22 @@ public final class IconRecentsView extends FrameLayout implements Insettable { + "of items to animate to."); } // Possible that task list loads faster than adapter changes propagate to layout so - // only start content fill animation if there aren't any pending adapter changes. - if (!mTaskRecyclerView.hasPendingAdapterUpdates()) { + // only start content fill animation if there aren't any pending adapter changes and + // we've started the on enter layout animation. + boolean needsContentFillAnimation = + !mTaskRecyclerView.hasPendingAdapterUpdates() && mStartedEnterAnimation; + if (needsContentFillAnimation) { // Set item animator for content filling animation. The item animator will switch // back to the default on completion mTaskRecyclerView.setItemAnimator(mLoadingContentItemAnimator); + mTaskAdapter.notifyItemRangeRemoved(TASKS_START_POSITION + numActualItems, + numEmptyItems - numActualItems); + mTaskAdapter.notifyItemRangeChanged(TASKS_START_POSITION, numActualItems, + CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT); + } else { + // Notify change without animating. + mTaskAdapter.notifyDataSetChanged(); } - mTaskAdapter.notifyItemRangeRemoved(TASKS_START_POSITION + numActualItems, - numEmptyItems - numActualItems); - mTaskAdapter.notifyItemRangeChanged(TASKS_START_POSITION, numActualItems, - CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT); }); } @@ -622,6 +630,7 @@ public final class IconRecentsView extends FrameLayout implements Insettable { } }); mLayoutAnimation.start(); + mStartedEnterAnimation = true; } /** @@ -647,6 +656,7 @@ public final class IconRecentsView extends FrameLayout implements Insettable { playRemoteAppScaleDownAnim(anim, appMatrix, appTarget, recentsTarget, bottomView.getThumbnailView()); playRemoteTaskListFadeIn(anim, bottomView); + mStartedEnterAnimation = true; } /** From 32f55bf02da11709648cd98ab80469fde64bce10 Mon Sep 17 00:00:00 2001 From: Kevin Date: Fri, 10 May 2019 10:28:07 -0700 Subject: [PATCH 48/72] Set "usingRemoteAnim" for fallback recents Ensure fallback recents uses the correct layout animation by hooking into usingRemoteAnim API. Bug: 132112131 Test: Manual test Change-Id: I9bb96dbf436018e5488ede0c5ca48af787180aec --- .../com/android/quickstep/FallbackActivityControllerHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java index d39a66c707..eb0c5b942f 100644 --- a/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java +++ b/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java @@ -45,12 +45,12 @@ public final class FallbackActivityControllerHelper extends @Override public AnimationFactory prepareRecentsUI(RecentsActivity activity, boolean activityVisible, boolean animateActivity, Consumer callback) { - // TODO: Logic for setting remote animation if (activityVisible) { return (transitionLength) -> { }; } IconRecentsView rv = activity.getOverviewPanel(); + rv.setUsingRemoteAnimation(true); rv.setAlpha(0); return new AnimationFactory() { From 3792c733f804ed4f1909fc1ca3dcc61b6ab1eef8 Mon Sep 17 00:00:00 2001 From: Kevin Date: Fri, 10 May 2019 11:19:37 -0700 Subject: [PATCH 49/72] Fix missing early return. Fix a missing early return for when we don't have the bottom view laid out on remote animation start. Test: Builds Change-Id: Iba0d5097ce59d017dc43030552f2171c5e6e4fb1 --- .../src/com/android/quickstep/views/IconRecentsView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index 82760785bd..e71cda6879 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -641,6 +641,7 @@ public final class IconRecentsView extends FrameLayout implements Insettable { // enough time to take in the data change, bind a new view, and lay out the new view. // TODO: Have a fallback to animate to anim.play(ValueAnimator.ofInt(0, 1).setDuration(REMOTE_APP_TO_OVERVIEW_DURATION)); + return; } final Matrix appMatrix = new Matrix(); playRemoteTransYAnim(anim, appMatrix); From 732becfd664b243dea7507d568654b97efd211a6 Mon Sep 17 00:00:00 2001 From: vadimt Date: Fri, 10 May 2019 12:12:44 -0700 Subject: [PATCH 50/72] Tests: attempt to wait until navigation mode settles after switching Change-Id: If2d14a062275a6cfbc81d1cb7d7ca38da302b51c --- .../tapl/LauncherInstrumentation.java | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 4d8ff1bb2d..0d8d92c887 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -176,17 +176,19 @@ public final class LauncherInstrumentation { // Workaround, use constructed context because both the instrumentation context and the // app context are not constructed with resources that take overlays into account final Context ctx = baseContext.createPackageContext("android", 0); - log("Interaction mode = " + getCurrentInteractionMode(ctx)); - if (isGesturalMode(ctx)) { - return NavigationModel.ZERO_BUTTON; - } else if (isSwipeUpMode(ctx)) { - return NavigationModel.TWO_BUTTON; - } else if (isLegacyMode(ctx)) { - return NavigationModel.THREE_BUTTON; - } else { - fail("Can't detect navigation mode"); + for (int i = 0; i < 100; ++i) { + log("Interaction mode = " + getCurrentInteractionMode(ctx)); + if (isGesturalMode(ctx)) { + return NavigationModel.ZERO_BUTTON; + } else if (isSwipeUpMode(ctx)) { + return NavigationModel.TWO_BUTTON; + } else if (isLegacyMode(ctx)) { + return NavigationModel.THREE_BUTTON; + } + Thread.sleep(100); } - } catch (PackageManager.NameNotFoundException e) { + fail("Can't detect navigation mode"); + } catch (Exception e) { fail(e.toString()); } return NavigationModel.THREE_BUTTON; From b35b7dee18be3f2715e75a2aafe6bfaaa3815f65 Mon Sep 17 00:00:00 2001 From: Kevin Date: Thu, 9 May 2019 09:02:27 -0700 Subject: [PATCH 51/72] Update scrim if insets are added later. If insets are set after the activity requests the foreground scrim to be shown, the scrim will not be correctly added. This is an issue for fallback recents which requests the foreground scrim right after initialization. To fix this, we save the request to show the scrim in a local variable and check this on inset updates to determine whether we should show the scrim or not. Bug: 131853975 Test: Fallback recents now has foreground scrim added correctly Change-Id: I15a19a3184bbc97a20fbcbba2106fd7221410df0 --- .../android/quickstep/views/IconRecentsView.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index a53a06e86b..771c7d7fa7 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -154,6 +154,7 @@ public final class IconRecentsView extends FrameLayout implements Insettable { private boolean mTransitionedFromApp; private boolean mUsingRemoteAnimation; private boolean mStartedEnterAnimation; + private boolean mShowStatusBarForegroundScrim; private AnimatorSet mLayoutAnimation; private final ArraySet mLayingOutViews = new ArraySet<>(); private Rect mInsets; @@ -409,7 +410,14 @@ public final class IconRecentsView extends FrameLayout implements Insettable { * @param showStatusBarForegroundScrim true to show the scrim, false to hide */ public void setShowStatusBarForegroundScrim(boolean showStatusBarForegroundScrim) { - boolean shouldShow = mInsets.top != 0 && showStatusBarForegroundScrim; + mShowStatusBarForegroundScrim = showStatusBarForegroundScrim; + if (mShowStatusBarForegroundScrim != showStatusBarForegroundScrim) { + updateStatusBarScrim(); + } + } + + private void updateStatusBarScrim() { + boolean shouldShow = mInsets.top != 0 && mShowStatusBarForegroundScrim; mActivity.getDragLayer().setForeground(shouldShow ? mStatusBarForegroundScrim : null); } @@ -838,6 +846,9 @@ public final class IconRecentsView extends FrameLayout implements Insettable { mInsets = insets; mTaskRecyclerView.setPadding(insets.left, insets.top, insets.right, insets.bottom); mTaskRecyclerView.invalidateItemDecorations(); + if (mInsets.top != 0) { + updateStatusBarScrim(); + } } /** From 8f1ac1efeb5862dcabc1382755232f74ad207012 Mon Sep 17 00:00:00 2001 From: Kevin Date: Fri, 10 May 2019 12:03:29 -0700 Subject: [PATCH 52/72] Refactor lambda to use listener instead We should not use an in-line lambda here as we allocate a new RemoteAnimationProvider object to do so which hooks into the already existing AppToOverviewAnimationProvider. This can be a problem if AppToOverviewAnimationProvider overrides methods as we would not use the overridden methods but instead the default ones. Instead, we use the same AppToOverviewAnimationProvider object but hook into the activity ready and window animation calls with a listener to add on the logging logic. Bug: 132112131 Test: Manual test; app to overview functions as before Change-Id: Ie70541691ed420c33770d6ed4f54d9555374dc0a --- .../AppToOverviewAnimationProvider.java | 33 ++++++++++ .../quickstep/OverviewCommandHelper.java | 60 +++++++++---------- 2 files changed, 63 insertions(+), 30 deletions(-) diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java index 62e40d1947..c03222db14 100644 --- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java +++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java @@ -42,12 +42,22 @@ final class AppToOverviewAnimationProvider imple private final ActivityControlHelper mHelper; private final int mTargetTaskId; private IconRecentsView mRecentsView; + private AppToOverviewAnimationListener mAnimationReadyListener; AppToOverviewAnimationProvider(ActivityControlHelper helper, int targetTaskId) { mHelper = helper; mTargetTaskId = targetTaskId; } + /** + * Set listener to various points in the animation preparing to animate. + * + * @param listener listener + */ + void setAnimationListener(AppToOverviewAnimationListener listener) { + mAnimationReadyListener = listener; + } + /** * Callback for when the activity is ready/initialized. * @@ -55,6 +65,9 @@ final class AppToOverviewAnimationProvider imple * @param wasVisible true if it was visible before */ boolean onActivityReady(T activity, Boolean wasVisible) { + if (mAnimationReadyListener != null) { + mAnimationReadyListener.onActivityReady(activity); + } ActivityControlHelper.AnimationFactory factory = mHelper.prepareRecentsUI(activity, wasVisible, false /* animate activity */, (controller) -> { @@ -79,6 +92,9 @@ final class AppToOverviewAnimationProvider imple */ @Override public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) { + if (mAnimationReadyListener != null) { + mAnimationReadyListener.onWindowAnimationCreated(); + } AnimatorSet anim = new AnimatorSet(); if (mRecentsView == null) { if (Log.isLoggable(TAG, Log.WARN)) { @@ -131,4 +147,21 @@ final class AppToOverviewAnimationProvider imple long getRecentsLaunchDuration() { return REMOTE_APP_TO_OVERVIEW_DURATION; } + + /** + * Listener for various points in the app to overview animation preparing to animate. + */ + interface AppToOverviewAnimationListener { + /** + * Logic for when activity we're animating to is ready + * + * @param activity activity to animate to + */ + void onActivityReady(BaseDraggingActivity activity); + + /** + * Logic for when we've created the app to recents animation. + */ + void onWindowAnimationCreated(); + } } diff --git a/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java index 379cc100e8..0fa3d866f6 100644 --- a/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java +++ b/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java @@ -18,7 +18,6 @@ package com.android.quickstep; import static com.android.systemui.shared.system.ActivityManagerWrapper .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; -import android.animation.AnimatorSet; import android.annotation.TargetApi; import android.content.Context; import android.os.Build; @@ -30,10 +29,10 @@ import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.quickstep.ActivityControlHelper.ActivityInitListener; +import com.android.quickstep.AppToOverviewAnimationProvider.AppToOverviewAnimationListener; import com.android.quickstep.views.IconRecentsView; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.LatencyTrackerCompat; -import com.android.systemui.shared.system.RemoteAnimationTargetCompat; /** * Helper class to handle various atomic commands for switching between Overview. @@ -105,7 +104,6 @@ public class OverviewCommandHelper { protected final ActivityControlHelper mHelper; private final long mCreateTime; - private final AppToOverviewAnimationProvider mAnimationProvider; private final long mToggleClickedTime = SystemClock.uptimeMillis(); private boolean mUserEventLogged; @@ -114,8 +112,6 @@ public class OverviewCommandHelper { public RecentsActivityCommand() { mHelper = mOverviewComponentObserver.getActivityControlHelper(); mCreateTime = SystemClock.elapsedRealtime(); - mAnimationProvider = - new AppToOverviewAnimationProvider<>(mHelper, RecentsModel.getRunningTaskId()); // Preload the plan mRecentsModel.getTasks(null); @@ -136,11 +132,37 @@ public class OverviewCommandHelper { return; } + AppToOverviewAnimationProvider provider = + new AppToOverviewAnimationProvider<>(mHelper, RecentsModel.getRunningTaskId()); + provider.setAnimationListener( + new AppToOverviewAnimationListener() { + @Override + public void onActivityReady(BaseDraggingActivity activity) { + if (!mUserEventLogged) { + activity.getUserEventDispatcher().logActionCommand( + LauncherLogProto.Action.Command.RECENTS_BUTTON, + mHelper.getContainerType(), + LauncherLogProto.ContainerType.TASKSWITCHER); + mUserEventLogged = true; + } + } + + @Override + public void onWindowAnimationCreated() { + if (LatencyTrackerCompat.isEnabled(mContext)) { + LatencyTrackerCompat.logToggleRecents( + (int) (SystemClock.uptimeMillis() - mToggleClickedTime)); + } + + mListener.unregister(); + } + }); + // Otherwise, start overview. - mListener = mHelper.createActivityInitListener(this::onActivityReady); + mListener = mHelper.createActivityInitListener(provider::onActivityReady); mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(), - this::createWindowAnimation, mContext, mMainThreadExecutor.getHandler(), - mAnimationProvider.getRecentsLaunchDuration()); + provider, mContext, mMainThreadExecutor.getHandler(), + provider.getRecentsLaunchDuration()); } protected boolean handleCommand(long elapsedTime) { @@ -155,27 +177,5 @@ public class OverviewCommandHelper { } return false; } - - private boolean onActivityReady(T activity, Boolean wasVisible) { - if (!mUserEventLogged) { - activity.getUserEventDispatcher().logActionCommand( - LauncherLogProto.Action.Command.RECENTS_BUTTON, - mHelper.getContainerType(), - LauncherLogProto.ContainerType.TASKSWITCHER); - mUserEventLogged = true; - } - return mAnimationProvider.onActivityReady(activity, wasVisible); - } - - private AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) { - if (LatencyTrackerCompat.isEnabled(mContext)) { - LatencyTrackerCompat.logToggleRecents( - (int) (SystemClock.uptimeMillis() - mToggleClickedTime)); - } - - mListener.unregister(); - - return mAnimationProvider.createWindowAnimation(targetCompats); - } } } From d0863ae5c4dbcabc3522c335bc41f7fdb21bf113 Mon Sep 17 00:00:00 2001 From: Kevin Date: Fri, 10 May 2019 13:09:56 -0700 Subject: [PATCH 53/72] Add basic home to recents anim for fallback recents For users/OEMs who ship with a different default launcher, we should provide at least a basic animation from home to recents. This creates a simple cross-fade animation if coming from a separate home activity. Bug: 132449252 Test: Have different default launcher, go to home, go to recents Change-Id: I5ac7b0eedbcdf1e020c31d9cf120887a4bb3826a --- .../AppToOverviewAnimationProvider.java | 7 ++- .../quickstep/views/IconRecentsView.java | 47 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java index c03222db14..fe10a7a21b 100644 --- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java +++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java @@ -17,6 +17,7 @@ package com.android.quickstep; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.quickstep.views.IconRecentsView.REMOTE_APP_TO_OVERVIEW_DURATION; +import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING; @@ -134,7 +135,11 @@ final class AppToOverviewAnimationProvider imple return anim; } - mRecentsView.playRemoteAppToRecentsAnimation(anim, closingAppTarget, recentsTarget); + if (closingAppTarget.activityType == ACTIVITY_TYPE_HOME) { + mRecentsView.playRemoteHomeToRecentsAnimation(anim, closingAppTarget, recentsTarget); + } else { + mRecentsView.playRemoteAppToRecentsAnimation(anim, closingAppTarget, recentsTarget); + } return anim; } diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index 771c7d7fa7..dddaa367c9 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -641,6 +641,53 @@ public final class IconRecentsView extends FrameLayout implements Insettable { mStartedEnterAnimation = true; } + /** + * Play remote app to recents animation when the app is the home activity. We use a simple + * cross-fade here. Note this is only used if the home activity is a separate app than the + * recents activity. + * + * @param anim animator set + * @param homeTarget the home surface thats closing + * @param recentsTarget the surface containing recents + */ + public void playRemoteHomeToRecentsAnimation(@NonNull AnimatorSet anim, + @NonNull RemoteAnimationTargetCompat homeTarget, + @NonNull RemoteAnimationTargetCompat recentsTarget) { + SyncRtSurfaceTransactionApplierCompat surfaceApplier = + new SyncRtSurfaceTransactionApplierCompat(this); + + SurfaceParams[] params = new SurfaceParams[2]; + int boostedMode = MODE_CLOSING; + + ValueAnimator remoteHomeAnim = ValueAnimator.ofFloat(0, 1); + remoteHomeAnim.setDuration(REMOTE_APP_TO_OVERVIEW_DURATION); + + remoteHomeAnim.addUpdateListener(valueAnimator -> { + float val = (float) valueAnimator.getAnimatedValue(); + float alpha; + RemoteAnimationTargetCompat visibleTarget; + RemoteAnimationTargetCompat invisibleTarget; + if (val < .5f) { + visibleTarget = homeTarget; + invisibleTarget = recentsTarget; + alpha = 1 - (val * 2); + } else { + visibleTarget = recentsTarget; + invisibleTarget = homeTarget; + alpha = (val - .5f) * 2; + } + params[0] = new SurfaceParams(visibleTarget.leash, alpha, null /* matrix */, + null /* windowCrop */, getLayer(visibleTarget, boostedMode), + 0 /* cornerRadius */); + params[1] = new SurfaceParams(invisibleTarget.leash, 0.0f, null /* matrix */, + null /* windowCrop */, getLayer(invisibleTarget, boostedMode), + 0 /* cornerRadius */); + surfaceApplier.scheduleApply(params); + }); + anim.play(remoteHomeAnim); + animateFadeInLayoutAnimation(); + } + /** * Play remote animation from app to recents. This should scale the currently closing app down * to the recents thumbnail. From 90b420a618350cc441b15f86415ab2666e00dd73 Mon Sep 17 00:00:00 2001 From: vadimt Date: Fri, 10 May 2019 13:31:07 -0700 Subject: [PATCH 54/72] TAPL: Simplifying BaseOverview Change-Id: I15124a95bf71be78f8e450688d83d4adbad3196f --- .../android/launcher3/tapl/Background.java | 6 ------ .../android/launcher3/tapl/BaseOverview.java | 1 + .../tapl/LauncherInstrumentation.java | 19 ++++++++++++++++--- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java index 60d2850b4f..8f5e7fed0d 100644 --- a/tests/tapl/com/android/launcher3/tapl/Background.java +++ b/tests/tapl/com/android/launcher3/tapl/Background.java @@ -17,16 +17,12 @@ package com.android.launcher3.tapl; import static com.android.launcher3.TestProtocol.BACKGROUND_APP_STATE_ORDINAL; -import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS; -import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName; import android.graphics.Point; import android.os.SystemClock; import android.view.MotionEvent; import androidx.annotation.NonNull; -import androidx.test.uiautomator.By; -import androidx.test.uiautomator.Until; import com.android.launcher3.TestProtocol; @@ -59,8 +55,6 @@ public class Background extends LauncherInstrumentation.VisibleContainer { "want to switch from background to overview")) { verifyActiveContainer(); goToOverviewUnchecked(BACKGROUND_APP_STATE_ORDINAL); - mLauncher.assertTrue("Overview not visible", mLauncher.getDevice().wait( - Until.hasObject(By.pkg(getOverviewPackageName())), WAIT_TIME_MS)); return new BaseOverview(mLauncher); } } diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java index 70d8cf7ab2..b3a369aadc 100644 --- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java +++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java @@ -33,6 +33,7 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { BaseOverview(LauncherInstrumentation launcher) { super(launcher); + verifyActiveContainer(); } @Override diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 4d8ff1bb2d..343bcaff41 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -18,6 +18,7 @@ package com.android.launcher3.tapl; import static com.android.launcher3.TestProtocol.BACKGROUND_APP_STATE_ORDINAL; import static com.android.launcher3.TestProtocol.NORMAL_STATE_ORDINAL; +import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName; import android.app.ActivityManager; import android.app.Instrumentation; @@ -294,14 +295,14 @@ public final class LauncherInstrumentation { } else { waitUntilGone(APPS_RES_ID); } - // Fall through - } - case BASE_OVERVIEW: { waitUntilGone(WORKSPACE_RES_ID); waitUntilGone(WIDGETS_RES_ID); return waitForLauncherObject(OVERVIEW_RES_ID); } + case BASE_OVERVIEW: { + return waitForFallbackLauncherObject(OVERVIEW_RES_ID); + } case BACKGROUND: { waitUntilGone(WORKSPACE_RES_ID); waitUntilGone(APPS_RES_ID); @@ -526,10 +527,22 @@ public final class LauncherInstrumentation { return object; } + @NonNull + UiObject2 waitForFallbackLauncherObject(String resName) { + final BySelector selector = getFallbackLauncherObjectSelector(resName); + final UiObject2 object = mDevice.wait(Until.findObject(selector), WAIT_TIME_MS); + assertNotNull("Can't find a fallback launcher object; selector: " + selector, object); + return object; + } + BySelector getLauncherObjectSelector(String resName) { return By.res(getLauncherPackageName(), resName); } + BySelector getFallbackLauncherObjectSelector(String resName) { + return By.res(getOverviewPackageName(), resName); + } + String getLauncherPackageName() { return mDevice.getLauncherPackageName(); } From 02fb35319e7bb37e4308e0ef71cbd00c179d6055 Mon Sep 17 00:00:00 2001 From: vadimt Date: Fri, 10 May 2019 14:34:16 -0700 Subject: [PATCH 55/72] TAPL: avoiding negative final coordinate of swipe-to-all apps Change-Id: I15c2bf44b7fe2e2ac2fc85db02069441b2aeb807 --- tests/tapl/com/android/launcher3/tapl/Workspace.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index e8a0b54686..c0bafa2e7c 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -58,9 +58,13 @@ public final class Workspace extends Home { verifyActiveContainer(); final UiObject2 hotseat = mHotseat; final Point start = hotseat.getVisibleCenter(); + start.y = hotseat.getVisibleBounds().bottom - 1; final int swipeHeight = mLauncher.getTestInfo( TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT). getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD); + LauncherInstrumentation.log( + "switchToAllApps: swipeHeight = " + swipeHeight + ", slop = " + + mLauncher.getTouchSlop()); mLauncher.swipe( start.x, start.y, From 162e2c5fb0dfc87810334890d9758128ca72f17e Mon Sep 17 00:00:00 2001 From: Kevin Date: Fri, 10 May 2019 16:48:58 -0700 Subject: [PATCH 56/72] Add bottom margin to recents Go again Navigation bar insets are not always transparent and shouldn't be used for margin. So we put the margin back to get a bit more space from the nav bar. Bug: 132461256 Test: Manual test, see margins Change-Id: Id92e8b80a0d2c18c0a603942600366e355649ea8 --- .../src/com/android/quickstep/views/IconRecentsView.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java index 771c7d7fa7..c5e9cf42c0 100644 --- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java +++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java @@ -234,12 +234,8 @@ public final class IconRecentsView extends FrameLayout implements Insettable { case ITEM_TYPE_CLEAR_ALL: outRect.top = (int) res.getDimension( R.dimen.clear_all_item_view_top_margin); - int desiredBottomMargin = (int) res.getDimension( + outRect.bottom = (int) res.getDimension( R.dimen.clear_all_item_view_bottom_margin); - // Only add bottom margin if insets aren't enough. - if (mInsets.bottom < desiredBottomMargin) { - outRect.bottom = desiredBottomMargin - mInsets.bottom; - } break; case ITEM_TYPE_TASK: int desiredTopMargin = (int) res.getDimension( From 426d3650ae0c0b1439fc8ecfcaa090c60058ad7b Mon Sep 17 00:00:00 2001 From: George Hodulik Date: Fri, 10 May 2019 16:44:31 -0700 Subject: [PATCH 57/72] Use new not-deprecated builder constructors for AppTarget Bug: 132458790 Test: Builds now Change-Id: I20109d868d2ddd4434bc7b2e812219ec853161d8 --- .../appprediction/PredictionAppTracker.java | 16 ++++++++-------- .../android/quickstep/AppPredictionsUITests.java | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) 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 0b8c1c57f9..fa2810630c 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 @@ -158,10 +158,10 @@ public class PredictionAppTracker extends AppLaunchTracker { public void onStartShortcut(String packageName, String shortcutId, UserHandle user, String container) { // TODO: Use the full shortcut info - AppTarget target = new AppTarget.Builder(new AppTargetId("shortcut:" + shortcutId)) - .setTarget(packageName, user) - .setClassName(shortcutId) - .build(); + AppTarget target = new AppTarget + .Builder(new AppTargetId("shortcut:" + shortcutId), packageName, user) + .setClassName(shortcutId) + .build(); sendLaunch(target, container); } @@ -169,10 +169,10 @@ public class PredictionAppTracker extends AppLaunchTracker { @UiThread public void onStartApp(ComponentName cn, UserHandle user, String container) { if (cn != null) { - AppTarget target = new AppTarget.Builder(new AppTargetId("app:" + cn)) - .setTarget(cn.getPackageName(), user) - .setClassName(cn.getClassName()) - .build(); + AppTarget target = new AppTarget + .Builder(new AppTargetId("app:" + cn), cn.getPackageName(), user) + .setClassName(cn.getClassName()) + .build(); sendLaunch(target, container); } } diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java index ffe3633353..47ce44c676 100644 --- a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java +++ b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java @@ -149,10 +149,10 @@ public class AppPredictionsUITests extends AbstractQuickStepTest { List targets = new ArrayList<>(activities.length); for (LauncherActivityInfo info : activities) { ComponentName cn = info.getComponentName(); - AppTarget target = new AppTarget.Builder(new AppTargetId("app:" + cn)) - .setTarget(cn.getPackageName(), info.getUser()) - .setClassName(cn.getClassName()) - .build(); + AppTarget target = + new AppTarget.Builder(new AppTargetId("app:" + cn), cn.getPackageName(), info.getUser()) + .setClassName(cn.getClassName()) + .build(); targets.add(target); } mCallback.onTargetsAvailable(targets); From d849ef084c180c393c7f3c69f6f00a01358a644f Mon Sep 17 00:00:00 2001 From: Lucas Dupin Date: Thu, 9 May 2019 14:35:13 -0700 Subject: [PATCH 58/72] Implement scrim method Bug: 130451254 Test: make Change-Id: Ifd66f9e53dd86d21fe66e4617cc4b209fecd7328 (cherry picked from commit 904c429b9998c96476ab5026bc04e7242d559f8d) --- .../src/com/android/quickstep/TouchInteractionService.java | 5 +++++ .../src/com/android/quickstep/TouchInteractionService.java | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java index 39f8448d1f..da4c5e11b2 100644 --- a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -92,6 +92,11 @@ public class TouchInteractionService extends Service { // To be implemented } + @Override + public void onScrimColorsChanged(int color, int type) { + + } + /** Deprecated methods **/ public void onQuickStep(MotionEvent motionEvent) { } 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 128fd45fe5..f38ad0d831 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -176,6 +176,11 @@ public class TouchInteractionService extends Service implements mSystemUiStateFlags = stateFlags; } + @Override + public void onScrimColorsChanged(int color, int type) { + + } + /** Deprecated methods **/ public void onQuickStep(MotionEvent motionEvent) { } From 2aa964732138c3d17d3fcfd8f6d9b96b02f1c103 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Sat, 11 May 2019 07:48:45 +0000 Subject: [PATCH 59/72] Revert "Implement scrim method" This reverts commit d849ef084c180c393c7f3c69f6f00a01358a644f. Reason for revert: not needed I was too eager to merge this CL. It turns out all I need is these three constants and doesn't actually need the aidl change. public static final int MAIN_COLOR_LIGHT = 0xffdadce0; public static final int MAIN_COLOR_DARK = 0xff202124; public static final int MAIN_COLOR_REGULAR = 0xff000000; Change-Id: Iafd539a06455ba0878a6d3750f3c7194f8919fbf --- .../src/com/android/quickstep/TouchInteractionService.java | 5 ----- .../src/com/android/quickstep/TouchInteractionService.java | 5 ----- 2 files changed, 10 deletions(-) diff --git a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java index da4c5e11b2..39f8448d1f 100644 --- a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -92,11 +92,6 @@ public class TouchInteractionService extends Service { // To be implemented } - @Override - public void onScrimColorsChanged(int color, int type) { - - } - /** Deprecated methods **/ public void onQuickStep(MotionEvent motionEvent) { } 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 f38ad0d831..128fd45fe5 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -176,11 +176,6 @@ public class TouchInteractionService extends Service implements mSystemUiStateFlags = stateFlags; } - @Override - public void onScrimColorsChanged(int color, int type) { - - } - /** Deprecated methods **/ public void onQuickStep(MotionEvent motionEvent) { } From 5bbc3b7054c93957d05708f57087d11f60f73833 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Sat, 11 May 2019 20:58:37 -0700 Subject: [PATCH 60/72] Force hide notification dot until fade animation is complete. When the animation is complete, the notification dot animates in. Bug: 123900446 Change-Id: Ie98342f2945ac4e42fb810b4334ef03a734ebef2 --- src/com/android/launcher3/views/FloatingIconView.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java index 73f11b22d4..cd0ae3d720 100644 --- a/src/com/android/launcher3/views/FloatingIconView.java +++ b/src/com/android/launcher3/views/FloatingIconView.java @@ -568,6 +568,17 @@ public class FloatingIconView extends View implements } }); + if (originalView instanceof BubbleTextView) { + BubbleTextView btv = (BubbleTextView) originalView; + btv.forceHideDot(true); + fade.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + btv.forceHideDot(false); + } + }); + } + if (originalView instanceof FolderIcon) { FolderIcon folderIcon = (FolderIcon) originalView; folderIcon.setBackgroundVisible(false); From 64941fd9c6bf5326c6dbc199b16cbe26ccfdeffa Mon Sep 17 00:00:00 2001 From: Perumaal S Date: Sun, 12 May 2019 14:16:37 -0700 Subject: [PATCH 61/72] Add proactive hints container This replaces all other chips containers and causes minimal layout overhead: It is strictly within the task itself and causes zero jank or space issues. Screenshots: https://drive.google.com/corp/drive/folders/1-3ZNCJtWc9_cgmP1nNd8U1yvpCr6GGoq Test: manual Change-Id: I5e22a34c4dc1ac3e73c4d5c913befc2360cb388e --- quickstep/res/layout/task.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml index 1d1c2723fb..d1ef631075 100644 --- a/quickstep/res/layout/task.xml +++ b/quickstep/res/layout/task.xml @@ -61,4 +61,14 @@ android:gravity="center_vertical" /> + + \ No newline at end of file From f941f8493d6e4a31e9ea311e4c8e05b013d6e428 Mon Sep 17 00:00:00 2001 From: Tony Date: Mon, 13 May 2019 11:15:54 -0500 Subject: [PATCH 62/72] Add some floating views that should hide back button Change-Id: I5c8b7803d2ab8347d11786d84128f6f431b4cbfc --- src/com/android/launcher3/AbstractFloatingView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index d6f992f516..65f9d6bc09 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -90,7 +90,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch // Usually we show the back button when a floating view is open. Instead, hide for these types. public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE - | TYPE_SNACKBAR; + | TYPE_SNACKBAR | TYPE_WIDGET_RESIZE_FRAME | TYPE_LISTENER; public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE & ~TYPE_LISTENER; From 7a39b1bc5f544eaebb0e9887362fd96bdd99ca4d Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 2 May 2019 13:39:16 -0700 Subject: [PATCH 63/72] Fixing insets mapping in 3-button and 2-button mode Bug: 131360075 Change-Id: If6e3a4fbb011fc313efeb91686a9d787761862c5 --- .../uioverrides/RecentsUiFactory.java | 6 ++ .../uioverrides/RecentsUiFactory.java | 81 ++++++++++++++++++- .../OtherActivityInputConsumer.java | 6 +- src/com/android/launcher3/Launcher.java | 11 +-- src/com/android/launcher3/Workspace.java | 2 +- .../launcher3/dragndrop/DragLayer.java | 24 ++---- .../launcher3/graphics/RotationMode.java | 31 +++---- .../launcher3/uioverrides/UiFactory.java | 6 ++ 8 files changed, 117 insertions(+), 50 deletions(-) diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java index d9b9686030..b5fefb46d6 100644 --- a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java +++ b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java @@ -21,10 +21,12 @@ import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import android.view.View; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherStateManager.StateHandler; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController; import com.android.launcher3.uioverrides.touchcontrollers.LandscapeStatesTouchController; import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController; @@ -102,4 +104,8 @@ public abstract class RecentsUiFactory { * @param launcher the launcher activity */ public static void onLauncherStateOrResumeChanged(Launcher launcher) {} + + public static RotationMode getRotationMode(DeviceProfile dp) { + return RotationMode.NORMAL; + } } 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 518a2a6851..3a2958d54e 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 @@ -20,6 +20,11 @@ import static android.view.View.VISIBLE; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; 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.graphics.Rect; +import android.view.Gravity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; @@ -28,6 +33,7 @@ import com.android.launcher3.LauncherStateManager.StateHandler; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController; import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController; import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController; @@ -58,12 +64,83 @@ public abstract class RecentsUiFactory { // Scale recents takes before animating in private static final float RECENTS_PREPARE_SCALE = 1.33f; + public static 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 (SysUINavigationMode.getMode(context) == NO_BUTTON) { + out.set(insets); + } else { + out.top = Math.max(insets.top, insets.left); + out.bottom = insets.right; + out.left = insets.bottom; + out.right = 0; + } + } + }; + + public static 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.set(insets); + } 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; + } + }; + + public static RotationMode getRotationMode(DeviceProfile dp) { + return !dp.isVerticalBarLayout() ? RotationMode.NORMAL + : (dp.isSeascape() ? ROTATION_SEASCAPE : ROTATION_LANDSCAPE); + } + public static TouchController[] createTouchControllers(Launcher launcher) { Mode mode = SysUINavigationMode.getMode(launcher); ArrayList list = new ArrayList<>(); list.add(launcher.getDragController()); - if (mode == Mode.NO_BUTTON) { + if (mode == NO_BUTTON) { list.add(new QuickSwitchTouchController(launcher)); list.add(new NavBarToHomeTouchController(launcher)); list.add(new FlingAndHoldTouchController(launcher)); @@ -106,7 +183,7 @@ public abstract class RecentsUiFactory { * @param launcher the launcher activity */ public static void prepareToShowOverview(Launcher launcher) { - if (SysUINavigationMode.getMode(launcher) == Mode.NO_BUTTON) { + if (SysUINavigationMode.getMode(launcher) == NO_BUTTON) { // Overview lives on the side, so doesn't scale in from above. return; } 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 e862fa6c88..35b96ccc1b 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 @@ -22,6 +22,8 @@ import static android.view.MotionEvent.ACTION_POINTER_UP; import static android.view.MotionEvent.ACTION_UP; import static android.view.MotionEvent.INVALID_POINTER_ID; import static com.android.launcher3.Utilities.EDGE_NAV_BAR; +import static com.android.launcher3.uioverrides.RecentsUiFactory.ROTATION_LANDSCAPE; +import static com.android.launcher3.uioverrides.RecentsUiFactory.ROTATION_SEASCAPE; import static com.android.launcher3.util.RaceConditionTracker.ENTER; import static com.android.launcher3.util.RaceConditionTracker.EXIT; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; @@ -171,8 +173,8 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC && !mRecentsViewDispatcher.hasConsumer()) { mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher( isNavBarOnLeft() - ? RotationMode.SEASCAPE - : (isNavBarOnRight() ? RotationMode.LANDSCAPE : RotationMode.NORMAL))); + ? ROTATION_SEASCAPE + : (isNavBarOnRight() ? ROTATION_LANDSCAPE : RotationMode.NORMAL))); } int edgeFlags = ev.getEdgeFlags(); ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 417c5a29a0..823fb6b0a2 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -431,8 +431,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, @Override protected void reapplyUi() { if (FeatureFlags.FAKE_LANDSCAPE_UI.get()) { - mRotationMode = mStableDeviceProfile == null ? RotationMode.NORMAL : - (mDeviceProfile.isSeascape() ? RotationMode.SEASCAPE : RotationMode.LANDSCAPE); + mRotationMode = mStableDeviceProfile == null + ? RotationMode.NORMAL : UiFactory.getRotationMode(mDeviceProfile); } getRootView().dispatchInsets(); getStateManager().reapplyState(true /* cancelCurrentAnimation */); @@ -489,8 +489,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, if (FeatureFlags.FAKE_LANDSCAPE_UI.get() && mDeviceProfile.isVerticalBarLayout() && !mDeviceProfile.isMultiWindowMode) { mStableDeviceProfile = mDeviceProfile.inv.portraitProfile; - mRotationMode = mDeviceProfile.isSeascape() - ? RotationMode.SEASCAPE : RotationMode.LANDSCAPE; + mRotationMode = UiFactory.getRotationMode(mDeviceProfile); } else { mStableDeviceProfile = null; mRotationMode = RotationMode.NORMAL; @@ -503,7 +502,9 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, public void updateInsets(Rect insets) { mDeviceProfile.updateInsets(insets); if (mStableDeviceProfile != null) { - mStableDeviceProfile.updateInsets(insets); + Rect r = mStableDeviceProfile.getInsets(); + mRotationMode.mapInsets(this, insets, r); + mStableDeviceProfile.updateInsets(r); } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 0f4c42d51e..a508ce52bd 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -302,7 +302,7 @@ public class Workspace extends PagedView rotationMode.mapRect(padding, mTempRect); setPadding(mTempRect.left, mTempRect.top, mTempRect.right, mTempRect.bottom); - rotationMode.mapRect(insets, mInsets); + rotationMode.mapRect(stableGrid.getInsets(), mInsets); if (mWorkspaceFadeInAdjacentScreens) { // In landscape mode the page spacing is set to the default. diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index 8de2f57ee3..b35e23ce96 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -638,25 +638,11 @@ public class DragLayer extends BaseDragLayer { final int layoutDirection = getLayoutDirection(); int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); - int horizontalGravity = absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK; - int verticalGravity = absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK; if (child instanceof Transposable) { - if (rotation == RotationMode.SEASCAPE) { - if (horizontalGravity == Gravity.RIGHT) { - horizontalGravity = Gravity.LEFT; - } else if (horizontalGravity == Gravity.LEFT) { - horizontalGravity = Gravity.RIGHT; - } + absoluteGravity = rotation.toNaturalGravity(absoluteGravity); - if (verticalGravity == Gravity.TOP) { - verticalGravity = Gravity.BOTTOM; - } else if (verticalGravity == Gravity.BOTTOM) { - verticalGravity = Gravity.TOP; - } - } - - switch (horizontalGravity) { + switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.CENTER_HORIZONTAL: childTop = (parentHeight - height) / 2 + lp.topMargin - lp.bottomMargin; @@ -669,7 +655,7 @@ public class DragLayer extends BaseDragLayer { childTop = parentHeight - lp.leftMargin - width / 2 - height / 2; } - switch (verticalGravity) { + switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) { case Gravity.CENTER_VERTICAL: childLeft = (parentWidth - width) / 2 + lp.leftMargin - lp.rightMargin; @@ -682,7 +668,7 @@ public class DragLayer extends BaseDragLayer { childLeft = height / 2 - width / 2 + lp.topMargin; } } else { - switch (horizontalGravity) { + switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.CENTER_HORIZONTAL: childLeft = (parentWidth - width) / 2 + lp.leftMargin - lp.rightMargin; @@ -695,7 +681,7 @@ public class DragLayer extends BaseDragLayer { childLeft = lp.leftMargin; } - switch (verticalGravity) { + switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) { case Gravity.TOP: childTop = lp.topMargin; break; diff --git a/src/com/android/launcher3/graphics/RotationMode.java b/src/com/android/launcher3/graphics/RotationMode.java index 1b2cbdb9f4..b06305fc20 100644 --- a/src/com/android/launcher3/graphics/RotationMode.java +++ b/src/com/android/launcher3/graphics/RotationMode.java @@ -15,14 +15,17 @@ */ package com.android.launcher3.graphics; +import android.content.Context; import android.graphics.Rect; public abstract class RotationMode { + public static RotationMode NORMAL = new RotationMode(0) { }; + public final float surfaceRotation; public final boolean isTransposed; - private RotationMode(float surfaceRotation) { + public RotationMode(float surfaceRotation) { this.surfaceRotation = surfaceRotation; isTransposed = surfaceRotation != 0; } @@ -35,25 +38,11 @@ public abstract class RotationMode { out.set(left, top, right, bottom); } - public static RotationMode NORMAL = new RotationMode(0) { }; + public void mapInsets(Context context, Rect insets, Rect out) { + out.set(insets); + } - public static RotationMode 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; - } - }; - - public static RotationMode 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; - } - }; + public int toNaturalGravity(int absoluteGravity) { + return absoluteGravity; + } } diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java index c01b54a6dc..6008d14508 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java @@ -20,10 +20,12 @@ import android.app.Activity; import android.content.Context; import android.os.CancellationSignal; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState.ScaleAndTranslation; import com.android.launcher3.LauncherStateManager.StateHandler; import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.graphics.RotationMode; import com.android.launcher3.util.TouchController; import java.io.PrintWriter; @@ -73,4 +75,8 @@ public class UiFactory { public static ScaleAndTranslation getOverviewScaleAndTranslationForNormalState(Launcher l) { return new ScaleAndTranslation(1.1f, 0f, 0f); } + + public static RotationMode getRotationMode(DeviceProfile dp) { + return RotationMode.NORMAL; + } } From c2e54bc2104655c448e4e8de44c42a9382f59c70 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Fri, 10 May 2019 13:31:51 -0700 Subject: [PATCH 64/72] Use LauncherApps#getAllPackageInstallerSessions to get all sessions. This new API allows us to get the package installer sessions for the main user as well as the managed profile, which will allow us to restore work profile items on the workspace. Bug: 131315856 Change-Id: Id76e815a76d1c715bc160776fd87d75bba92ff93 --- .../launcher3/compat/LauncherAppsCompat.java | 7 +++- .../compat/LauncherAppsCompatVL.java | 6 ++++ .../compat/LauncherAppsCompatVQ.java | 36 +++++++++++++++++++ .../compat/PackageInstallerCompatVL.java | 7 +++- 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 src/com/android/launcher3/compat/LauncherAppsCompatVQ.java diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java index 4275f312b3..58fc73d235 100644 --- a/src/com/android/launcher3/compat/LauncherAppsCompat.java +++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.LauncherActivityInfo; +import android.content.pm.PackageInstaller; import android.content.pm.ShortcutInfo; import android.graphics.Rect; import android.os.Bundle; @@ -56,7 +57,9 @@ public abstract class LauncherAppsCompat { public static LauncherAppsCompat getInstance(Context context) { synchronized (sInstanceLock) { if (sInstance == null) { - if (Utilities.ATLEAST_OREO) { + if (Utilities.ATLEAST_Q) { + sInstance = new LauncherAppsCompatVQ(context.getApplicationContext()); + } else if (Utilities.ATLEAST_OREO) { sInstance = new LauncherAppsCompatVO(context.getApplicationContext()); } else { sInstance = new LauncherAppsCompatVL(context.getApplicationContext()); @@ -83,4 +86,6 @@ public abstract class LauncherAppsCompat { UserHandle user); public abstract List getCustomShortcutActivityList( @Nullable PackageUserKey packageUser); + + public abstract List getAllPackageInstallerSessions(); } diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java index fc48ba7568..1d19b533a0 100644 --- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java +++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java @@ -22,6 +22,7 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; +import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ShortcutInfo; @@ -199,5 +200,10 @@ public class LauncherAppsCompatVL extends LauncherAppsCompat { } return result; } + + @Override + public List getAllPackageInstallerSessions() { + return mContext.getPackageManager().getPackageInstaller().getAllSessions(); + } } diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVQ.java b/src/com/android/launcher3/compat/LauncherAppsCompatVQ.java new file mode 100644 index 0000000000..0a1811e341 --- /dev/null +++ b/src/com/android/launcher3/compat/LauncherAppsCompatVQ.java @@ -0,0 +1,36 @@ +/* + * 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.compat; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInstaller; + +import java.util.List; + +@TargetApi(29) +public class LauncherAppsCompatVQ extends LauncherAppsCompatVO { + + LauncherAppsCompatVQ(Context context) { + super(context); + } + + public List getAllPackageInstallerSessions() { + return mLauncherApps.getAllPackageInstallerSessions(); + } +} diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java index fe7b4e5d0d..a34ca50ebf 100644 --- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java +++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java @@ -27,6 +27,7 @@ import android.os.UserHandle; import android.text.TextUtils; import android.util.SparseArray; +import com.android.launcher3.Utilities; import com.android.launcher3.icons.IconCache; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; @@ -49,6 +50,7 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat { private final Handler mWorker; private final Context mAppContext; private final HashMap mSessionVerifiedMap = new HashMap<>(); + private final LauncherAppsCompat mLauncherApps; PackageInstallerCompatVL(Context context) { mAppContext = context.getApplicationContext(); @@ -56,6 +58,7 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat { mCache = LauncherAppState.getInstance(context).getIconCache(); mWorker = new Handler(LauncherModel.getWorkerLooper()); mInstaller.registerSessionCallback(mCallback, mWorker); + mLauncherApps = LauncherAppsCompat.getInstance(context); } @Override @@ -171,7 +174,9 @@ public class PackageInstallerCompatVL extends PackageInstallerCompat { @Override public List getAllVerifiedSessions() { - List list = new ArrayList<>(mInstaller.getAllSessions()); + List list = new ArrayList<>(Utilities.ATLEAST_Q + ? mLauncherApps.getAllPackageInstallerSessions() + : mInstaller.getAllSessions()); Iterator it = list.iterator(); while (it.hasNext()) { if (verify(it.next()) == null) { From a3c0b0aa68d2d980ed21a76b347da6a7b7537da2 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Fri, 10 May 2019 11:36:51 -0700 Subject: [PATCH 65/72] Swipe up to home animation changes. * Tracks either the top or bottom of the rect for the fling/spring in the Y direction. - This allows us to have the icon always animate downwards. * Scales the spring velocity for the Y component. Bug: 124510042 Change-Id: I47571f906c8aa1beca4dd703d67d013ab6a1161f --- .../res/values/dimens.xml | 4 ++ .../WindowTransformSwipeHandler.java | 3 +- .../quickstep/util/RectFSpringAnim.java | 67 ++++++++++++++----- .../launcher3/anim/FlingSpringAnim.java | 18 ++--- 4 files changed, 66 insertions(+), 26 deletions(-) diff --git a/quickstep/recents_ui_overrides/res/values/dimens.xml b/quickstep/recents_ui_overrides/res/values/dimens.xml index 61c576e82b..b316edd737 100644 --- a/quickstep/recents_ui_overrides/res/values/dimens.xml +++ b/quickstep/recents_ui_overrides/res/values/dimens.xml @@ -23,4 +23,8 @@ 80dp + + + 18dp + 10dp \ No newline at end of file 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 d927d83068..404cfe62ff 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -1077,8 +1077,7 @@ public class WindowTransformSwipeHandler final View floatingView = homeAnimationFactory.getFloatingView(); final boolean isFloatingIconView = floatingView instanceof FloatingIconView; - - RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect); + RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mActivity.getResources()); if (isFloatingIconView) { FloatingIconView fiv = (FloatingIconView) floatingView; anim.addAnimatorListener(fiv); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java index 09db6952f7..3f4ad58ab8 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java @@ -19,6 +19,7 @@ import android.animation.Animator; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.animation.ValueAnimator; +import android.content.res.Resources; import android.graphics.PointF; import android.graphics.RectF; import android.util.FloatProperty; @@ -26,6 +27,7 @@ import android.util.FloatProperty; import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener; import androidx.dynamicanimation.animation.FloatPropertyCompat; +import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.FlingSpringAnim; @@ -63,16 +65,16 @@ public class RectFSpringAnim { } }; - private static final FloatPropertyCompat RECT_CENTER_Y = - new FloatPropertyCompat("rectCenterYSpring") { + private static final FloatPropertyCompat RECT_Y = + new FloatPropertyCompat("rectYSpring") { @Override public float getValue(RectFSpringAnim anim) { - return anim.mCurrentCenterY; + return anim.mCurrentY; } @Override - public void setValue(RectFSpringAnim anim, float currentCenterY) { - anim.mCurrentCenterY = currentCenterY; + public void setValue(RectFSpringAnim anim, float y) { + anim.mCurrentY = y; anim.onUpdate(); } }; @@ -98,7 +100,9 @@ public class RectFSpringAnim { private final List mAnimatorListeners = new ArrayList<>(); private float mCurrentCenterX; - private float mCurrentCenterY; + private float mCurrentY; + // If true, tracking the bottom of the rects, else tracking the top. + private boolean mTrackingBottomY; private float mCurrentScaleProgress; private FlingSpringAnim mRectXAnim; private FlingSpringAnim mRectYAnim; @@ -108,19 +112,32 @@ public class RectFSpringAnim { private boolean mRectYAnimEnded; private boolean mRectScaleAnimEnded; - public RectFSpringAnim(RectF startRect, RectF targetRect) { + private float mMinVisChange; + private float mYOvershoot; + + public RectFSpringAnim(RectF startRect, RectF targetRect, Resources resources) { mStartRect = startRect; mTargetRect = targetRect; mCurrentCenterX = mStartRect.centerX(); - mCurrentCenterY = mStartRect.centerY(); + + mTrackingBottomY = startRect.bottom < targetRect.bottom; + mCurrentY = mTrackingBottomY ? mStartRect.bottom : mStartRect.top; + + mMinVisChange = resources.getDimensionPixelSize(R.dimen.swipe_up_fling_min_visible_change); + mYOvershoot = resources.getDimensionPixelSize(R.dimen.swipe_up_y_overshoot); } public void onTargetPositionChanged() { if (mRectXAnim != null && mRectXAnim.getTargetPosition() != mTargetRect.centerX()) { mRectXAnim.updatePosition(mCurrentCenterX, mTargetRect.centerX()); } - if (mRectYAnim != null && mRectYAnim.getTargetPosition() != mTargetRect.centerY()) { - mRectYAnim.updatePosition(mCurrentCenterY, mTargetRect.centerY()); + + if (mRectYAnim != null) { + if (mTrackingBottomY && mRectYAnim.getTargetPosition() != mTargetRect.bottom) { + mRectYAnim.updatePosition(mCurrentY, mTargetRect.bottom); + } else if (!mTrackingBottomY && mRectYAnim.getTargetPosition() != mTargetRect.top) { + mRectYAnim.updatePosition(mCurrentY, mTargetRect.top); + } } } @@ -142,10 +159,23 @@ public class RectFSpringAnim { mRectYAnimEnded = true; maybeOnEnd(); }); - mRectXAnim = new FlingSpringAnim(this, RECT_CENTER_X, mCurrentCenterX, - mTargetRect.centerX(), velocityPxPerMs.x * 1000, onXEndListener); - mRectYAnim = new FlingSpringAnim(this, RECT_CENTER_Y, mCurrentCenterY, - mTargetRect.centerY(), velocityPxPerMs.y * 1000, onYEndListener); + + float startX = mCurrentCenterX; + float endX = mTargetRect.centerX(); + float minXValue = Math.min(startX, endX); + float maxXValue = Math.max(startX, endX); + mRectXAnim = new FlingSpringAnim(this, RECT_CENTER_X, startX, endX, + velocityPxPerMs.x * 1000, mMinVisChange, minXValue, maxXValue, 1f, onXEndListener); + + float startVelocityY = velocityPxPerMs.y * 1000; + // Scale the Y velocity based on the initial velocity to tune the curves. + float springVelocityFactor = 0.1f + 0.9f * Math.abs(startVelocityY) / 20000.0f; + float startY = mCurrentY; + float endY = mTrackingBottomY ? mTargetRect.bottom : mTargetRect.top; + float minYValue = Math.min(startY, endY - mYOvershoot); + float maxYValue = Math.max(startY, endY); + mRectYAnim = new FlingSpringAnim(this, RECT_Y, startY, endY, startVelocityY, + mMinVisChange, minYValue, maxYValue, springVelocityFactor, onYEndListener); mRectScaleAnim = ObjectAnimator.ofPropertyValuesHolder(this, PropertyValuesHolder.ofFloat(RECT_SCALE_PROGRESS, 1)) @@ -182,8 +212,13 @@ public class RectFSpringAnim { mTargetRect.width()); float currentHeight = Utilities.mapRange(mCurrentScaleProgress, mStartRect.height(), mTargetRect.height()); - mCurrentRect.set(mCurrentCenterX - currentWidth / 2, mCurrentCenterY - currentHeight / 2, - mCurrentCenterX + currentWidth / 2, mCurrentCenterY + currentHeight / 2); + if (mTrackingBottomY) { + mCurrentRect.set(mCurrentCenterX - currentWidth / 2, mCurrentY - currentHeight, + mCurrentCenterX + currentWidth / 2, mCurrentY); + } else { + mCurrentRect.set(mCurrentCenterX - currentWidth / 2, mCurrentY, + mCurrentCenterX + currentWidth / 2, mCurrentY + currentHeight); + } for (OnUpdateListener onUpdateListener : mOnUpdateListeners) { onUpdateListener.onUpdate(mCurrentRect, mCurrentScaleProgress); } diff --git a/src/com/android/launcher3/anim/FlingSpringAnim.java b/src/com/android/launcher3/anim/FlingSpringAnim.java index 3d981c136c..f53ea51509 100644 --- a/src/com/android/launcher3/anim/FlingSpringAnim.java +++ b/src/com/android/launcher3/anim/FlingSpringAnim.java @@ -28,8 +28,6 @@ import androidx.dynamicanimation.animation.SpringForce; public class FlingSpringAnim { private static final float FLING_FRICTION = 1.5f; - // Have the spring pull towards the target if we've slowed down too much before reaching it. - private static final float FLING_END_THRESHOLD_PX = 50f; private static final float SPRING_STIFFNESS = 200; private static final float SPRING_DAMPING = 0.85f; @@ -39,23 +37,27 @@ public class FlingSpringAnim { private float mTargetPosition; public FlingSpringAnim(K object, FloatPropertyCompat property, float startPosition, - float targetPosition, float startVelocity, OnAnimationEndListener onEndListener) { + float targetPosition, float startVelocity, float minVisChange, float minValue, + float maxValue, float springVelocityFactor, OnAnimationEndListener onEndListener) { mFlingAnim = new FlingAnimation(object, property) .setFriction(FLING_FRICTION) - .setMinimumVisibleChange(FLING_END_THRESHOLD_PX) + // Have the spring pull towards the target if we've slowed down too much before + // reaching it. + .setMinimumVisibleChange(minVisChange) .setStartVelocity(startVelocity) - .setMinValue(Math.min(startPosition, targetPosition)) - .setMaxValue(Math.max(startPosition, targetPosition)); + .setMinValue(minValue) + .setMaxValue(maxValue); mTargetPosition = targetPosition; mFlingAnim.addEndListener(((animation, canceled, value, velocity) -> { mSpringAnim = new SpringAnimation(object, property) - .setStartVelocity(velocity) + .setStartValue(value) + .setStartVelocity(velocity * springVelocityFactor) .setSpring(new SpringForce(mTargetPosition) .setStiffness(SPRING_STIFFNESS) .setDampingRatio(SPRING_DAMPING)); mSpringAnim.addEndListener(onEndListener); - mSpringAnim.start(); + mSpringAnim.animateToFinalPosition(mTargetPosition); })); } From 8bab8fa3911e7a6a919e37be70388f3a517ce3f2 Mon Sep 17 00:00:00 2001 From: Kevin Date: Fri, 10 May 2019 11:04:08 -0700 Subject: [PATCH 66/72] Fallback recents should wait for remote anim ready Hook into the remote animation API on the recents view for the fallback recents code path. This ensures the views are attached before attempting the layout animation. Bug: 132112131 Test: Set separate default launcher, go to recents from app, views animate correctly Change-Id: Id73f18e5f864e1970f954c7ec633e25e466d832e --- .../AppToOverviewAnimationProvider.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java index c03222db14..64ee1a9f4a 100644 --- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java +++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java @@ -15,6 +15,7 @@ */ package com.android.quickstep; +import static com.android.launcher3.Utilities.postAsyncCallback; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.quickstep.views.IconRecentsView.REMOTE_APP_TO_OVERVIEW_DURATION; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; @@ -22,12 +23,17 @@ import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MOD import android.animation.AnimatorSet; import android.animation.ValueAnimator; +import android.app.ActivityOptions; +import android.os.Handler; import android.util.Log; import com.android.launcher3.BaseDraggingActivity; +import com.android.launcher3.LauncherAnimationRunner; import com.android.quickstep.util.RemoteAnimationProvider; import com.android.quickstep.util.RemoteAnimationTargetSet; import com.android.quickstep.views.IconRecentsView; +import com.android.systemui.shared.system.ActivityOptionsCompat; +import com.android.systemui.shared.system.RemoteAnimationAdapterCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; /** @@ -139,6 +145,29 @@ final class AppToOverviewAnimationProvider imple return anim; } + @Override + public ActivityOptions toActivityOptions(Handler handler, long duration) { + LauncherAnimationRunner runner = new LauncherAnimationRunner(handler, + false /* startAtFrontOfQueue */) { + + @Override + public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats, + AnimationResult result) { + IconRecentsView recentsView = mRecentsView; + if (!recentsView.isReadyForRemoteAnim()) { + recentsView.setOnReadyForRemoteAnimCallback(() -> postAsyncCallback(handler, + () -> onCreateAnimation(targetCompats, result)) + ); + return; + } + result.setAnimation(createWindowAnimation(targetCompats)); + } + }; + return ActivityOptionsCompat.makeRemoteAnimation( + new RemoteAnimationAdapterCompat(runner, duration, + 0 /* statusBarTransitionDelay */)); + } + /** * Get duration of animation from app to overview. * From 8312866779168e65c7f9fe319016f70d2d567048 Mon Sep 17 00:00:00 2001 From: Tony Date: Mon, 13 May 2019 13:39:07 -0500 Subject: [PATCH 67/72] Use device profile width instead of drag layer width for overview translation This fixes the issue where overview doesn't come in from offscreen the first time you enter overview from home. Test: - Force stop launcher, quick switch from home - Force stop launcher, swipe up and hold Change-Id: Ia4dbd36267008bc199cff088833979606238d3eb --- quickstep/src/com/android/launcher3/uioverrides/UiFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java index 4f50cdb614..77ac35c50a 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java @@ -194,7 +194,7 @@ public class UiFactory extends RecentsUiFactory { public static ScaleAndTranslation getOverviewScaleAndTranslationForNormalState(Launcher l) { if (SysUINavigationMode.getMode(l) == Mode.NO_BUTTON) { - float offscreenTranslationX = l.getDragLayer().getWidth() + float offscreenTranslationX = l.getDeviceProfile().widthPx - l.getOverviewPanel().getPaddingStart(); return new ScaleAndTranslation(1f, offscreenTranslationX, 0f); } From 52aada013d6977c9d6cd43b590a1816c62e5a979 Mon Sep 17 00:00:00 2001 From: Tony Date: Thu, 9 May 2019 18:03:42 -0500 Subject: [PATCH 68/72] Fix fullscreen and landscape task insets Previously, we were incorrectly using the thumbnail insets, which were insufficient for fullscreen tasks, and ignoring rotated tasks entirely. Now we check what part of the thumbnail is actually clipped (e.g. based on the scale we apply to get it to fit in the same width as all the other tasks), and fill that out when setting the fullscreen progress on each task. Test: - Quick switch (from home and from apps) to: - "Normal" apps such as Calculator in portrait and landscape - Immersive portrait apps such as Elder Scrolls: Blades - Immersive landscape apps such as Stardew Valley - Swipe up from an app with the above cases in the adjacent task, ensure the thumbnail gets clipped from fullscreen to its final position in recents - Do the above tests from landscape launcher as well Bug: 130020567 Change-Id: I6f6b7f0ee134d464b3704990db9e1d3a01e6de0b --- .../uioverrides/states/QuickSwitchState.java | 16 +--- .../QuickSwitchTouchController.java | 2 +- .../LauncherActivityControllerHelper.java | 37 +++------ .../quickstep/util/ClipAnimationHelper.java | 17 ++++ .../quickstep/views/TaskThumbnailView.java | 77 ++++++++++------- .../com/android/quickstep/views/TaskView.java | 82 +++++++++++++------ 6 files changed, 137 insertions(+), 94 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java index fa07e27a97..c26a1d0578 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java @@ -17,13 +17,10 @@ package com.android.launcher3.uioverrides.states; import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; -import android.graphics.Rect; - import com.android.launcher3.Launcher; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.quickstep.util.ClipAnimationHelper; import com.android.quickstep.views.RecentsView; -import com.android.quickstep.views.TaskThumbnailView; import com.android.quickstep.views.TaskView; /** @@ -45,18 +42,9 @@ public class QuickSwitchState extends OverviewState { if (recentsView.getTaskViewCount() == 0) { return super.getOverviewScaleAndTranslation(launcher); } - // Compute scale and translation y such that the most recent task view fills the screen. - TaskThumbnailView dummyThumbnail = recentsView.getTaskViewAt(0).getThumbnail(); + TaskView dummyTask = recentsView.getTaskViewAt(0); ClipAnimationHelper clipAnimationHelper = new ClipAnimationHelper(launcher); - clipAnimationHelper.fromTaskThumbnailView(dummyThumbnail, recentsView); - Rect targetRect = new Rect(); - recentsView.getTaskSize(targetRect); - clipAnimationHelper.updateTargetRect(targetRect); - float toScale = clipAnimationHelper.getSourceRect().width() - / clipAnimationHelper.getTargetRect().width(); - float toTranslationY = clipAnimationHelper.getSourceRect().centerY() - - clipAnimationHelper.getTargetRect().centerY(); - return new ScaleAndTranslation(toScale, 0, toTranslationY); + return clipAnimationHelper.getOverviewFullscreenScaleAndTranslation(dummyTask); } @Override 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 3b664b79dd..a1a790c2b6 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 @@ -124,7 +124,7 @@ public class QuickSwitchTouchController extends AbstractStateChangeTouchControll @Override protected void updateProgress(float progress) { super.updateProgress(progress); - updateFullscreenProgress(progress); + updateFullscreenProgress(Utilities.boundToRange(progress, 0, 1)); } private void updateFullscreenProgress(float progress) { 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 e932452edd..c33d25ccf6 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java @@ -301,34 +301,19 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe return; } - // Setup the clip animation helper source/target rects in the final transformed state - // of the recents view (a scale/translationY may be applied prior to this animation - // starting to line up the side pages during swipe up) - float prevRvScale = recentsView.getScaleX(); - float prevRvTransY = recentsView.getTranslationY(); - float targetRvScale = endState.getOverviewScaleAndTranslation(launcher).scale; - SCALE_PROPERTY.set(recentsView, targetRvScale); - recentsView.setTranslationY(0); ClipAnimationHelper clipHelper = new ClipAnimationHelper(launcher); - float tmpCurveScale = v.getCurveScale(); - v.setCurveScale(1f); - clipHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(), null); - v.setCurveScale(tmpCurveScale); - SCALE_PROPERTY.set(recentsView, prevRvScale); - recentsView.setTranslationY(prevRvTransY); + LauncherState.ScaleAndTranslation fromScaleAndTranslation + = clipHelper.getOverviewFullscreenScaleAndTranslation(v); + LauncherState.ScaleAndTranslation endScaleAndTranslation + = endState.getOverviewScaleAndTranslation(launcher); - if (!clipHelper.getSourceRect().isEmpty() && !clipHelper.getTargetRect().isEmpty()) { - float fromScale = clipHelper.getSourceRect().width() - / clipHelper.getTargetRect().width(); - float fromTranslationY = clipHelper.getSourceRect().centerY() - - clipHelper.getTargetRect().centerY(); - Animator scale = ObjectAnimator.ofFloat(recentsView, SCALE_PROPERTY, fromScale, 1); - Animator translateY = ObjectAnimator.ofFloat(recentsView, TRANSLATION_Y, - fromTranslationY, 0); - scale.setInterpolator(LINEAR); - translateY.setInterpolator(LINEAR); - anim.playTogether(scale, translateY); - } + Animator scale = ObjectAnimator.ofFloat(recentsView, SCALE_PROPERTY, + fromScaleAndTranslation.scale, endScaleAndTranslation.scale); + Animator translateY = ObjectAnimator.ofFloat(recentsView, TRANSLATION_Y, + fromScaleAndTranslation.translationY, endScaleAndTranslation.translationY); + scale.setInterpolator(LINEAR); + translateY.setInterpolator(LINEAR); + anim.playTogether(scale, translateY); } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java index 3109921fbb..a650113d55 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java @@ -35,12 +35,14 @@ import androidx.annotation.Nullable; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.LauncherState; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.views.BaseDragLayer; import com.android.quickstep.RecentsModel; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskThumbnailView; +import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.recents.utilities.RectFEvaluator; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; @@ -280,6 +282,21 @@ public class ClipAnimationHelper { } } + /** + * Compute scale and translation y such that the specified task view fills the screen. + */ + public LauncherState.ScaleAndTranslation getOverviewFullscreenScaleAndTranslation(TaskView v) { + TaskThumbnailView thumbnailView = v.getThumbnail(); + RecentsView recentsView = v.getRecentsView(); + fromTaskThumbnailView(thumbnailView, recentsView); + Rect taskSize = new Rect(); + recentsView.getTaskSize(taskSize); + updateTargetRect(taskSize); + float scale = mSourceRect.width() / mTargetRect.width(); + float translationY = mSourceRect.centerY() - mSourceRect.top - mTargetRect.centerY(); + return new LauncherState.ScaleAndTranslation(scale, 0, translationY); + } + private void updateStackBoundsToMultiWindowTaskSize(BaseDraggingActivity activity) { ISystemUiProxy sysUiProxy = RecentsModel.INSTANCE.get(activity).getSystemUiProxy(); if (sysUiProxy != null) { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java index 7e15d523d3..a9184ecefb 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java @@ -33,6 +33,7 @@ import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.Shader; import android.util.AttributeSet; import android.util.FloatProperty; @@ -59,7 +60,7 @@ public class TaskThumbnailView extends View { private final static ColorMatrix COLOR_MATRIX = new ColorMatrix(); private final static ColorMatrix SATURATION_COLOR_MATRIX = new ColorMatrix(); - private final static Rect EMPTY_RECT = new Rect(); + private final static RectF EMPTY_RECT_F = new RectF(); public static final Property DIM_ALPHA = new FloatProperty("dimAlpha") { @@ -87,10 +88,9 @@ public class TaskThumbnailView extends View { private final Matrix mMatrix = new Matrix(); private float mClipBottom = -1; - private Rect mScaledInsets = new Rect(); - private Rect mCurrentDrawnInsets = new Rect(); - private float mCurrentDrawnCornerRadius; - private boolean mIsRotated; + // Contains the portion of the thumbnail that is clipped when fullscreen progress = 0. + private RectF mClippedInsets = new RectF(); + private TaskView.FullscreenDrawParams mFullscreenParams; private Task mTask; private ThumbnailData mThumbnailData; @@ -118,7 +118,7 @@ public class TaskThumbnailView extends View { mDimmingPaintAfterClearing.setColor(Color.BLACK); mActivity = BaseActivity.fromContext(context); mIsDarkTextTheme = Themes.getAttrBoolean(mActivity, R.attr.isWorkspaceDarkText); - setCurrentDrawnInsetsAndRadius(EMPTY_RECT, mCornerRadius); + mFullscreenParams = new TaskView.FullscreenDrawParams(mCornerRadius); } public void bind(Task task) { @@ -201,23 +201,27 @@ public class TaskThumbnailView extends View { @Override protected void onDraw(Canvas canvas) { + RectF currentDrawnInsets = mFullscreenParams.mCurrentDrawnInsets; + canvas.save(); + canvas.translate(currentDrawnInsets.left, currentDrawnInsets.top); + canvas.scale(mFullscreenParams.mScale, mFullscreenParams.mScale); // Draw the insets if we're being drawn fullscreen (we do this for quick switch). drawOnCanvas(canvas, - -mCurrentDrawnInsets.left, - -mCurrentDrawnInsets.top, - getMeasuredWidth() + mCurrentDrawnInsets.right, - getMeasuredHeight() + mCurrentDrawnInsets.bottom, - mCurrentDrawnCornerRadius); + -currentDrawnInsets.left, + -currentDrawnInsets.top, + getMeasuredWidth() + currentDrawnInsets.right, + getMeasuredHeight() + currentDrawnInsets.bottom, + mFullscreenParams.mCurrentDrawnCornerRadius); + canvas.restore(); } - public Rect getInsetsToDrawInFullscreen(boolean isMultiWindowMode) { - // Don't show insets in the wrong orientation or in multi window mode. - return mIsRotated || isMultiWindowMode ? EMPTY_RECT : mScaledInsets; + public RectF getInsetsToDrawInFullscreen(boolean isMultiWindowMode) { + // Don't show insets in multi window mode. + return isMultiWindowMode ? EMPTY_RECT_F : mClippedInsets; } - public void setCurrentDrawnInsetsAndRadius(Rect insets, float radius) { - mCurrentDrawnInsets.set(insets); - mCurrentDrawnCornerRadius = radius; + public void setFullscreenParams(TaskView.FullscreenDrawParams fullscreenParams) { + mFullscreenParams = fullscreenParams; invalidate(); } @@ -275,7 +279,7 @@ public class TaskThumbnailView extends View { } private void updateThumbnailMatrix() { - mIsRotated = false; + boolean isRotated = false; mClipBottom = -1; if (mBitmapShader != null && mThumbnailData != null) { float scale = mThumbnailData.scale; @@ -296,30 +300,28 @@ public class TaskThumbnailView extends View { final Configuration configuration = getContext().getResources().getConfiguration(); // Rotate the screenshot if not in multi-window mode - mIsRotated = FeatureFlags.OVERVIEW_USE_SCREENSHOT_ORIENTATION && + isRotated = FeatureFlags.OVERVIEW_USE_SCREENSHOT_ORIENTATION && configuration.orientation != mThumbnailData.orientation && !mActivity.isInMultiWindowMode() && mThumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN; // Scale the screenshot to always fit the width of the card. - thumbnailScale = mIsRotated + thumbnailScale = isRotated ? getMeasuredWidth() / thumbnailHeight : getMeasuredWidth() / thumbnailWidth; } - mScaledInsets.set(thumbnailInsets); - Utilities.scaleRect(mScaledInsets, thumbnailScale); - - if (mIsRotated) { + if (isRotated) { int rotationDir = profile.isVerticalBarLayout() && !profile.isSeascape() ? -1 : 1; mMatrix.setRotate(90 * rotationDir); int newLeftInset = rotationDir == 1 ? thumbnailInsets.bottom : thumbnailInsets.top; int newTopInset = rotationDir == 1 ? thumbnailInsets.left : thumbnailInsets.right; - mMatrix.postTranslate(-newLeftInset * scale, -newTopInset * scale); + mClippedInsets.offsetTo(newLeftInset * scale, newTopInset * scale); if (rotationDir == -1) { // Crop the right/bottom side of the screenshot rather than left/top float excessHeight = thumbnailWidth * thumbnailScale - getMeasuredHeight(); - mMatrix.postTranslate(0, -excessHeight); + mClippedInsets.offset(0, excessHeight); } + mMatrix.postTranslate(-mClippedInsets.left, -mClippedInsets.top); // Move the screenshot to the thumbnail window (rotation moved it out). if (rotationDir == 1) { mMatrix.postTranslate(mThumbnailData.thumbnail.getHeight(), 0); @@ -327,13 +329,28 @@ public class TaskThumbnailView extends View { mMatrix.postTranslate(0, mThumbnailData.thumbnail.getWidth()); } } else { - mMatrix.setTranslate(-mThumbnailData.insets.left * scale, - -mThumbnailData.insets.top * scale); + mClippedInsets.offsetTo(thumbnailInsets.left * scale, thumbnailInsets.top * scale); + mMatrix.setTranslate(-mClippedInsets.left, -mClippedInsets.top); } + + final float widthWithInsets; + final float heightWithInsets; + if (isRotated) { + widthWithInsets = mThumbnailData.thumbnail.getHeight() * thumbnailScale; + heightWithInsets = mThumbnailData.thumbnail.getWidth() * thumbnailScale; + } else { + widthWithInsets = mThumbnailData.thumbnail.getWidth() * thumbnailScale; + heightWithInsets = mThumbnailData.thumbnail.getHeight() * thumbnailScale; + } + mClippedInsets.left *= thumbnailScale; + mClippedInsets.top *= thumbnailScale; + mClippedInsets.right = widthWithInsets - mClippedInsets.left - getMeasuredWidth(); + mClippedInsets.bottom = heightWithInsets - mClippedInsets.top - getMeasuredHeight(); + mMatrix.postScale(thumbnailScale, thumbnailScale); mBitmapShader.setLocalMatrix(mMatrix); - float bitmapHeight = Math.max((mIsRotated ? thumbnailWidth : thumbnailHeight) + float bitmapHeight = Math.max((isRotated ? thumbnailWidth : thumbnailHeight) * thumbnailScale, 0); if (Math.round(bitmapHeight) < getMeasuredHeight()) { mClipBottom = bitmapHeight; @@ -341,7 +358,7 @@ public class TaskThumbnailView extends View { mPaint.setShader(mBitmapShader); } - if (mIsRotated) { + if (isRotated) { // The overlay doesn't really work when the screenshot is rotated, so don't add it. mOverlay.reset(); } else { 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..2b86f5e109 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 @@ -32,6 +32,7 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Outline; import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; @@ -166,7 +167,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { private float mCurveScale; private float mZoomScale; private float mFullscreenProgress; - private final Rect mCurrentDrawnInsets = new Rect(); + private final FullscreenDrawParams mCurrentFullscreenParams; private final float mCornerRadius; private final float mWindowCornerRadius; private final BaseDraggingActivity mActivity; @@ -214,7 +215,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { }); mCornerRadius = TaskCornerRadius.get(context); mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context.getResources()); - mOutlineProvider = new TaskOutlineProvider(getResources(), mCornerRadius); + mCurrentFullscreenParams = new FullscreenDrawParams(mCornerRadius); + mOutlineProvider = new TaskOutlineProvider(getResources(), mCurrentFullscreenParams); setOutlineProvider(mOutlineProvider); } @@ -540,26 +542,26 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { private static final class TaskOutlineProvider extends ViewOutlineProvider { private final int mMarginTop; - private final Rect mInsets = new Rect(); - private float mRadius; + private FullscreenDrawParams mFullscreenParams; - TaskOutlineProvider(Resources res, float radius) { + TaskOutlineProvider(Resources res, FullscreenDrawParams fullscreenParams) { mMarginTop = res.getDimensionPixelSize(R.dimen.task_thumbnail_top_margin); - mRadius = radius; + mFullscreenParams = fullscreenParams; } - public void setCurrentDrawnInsetsAndRadius(Rect insets, float radius) { - mInsets.set(insets); - mRadius = radius; + public void setFullscreenParams(FullscreenDrawParams params) { + mFullscreenParams = params; } @Override public void getOutline(View view, Outline outline) { - outline.setRoundRect(-mInsets.left, - mMarginTop - mInsets.top, - view.getWidth() + mInsets.right, - view.getHeight() + mInsets.bottom, - mRadius); + RectF insets = mFullscreenParams.mCurrentDrawnInsets; + float scale = mFullscreenParams.mScale; + outline.setRoundRect(0, + (int) (mMarginTop * scale), + (int) ((insets.left + view.getWidth() + insets.right) * scale), + (int) ((insets.top + view.getHeight() + insets.bottom) * scale), + mFullscreenParams.mCurrentDrawnCornerRadius); } } @@ -658,17 +660,25 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { TaskThumbnailView thumbnail = getThumbnail(); boolean isMultiWindowMode = mActivity.getDeviceProfile().isMultiWindowMode; - Rect insets = thumbnail.getInsetsToDrawInFullscreen(isMultiWindowMode); - mCurrentDrawnInsets.set((int) (insets.left * mFullscreenProgress), - (int) (insets.top * mFullscreenProgress), - (int) (insets.right * mFullscreenProgress), - (int) (insets.bottom * mFullscreenProgress)); + RectF insets = thumbnail.getInsetsToDrawInFullscreen(isMultiWindowMode); + float currentInsetsLeft = insets.left * mFullscreenProgress; + float currentInsetsRight = insets.right * mFullscreenProgress; + mCurrentFullscreenParams.setInsets(currentInsetsLeft, + insets.top * mFullscreenProgress, + currentInsetsRight, + insets.bottom * mFullscreenProgress); float fullscreenCornerRadius = isMultiWindowMode ? 0 : mWindowCornerRadius; - float cornerRadius = Utilities.mapRange(mFullscreenProgress, mCornerRadius, - fullscreenCornerRadius) / getRecentsView().getScaleX(); + mCurrentFullscreenParams.setCornerRadius(Utilities.mapRange(mFullscreenProgress, + mCornerRadius, fullscreenCornerRadius) / getRecentsView().getScaleX()); + // We scaled the thumbnail to fit the content (excluding insets) within task view width. + // Now that we are drawing left/right insets again, we need to scale down to fit them. + if (getWidth() > 0) { + mCurrentFullscreenParams.setScale(getWidth() + / (getWidth() + currentInsetsLeft + currentInsetsRight)); + } - thumbnail.setCurrentDrawnInsetsAndRadius(mCurrentDrawnInsets, cornerRadius); - mOutlineProvider.setCurrentDrawnInsetsAndRadius(mCurrentDrawnInsets, cornerRadius); + thumbnail.setFullscreenParams(mCurrentFullscreenParams); + mOutlineProvider.setFullscreenParams(mCurrentFullscreenParams); invalidateOutline(); } @@ -686,4 +696,30 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { } return mShowScreenshot; } + + /** + * We update and subsequently draw these in {@link #setFullscreenProgress(float)}. + */ + static class FullscreenDrawParams { + RectF mCurrentDrawnInsets = new RectF(); + float mCurrentDrawnCornerRadius; + /** The current scale we apply to the thumbnail to adjust for new left/right insets. */ + float mScale = 1; + + public FullscreenDrawParams(float cornerRadius) { + setCornerRadius(cornerRadius); + } + + public void setInsets(float left, float top, float right, float bottom) { + mCurrentDrawnInsets.set(left, top, right, bottom); + } + + public void setCornerRadius(float cornerRadius) { + mCurrentDrawnCornerRadius = cornerRadius; + } + + public void setScale(float scale) { + mScale = scale; + } + } } From d78e47b8f8c89e7329b23605c02a95a4aac70eba Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Mon, 13 May 2019 15:01:13 -0700 Subject: [PATCH 69/72] HeadLine text font family should not be Medium type Bug: 128986173 Change-Id: Icbc3292b636e8b664b68c62590e66d261843672f --- res/values-v28/styles.xml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 res/values-v28/styles.xml diff --git a/res/values-v28/styles.xml b/res/values-v28/styles.xml new file mode 100644 index 0000000000..7df9ce541f --- /dev/null +++ b/res/values-v28/styles.xml @@ -0,0 +1,23 @@ + + + + + From 0edb04ca248da66e3013690be6fd73f9d472d3a4 Mon Sep 17 00:00:00 2001 From: vadimt Date: Mon, 13 May 2019 18:28:19 -0700 Subject: [PATCH 70/72] Fixing flakiness of widget tests Change-Id: I1af6a733a3a7a89899c83d6714117ff9a51528d2 --- tests/src/com/android/launcher3/ui/TestViewHelpers.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/src/com/android/launcher3/ui/TestViewHelpers.java b/tests/src/com/android/launcher3/ui/TestViewHelpers.java index 2116f5e2d5..a73bde0116 100644 --- a/tests/src/com/android/launcher3/ui/TestViewHelpers.java +++ b/tests/src/com/android/launcher3/ui/TestViewHelpers.java @@ -27,6 +27,7 @@ import android.graphics.Point; import android.os.Process; import android.os.SystemClock; import android.util.Log; +import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; @@ -186,10 +187,8 @@ public class TestViewHelpers { */ public static UiObject2 openWidgetsTray() { final UiDevice device = getDevice(); - device.pressMenu(); // Enter overview mode. - device.wait(Until.findObject( - By.text(getTargetContext().getString(R.string.widget_button_text))), - AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT).click(); + device.pressKeyCode(KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON); + device.waitForIdle(); return findViewById(R.id.widgets_list_view); } From e751d90c449b388266f8ccf637e090a44b4f31cd Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Mon, 13 May 2019 23:23:05 -0700 Subject: [PATCH 71/72] Implement 3 shades of folder icon/container fill color Bug: 130451254 Change-Id: I36d885a2b0247f1bb84cb98073459853ae723331 --- .../uioverrides/WallpaperColorInfo.java | 8 +++++++ res/drawable-v28/round_rect_folder.xml | 21 ++++++++++++++++ res/drawable/round_rect_folder.xml | 21 ++++++++++++++++ res/layout/folder_application.xml | 1 + res/layout/user_folder_icon_normalized.xml | 2 +- res/values/attrs.xml | 4 +++- res/values/styles.xml | 24 ++++++++++++++++++- .../folder/FolderAnimationManager.java | 2 +- .../launcher3/folder/PreviewBackground.java | 2 +- src/com/android/launcher3/util/Themes.java | 6 +++-- .../uioverrides/WallpaperColorInfo.java | 8 +++++++ 11 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 res/drawable-v28/round_rect_folder.xml create mode 100644 res/drawable/round_rect_folder.xml diff --git a/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java b/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java index 8218517dcc..711e59a33e 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java +++ b/quickstep/src/com/android/launcher3/uioverrides/WallpaperColorInfo.java @@ -35,6 +35,10 @@ import java.util.ArrayList; @TargetApi(Build.VERSION_CODES.P) public class WallpaperColorInfo implements OnColorsChangedListener { + private static final int MAIN_COLOR_LIGHT = 0xffdadce0; + private static final int MAIN_COLOR_DARK = 0xff202124; + private static final int MAIN_COLOR_REGULAR = 0xff000000; + private static final Object sInstanceLock = new Object(); private static WallpaperColorInfo sInstance; @@ -79,6 +83,10 @@ public class WallpaperColorInfo implements OnColorsChangedListener { return mExtractionInfo.supportsDarkText; } + public boolean isMainColorDark() { + return mExtractionInfo.mainColor == MAIN_COLOR_DARK; + } + @Override public void onColorsChanged(WallpaperColors colors, int which) { if ((which & FLAG_SYSTEM) != 0) { diff --git a/res/drawable-v28/round_rect_folder.xml b/res/drawable-v28/round_rect_folder.xml new file mode 100644 index 0000000000..0403be09b0 --- /dev/null +++ b/res/drawable-v28/round_rect_folder.xml @@ -0,0 +1,21 @@ + + + + + + diff --git a/res/drawable/round_rect_folder.xml b/res/drawable/round_rect_folder.xml new file mode 100644 index 0000000000..8b3d06ca9b --- /dev/null +++ b/res/drawable/round_rect_folder.xml @@ -0,0 +1,21 @@ + + + + + + diff --git a/res/layout/folder_application.xml b/res/layout/folder_application.xml index de861a0ad4..c156e113fb 100644 --- a/res/layout/folder_application.xml +++ b/res/layout/folder_application.xml @@ -18,5 +18,6 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:launcher="http://schemas.android.com/apk/res-auto" style="@style/BaseIcon" + android:textColor="?attr/folderTextColor" android:includeFontPadding="false" launcher:iconDisplay="folder" /> diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml index 2e6ce946f8..835fee2d3d 100644 --- a/res/layout/user_folder_icon_normalized.xml +++ b/res/layout/user_folder_icon_normalized.xml @@ -18,7 +18,7 @@ xmlns:launcher="http://schemas.android.com/apk/res-auto" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:background="@drawable/round_rect_primary" + android:background="@drawable/round_rect_folder" android:elevation="5dp" android:orientation="vertical" > diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 43194d5cb6..69b8c8a228 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -36,8 +36,10 @@ + + @@ -55,7 +57,7 @@ - + diff --git a/res/values/styles.xml b/res/values/styles.xml index 7932c6d608..8116e30ef1 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -26,6 +26,7 @@ @android:color/transparent true true + ?attr/workspaceTextColor + + + + - +