Fix folder morphing animation
- Also splits interpolator between open / closing anim to setup future folder motion work
- Also fixed bug if no shape selected in picker
Bug: 393979768
Bug: 387543793
Test: Manual verify animation and picker bug
Flag: com.android.launcher3.enable_launcher_icon_shapes
Change-Id: Ieb7355b9128cd1ded46af6787f5c14baf9be6774
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index d2ff2cb..2157610 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -35,6 +35,7 @@
import android.util.Property;
import android.view.View;
import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
@@ -79,9 +80,10 @@
private final int mDuration;
private final int mDelay;
- private final TimeInterpolator mFolderInterpolator;
- private final TimeInterpolator mLargeFolderPreviewItemOpenInterpolator;
- private final TimeInterpolator mLargeFolderPreviewItemCloseInterpolator;
+ private final Interpolator mFolderOpenInterpolator;
+ private final Interpolator mFolderCloseInterpolator;
+ private final Interpolator mLargeFolderPreviewItemOpenInterpolator;
+ private final Interpolator mLargeFolderPreviewItemCloseInterpolator;
private final PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0);
private final FolderGridOrganizer mPreviewVerifier;
@@ -108,7 +110,9 @@
mDuration = res.getInteger(R.integer.config_materialFolderExpandDuration);
mDelay = res.getInteger(R.integer.config_folderDelay);
- mFolderInterpolator = AnimationUtils.loadInterpolator(mContext,
+ mFolderOpenInterpolator = AnimationUtils.loadInterpolator(mContext,
+ R.interpolator.standard_interpolator);
+ mFolderCloseInterpolator = AnimationUtils.loadInterpolator(mContext,
R.interpolator.standard_interpolator);
mLargeFolderPreviewItemOpenInterpolator = AnimationUtils.loadInterpolator(mContext,
R.interpolator.large_folder_preview_item_open_interpolator);
@@ -252,6 +256,7 @@
(int) (left + (startRect.right / initialScale)) + extraRadius,
(int) (startRect.bottom / initialScale) + extraRadius);
Rect contentEnd = new Rect(left, 0, left + lp.width, lp.height);
+ // animated contents of folder with the folder background
play(a, shapeDelegate.createRevealAnimator(
mFolder.getContent(), contentStart, contentEnd, finalRadius, !mIsOpening));
@@ -332,7 +337,11 @@
// We set the interpolator on all current child animators here, because the preview item
// animators may use a different interpolator.
for (Animator animator : a.getChildAnimations()) {
- animator.setInterpolator(mFolderInterpolator);
+ animator.setInterpolator(
+ mIsOpening
+ ? mFolderOpenInterpolator
+ : mFolderCloseInterpolator
+ );
}
int radiusDiff = scaledRadius - mPreviewBackground.getRadius();
@@ -467,7 +476,7 @@
return mFolder.getItemCount() > MAX_NUM_ITEMS_IN_PREVIEW;
}
- private TimeInterpolator getPreviewItemInterpolator() {
+ private Interpolator getPreviewItemInterpolator() {
if (isLargeFolder()) {
// With larger folders, we want the preview items to reach their final positions faster
// (when opening) and later (when closing) so that they appear aligned with the rest of
@@ -476,7 +485,7 @@
? mLargeFolderPreviewItemOpenInterpolator
: mLargeFolderPreviewItemCloseInterpolator;
}
- return mFolderInterpolator;
+ return mIsOpening ? mFolderOpenInterpolator : mFolderCloseInterpolator;
}
private Animator getAnimator(View view, Property property, float v1, float v2) {
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index 9edc386..80743af 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -55,6 +55,7 @@
import java.lang.ref.WeakReference;
import java.util.Collections;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
@@ -148,13 +149,23 @@
KEY_SHAPE_KEY, KEY_SHAPE_TITLE, KEY_PATH, KEY_IS_DEFAULT});
String currentShapePath =
ThemeManager.INSTANCE.get(context).getIconState().getIconMask();
+ Optional<IconShapeModel> selectedShape = ShapesProvider.INSTANCE.getIconShapes()
+ .values()
+ .stream()
+ .filter(shape -> shape.getPathString().equals(currentShapePath))
+ .findFirst();
+ // Handle default for when current shape doesn't match new shapes.
+ if (selectedShape.isEmpty()) {
+ selectedShape = Optional.ofNullable(ShapesProvider.INSTANCE.getIconShapes()
+ .get("circle"));
+ }
+
for (IconShapeModel shape : ShapesProvider.INSTANCE.getIconShapes().values()) {
cursor.newRow()
.add(KEY_SHAPE_KEY, shape.getKey())
.add(KEY_SHAPE_TITLE, shape.getTitle())
.add(KEY_PATH, shape.getPathString())
- .add(KEY_IS_DEFAULT,
- shape.getPathString().equals(currentShapePath));
+ .add(KEY_IS_DEFAULT, shape.equals(selectedShape.get()));
}
return cursor;
} else {
diff --git a/src/com/android/launcher3/graphics/IconShape.kt b/src/com/android/launcher3/graphics/IconShape.kt
index e62936c..eac3440 100644
--- a/src/com/android/launcher3/graphics/IconShape.kt
+++ b/src/com/android/launcher3/graphics/IconShape.kt
@@ -38,6 +38,7 @@
import androidx.graphics.shapes.Morph
import androidx.graphics.shapes.RoundedPolygon
import androidx.graphics.shapes.SvgPathParser
+import androidx.graphics.shapes.rectangle
import androidx.graphics.shapes.toPath
import androidx.graphics.shapes.transformed
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider
@@ -362,8 +363,8 @@
}
/**
- * Creates a rounded rect with the start point at the center of the top edge. This ensures a
- * better animation since our shape paths also start at top-center of the bounding box.
+ * Create RoundedRect using RoundedPolygon API. Ensures smoother animation morphing between
+ * generic polygon by using [RoundedPolygon.Companion.rectangle] directly.
*/
fun createRoundedRect(
left: Float,
@@ -372,27 +373,11 @@
bottom: Float,
cornerR: Float,
) =
- RoundedPolygon(
- vertices =
- floatArrayOf(
- // x1, y1
- (left + right) / 2,
- top,
- // x2, y2
- right,
- top,
- // x3, y3
- right,
- bottom,
- // x4, y4
- left,
- bottom,
- // x5, y5
- left,
- top,
- ),
- centerX = (left + right) / 2,
- centerY = (top + bottom) / 2,
+ RoundedPolygon.rectangle(
+ width = right - left,
+ height = bottom - top,
+ centerX = (right - left) / 2,
+ centerY = (bottom - top) / 2,
rounding = CornerRounding(cornerR),
)
}
diff --git a/src/com/android/launcher3/shapes/ShapesProvider.kt b/src/com/android/launcher3/shapes/ShapesProvider.kt
index dfb7793..f1ea3a0 100644
--- a/src/com/android/launcher3/shapes/ShapesProvider.kt
+++ b/src/com/android/launcher3/shapes/ShapesProvider.kt
@@ -202,9 +202,9 @@
)
} else {
mapOf(
- "default" to
+ "circle" to
IconShapeModel(
- key = "default",
+ key = "circle",
title = "circle",
pathString = "M50 0A50 50,0,1,1,50 100A50 50,0,1,1,50 0",
)