Fixing icon shape not properly clipped
> Using ShapeDelegate for icon clipping
> Creating ShapeDelegate based on the current icon shape
> Defining different shape delegates for icon and folder
> Updating preview rendering to use dagger graph
> Adding tests for all icon shapes
Bug: 392145015
Test: atest IconShapesProviderTest
Flag: com.android.launcher3.enable_launcher_icon_shapes
Change-Id: Ic9f287e2a6fef2aad18ba224404de58a64da0bb3
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 9f47da7..b4a24f1 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -873,7 +873,7 @@
if (renderer == null) {
renderer = new DotRenderer(
size,
- IconShape.INSTANCE.get(context).getShapeOverridePath(DEFAULT_DOT_SIZE),
+ IconShape.INSTANCE.get(context).getShape().getPath(DEFAULT_DOT_SIZE),
DEFAULT_DOT_SIZE);
cache.put(size, renderer);
}
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index d8bb84e..484cef4 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -58,11 +58,11 @@
else encryptedContext.getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
/** Returns the value with type [T] for [item]. */
- fun <T> get(item: ContextualItem<T>): T =
+ open fun <T> get(item: ContextualItem<T>): T =
getInner(item, item.defaultValueFromContext(encryptedContext))
/** Returns the value with type [T] for [item]. */
- fun <T> get(item: ConstantItem<T>): T = getInner(item, item.defaultValue)
+ open fun <T> get(item: ConstantItem<T>): T = getInner(item, item.defaultValue)
/**
* Retrieves the value for an [Item] from [SharedPreferences]. It handles method typing via the
@@ -406,3 +406,20 @@
ENCRYPTED,
DEVICE_PROTECTED,
}
+
+/**
+ * LauncherPrefs which delegates all lookup to [prefs] but uses the real prefs for initial values
+ */
+class ProxyPrefs(context: Context, private val prefs: SharedPreferences) : LauncherPrefs(context) {
+
+ private val realPrefs = LauncherPrefs(context)
+
+ override val Item.sharedPrefs: SharedPreferences
+ get() = prefs
+
+ override fun <T> get(item: ConstantItem<T>) =
+ super.get(backedUpItem(item.sharedPrefKey, realPrefs.get(item)))
+
+ override fun <T> get(item: ContextualItem<T>) =
+ super.get(backedUpItem(item.sharedPrefKey, realPrefs.get(item)))
+}
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 9c82748..072673d 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -273,7 +273,11 @@
Rect shrunkBounds = new Rect(bounds);
Utilities.scaleRectAboutCenter(shrunkBounds, 0.98f);
adaptiveIcon.setBounds(shrunkBounds);
- final Path mask = IconShape.INSTANCE.get(getContext()).getShapeOverridePath(w);
+
+ IconShape iconShape = IconShape.INSTANCE.get(getContext());
+ final Path mask = (adaptiveIcon instanceof FolderAdaptiveIcon
+ ? iconShape.getFolderShape() : iconShape.getShape())
+ .getPath(shrunkBounds);
mTranslateX = new SpringFloatValue(DragView.this,
w * AdaptiveIconDrawable.getExtraInsetFraction());
diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
index 6a43b24..929e52e 100644
--- a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
+++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
@@ -165,7 +165,7 @@
Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
backgroundPaint.setColor(bg.getBgColor());
bg.drawShadow(backgroundCanvas);
- backgroundCanvas.drawCircle(size / 2f, size / 2f, bg.getRadius(), backgroundPaint);
+ backgroundCanvas.drawPaint(backgroundPaint);
bg.drawBackgroundStroke(backgroundCanvas);
}
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 588a6db..d2ff2cb 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -233,7 +233,7 @@
}
play(a, getAnimator(mFolder.mFooter, ALPHA, 0, 1f), footerStartDelay, footerAlphaDuration);
- ShapeDelegate shapeDelegate = IconShape.INSTANCE.get(mContext).getShape();
+ ShapeDelegate shapeDelegate = IconShape.INSTANCE.get(mContext).getFolderShape();
// Create reveal animator for the folder background
play(a, shapeDelegate.createRevealAnimator(
mFolder, startRect, endRect, finalRadius, !mIsOpening));
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index df41d47..d9c60db 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -261,7 +261,7 @@
}
private ShapeDelegate getShape() {
- return IconShape.INSTANCE.get(mContext).getShape();
+ return IconShape.INSTANCE.get(mContext).getFolderShape();
}
public void drawShadow(Canvas canvas) {
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index ad176dc..3479cec 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -16,10 +16,12 @@
package com.android.launcher3.graphics;
-import static com.android.launcher3.graphics.IconShape.PREF_ICON_SHAPE;
+import static com.android.launcher3.graphics.ThemeManager.PREF_ICON_SHAPE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static java.util.Objects.requireNonNullElse;
+
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
@@ -52,7 +54,6 @@
import com.android.systemui.shared.Flags;
import java.util.Collections;
-import java.util.List;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutionException;
@@ -141,23 +142,15 @@
if (Flags.newCustomizationPickerUi()) {
MatrixCursor cursor = new MatrixCursor(new String[]{
KEY_SHAPE_KEY, KEY_SHAPE_TITLE, KEY_PATH, KEY_IS_DEFAULT});
- List<IconShapeModel> shapes = IconShapesProvider.INSTANCE.getShapes()
- .values()
- .stream()
- .toList();
- String currentPath = LauncherPrefs.get(context).get(PREF_ICON_SHAPE);
- IconShapeModel currentShape = shapes.stream()
- .filter(shape -> currentPath.equals(shape.getPathString()))
- .findFirst()
- .orElse(IconShapesProvider.INSTANCE.getShapes().get("circle"));
-
- for (int i = 0; i < shapes.size(); i++) {
- IconShapeModel shape = shapes.get(i);
+ String currentShapePath =
+ ThemeManager.INSTANCE.get(context).getIconState().getIconMask();
+ for (IconShapeModel shape : IconShapesProvider.INSTANCE.getShapes().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.equals(currentShape));
+ .add(KEY_IS_DEFAULT,
+ shape.getPathString().equals(currentShapePath));
}
return cursor;
} else {
@@ -218,9 +211,8 @@
switch (path) {
case KEY_DEFAULT_GRID: {
if (Flags.newCustomizationPickerUi()) {
- String shapeKey = values.getAsString(KEY_SHAPE_KEY);
- IconShapeModel shape = IconShapesProvider.INSTANCE.getShapes().get(shapeKey);
- IconShape.INSTANCE.get(context).setShapeOverride(shape);
+ LauncherPrefs.INSTANCE.get(context).put(PREF_ICON_SHAPE,
+ requireNonNullElse(values.getAsString(KEY_SHAPE_KEY), ""));
}
String gridName = values.getAsString(KEY_NAME);
InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(context);
@@ -362,9 +354,7 @@
if (Flags.newCustomizationPickerUi()
&& com.android.launcher3.Flags.enableLauncherIconShapes()) {
String shapeKey = message.getData().getString(KEY_SHAPE_KEY);
- IconShapeModel shape =
- IconShapesProvider.INSTANCE.getShapes().get(shapeKey);
- renderer.updateShape(shape);
+ renderer.updateShape(shapeKey);
}
break;
case MESSAGE_ID_UPDATE_GRID:
diff --git a/src/com/android/launcher3/graphics/IconShape.kt b/src/com/android/launcher3/graphics/IconShape.kt
index 2c4d8e4..0938a5a 100644
--- a/src/com/android/launcher3/graphics/IconShape.kt
+++ b/src/com/android/launcher3/graphics/IconShape.kt
@@ -18,7 +18,6 @@
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
-import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Matrix
@@ -41,19 +40,12 @@
import androidx.graphics.shapes.SvgPathParser
import androidx.graphics.shapes.toPath
import androidx.graphics.shapes.transformed
-import com.android.launcher3.EncryptionType
-import com.android.launcher3.LauncherPrefs
-import com.android.launcher3.LauncherPrefs.Companion.backedUpItem
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider
-import com.android.launcher3.dagger.ApplicationContext
import com.android.launcher3.dagger.LauncherAppComponent
import com.android.launcher3.dagger.LauncherAppSingleton
-import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext
import com.android.launcher3.graphics.ThemeManager.ThemeChangeListener
import com.android.launcher3.icons.GraphicsUtils
-import com.android.launcher3.icons.IconNormalizer
-import com.android.launcher3.shapes.IconShapeModel
-import com.android.launcher3.shapes.IconShapesProvider
+import com.android.launcher3.icons.IconNormalizer.normalizeAdaptiveIcon
import com.android.launcher3.util.DaggerSingletonObject
import com.android.launcher3.util.DaggerSingletonTracker
import com.android.launcher3.views.ClipPathView
@@ -63,56 +55,52 @@
@LauncherAppSingleton
class IconShape
@Inject
-constructor(
- @ApplicationContext private val context: Context,
- private val prefs: LauncherPrefs,
- private val themeManager: ThemeManager,
- lifeCycle: DaggerSingletonTracker,
-) {
+constructor(private val themeManager: ThemeManager, lifeCycle: DaggerSingletonTracker) {
- var shapeOverride: IconShapeModel? = getShapeFromPathString(prefs.get(PREF_ICON_SHAPE))
- set(value) {
- field = value
- if (context !is PreviewContext) {
- value?.let { prefs.put(PREF_ICON_SHAPE, value.pathString) }
- }
- }
+ val normalizationScale =
+ normalizeAdaptiveIcon(
+ AdaptiveIconDrawable(null, ColorDrawable(Color.BLACK)),
+ AREA_CALC_SIZE,
+ )
- var normalizationScale: Float = IconNormalizer.ICON_VISIBLE_AREA_FACTOR
+ var shape: ShapeDelegate = pickBestShape(themeManager.iconState.iconMask)
private set
- var shape: ShapeDelegate = pickBestShape(themeManager)
+ var folderShape: ShapeDelegate =
+ themeManager.iconState.run {
+ if (folderShapeMask == iconMask || folderShapeMask.isEmpty()) shape
+ else pickBestShape(folderShapeMask)
+ }
private set
init {
- val changeListener = ThemeChangeListener { shape = pickBestShape(themeManager) }
+ val changeListener = ThemeChangeListener {
+ shape = pickBestShape(themeManager.iconState.iconMask)
+ folderShape =
+ themeManager.iconState.run {
+ if (folderShapeMask == iconMask || folderShapeMask.isEmpty()) shape
+ else pickBestShape(folderShapeMask)
+ }
+ }
themeManager.addChangeListener(changeListener)
lifeCycle.addCloseable { themeManager.removeChangeListener(changeListener) }
}
- fun getShapeOverridePath(pathSize: Float = DEFAULT_PATH_SIZE): Path {
- val path = PathParser.createPathFromPathData(themeManager.iconState.iconMask)
- if (pathSize != DEFAULT_PATH_SIZE) {
- val matrix = Matrix()
- val scale: Float = pathSize / DEFAULT_PATH_SIZE
- matrix.setScale(scale, scale)
- path.transform(matrix)
- }
- return path
- }
+ interface ShapeDelegate {
+ fun getPath(pathSize: Float = DEFAULT_PATH_SIZE) =
+ Path().apply { addToPath(this, 0f, 0f, pathSize / 2) }
- /** Initializes the shape which is closest to the [AdaptiveIconDrawable] */
- private fun pickBestShape(themeManager: ThemeManager): ShapeDelegate {
- val drawable =
- AdaptiveIconDrawable(null, ColorDrawable(Color.BLACK)).apply {
- setBounds(0, 0, AREA_CALC_SIZE, AREA_CALC_SIZE)
+ fun getPath(bounds: Rect) =
+ Path().apply {
+ addToPath(
+ this,
+ bounds.left.toFloat(),
+ bounds.top.toFloat(),
+ // Radius is half of the average size of the icon
+ (bounds.width() + bounds.height()) / 4f,
+ )
}
- normalizationScale = IconNormalizer.normalizeAdaptiveIcon(drawable, AREA_CALC_SIZE)
- return pickBestShape(drawable.iconMask, themeManager.iconState.iconMask)
- }
-
- interface ShapeDelegate {
fun drawShape(canvas: Canvas, offsetX: Float, offsetY: Float, radius: Float, paint: Paint)
fun addToPath(path: Path, offsetX: Float, offsetY: Float, radius: Float)
@@ -217,14 +205,24 @@
paint: Paint,
) {
tmpPath.reset()
- addToPath(tmpPath, offsetX, offsetY, radius)
+ addToPath(tmpPath, offsetX, offsetY, radius, tmpMatrix)
canvas.drawPath(tmpPath, paint)
}
override fun addToPath(path: Path, offsetX: Float, offsetY: Float, radius: Float) {
- tmpMatrix.setScale(radius / 50, radius / 50)
- tmpMatrix.postTranslate(offsetX, offsetY)
- basePath.transform(tmpMatrix, path)
+ addToPath(path, offsetX, offsetY, radius, Matrix())
+ }
+
+ private fun addToPath(
+ path: Path,
+ offsetX: Float,
+ offsetY: Float,
+ radius: Float,
+ matrix: Matrix,
+ ) {
+ matrix.setScale(radius / 50, radius / 50)
+ matrix.postTranslate(offsetX, offsetY)
+ basePath.transform(matrix, path)
}
override fun <T> createRevealAnimator(
@@ -292,20 +290,11 @@
@JvmField var INSTANCE = DaggerSingletonObject(LauncherAppComponent::getIconShape)
const val TAG = "IconShape"
- const val KEY_ICON_SHAPE = "icon_shape"
const val DEFAULT_PATH_SIZE = 100f
const val AREA_CALC_SIZE = 1000
// .1% error margin
const val AREA_DIFF_THRESHOLD = AREA_CALC_SIZE * AREA_CALC_SIZE / 1000
- @JvmField val PREF_ICON_SHAPE = backedUpItem(KEY_ICON_SHAPE, "", EncryptionType.ENCRYPTED)
-
- private fun getShapeFromPathString(pathString: String): IconShapeModel? {
- return IconShapesProvider.shapes.values.firstOrNull { shape: IconShapeModel ->
- shape.pathString == pathString
- }
- }
-
/** Returns a function to calculate area diff from [base] */
@VisibleForTesting
fun areaDiffCalculator(base: Path): (ShapeDelegate) -> Int {
@@ -324,6 +313,26 @@
}
@VisibleForTesting
+ fun pickBestShape(shapeStr: String): ShapeDelegate {
+ val baseShape =
+ if (shapeStr.isNotEmpty()) {
+ PathParser.createPathFromPathData(shapeStr).apply {
+ transform(
+ Matrix().apply {
+ setScale(AREA_CALC_SIZE / 100f, AREA_CALC_SIZE / 100f)
+ }
+ )
+ }
+ } else {
+ AdaptiveIconDrawable(null, ColorDrawable(Color.BLACK)).let {
+ it.setBounds(0, 0, AREA_CALC_SIZE, AREA_CALC_SIZE)
+ it.iconMask
+ }
+ }
+ return pickBestShape(baseShape, shapeStr)
+ }
+
+ @VisibleForTesting
fun pickBestShape(baseShape: Path, shapeStr: String): ShapeDelegate {
val calcAreaDiff = areaDiffCalculator(baseShape)
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index a0b73ae..911064c 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -67,7 +67,9 @@
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.ProxyPrefs;
import com.android.launcher3.R;
import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceLayoutManager;
@@ -75,6 +77,9 @@
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
import com.android.launcher3.celllayout.CellPosMapper;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dagger.LauncherAppComponent;
+import com.android.launcher3.dagger.LauncherAppModule;
+import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
@@ -100,12 +105,16 @@
import com.android.launcher3.widget.util.WidgetSizes;
import com.android.systemui.shared.Flags;
+import dagger.BindsInstance;
+import dagger.Component;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -126,12 +135,25 @@
*/
public static class PreviewContext extends SandboxContext {
+ private final String mPrefName;
+
public PreviewContext(Context base, InvariantDeviceProfile idp) {
super(base);
+ mPrefName = "preview-" + UUID.randomUUID().toString();
+ initDaggerComponent(DaggerLauncherPreviewRenderer_PreviewAppComponent.builder()
+ .bindPrefs(new ProxyPrefs(
+ this, getSharedPreferences(mPrefName, MODE_PRIVATE))));
+
putObject(InvariantDeviceProfile.INSTANCE, idp);
putObject(LauncherAppState.INSTANCE,
new LauncherAppState(this, null /* iconCacheFileName */));
}
+
+ @Override
+ protected void cleanUpObjects() {
+ super.cleanUpObjects();
+ deleteSharedPreferences(mPrefName);
+ }
}
private final List<OnDeviceProfileChangeListener> mDpChangeListeners = new ArrayList<>();
@@ -578,4 +600,16 @@
return true;
}
}
+
+ @LauncherAppSingleton
+ @Component(modules = LauncherAppModule.class)
+ public interface PreviewAppComponent extends LauncherAppComponent {
+
+ /** Builder for NexusLauncherAppComponent. */
+ @Component.Builder
+ interface Builder extends LauncherAppComponent.Builder {
+ @BindsInstance Builder bindPrefs(LauncherPrefs prefs);
+ PreviewAppComponent build();
+ }
+ }
}
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index 3464e9b..7da4e65 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -120,7 +120,7 @@
IconPalette.getPreloadProgressColor(context, info.bitmap.color),
getPreloadColors(context),
Utilities.isDarkTheme(context),
- IconShape.INSTANCE.get(context).getShapeOverridePath(DEFAULT_PATH_SIZE)
+ IconShape.INSTANCE.get(context).getShape().getPath(DEFAULT_PATH_SIZE)
);
}
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 6afac71..a4b681b 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -21,6 +21,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
+import static com.android.launcher3.graphics.ThemeManager.PREF_ICON_SHAPE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -52,6 +53,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Workspace;
import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext;
@@ -62,7 +64,6 @@
import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.model.ModelDbController;
import com.android.launcher3.provider.LauncherDbUtils;
-import com.android.launcher3.shapes.IconShapeModel;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.Themes;
@@ -71,6 +72,7 @@
import java.util.ArrayList;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
/** Render preview using surface view. */
@@ -91,7 +93,8 @@
private Context mContext;
private SparseIntArray mPreviewColorOverride;
private String mGridName;
- private IconShapeModel mShape;
+ private String mShapeKey;
+
@Nullable private Boolean mDarkMode;
private boolean mDestroyed = false;
private LauncherPreviewRenderer mRenderer;
@@ -220,14 +223,14 @@
/**
* Update the shapes of the launcher preview
*
- * @param shape path for shapes
+ * @param shapeKey key for the IconShape model
*/
- public void updateShape(@NonNull IconShapeModel shape) {
- if (shape.equals(mShape)) {
- Log.w(TAG, "Preview shape already set, skipping. shape=" + shape);
+ public void updateShape(@Nullable String shapeKey) {
+ if (Objects.equals(mShapeKey, shapeKey)) {
+ Log.w(TAG, "Preview shape already set, skipping. shape=" + mShapeKey);
return;
}
- mShape = shape;
+ mShapeKey = shapeKey;
loadAsync();
}
@@ -317,11 +320,11 @@
final Context inflationContext = getPreviewContext();
final InvariantDeviceProfile idp = new InvariantDeviceProfile(inflationContext, mGridName);
if (GridSizeMigrationDBController.needsToMigrate(inflationContext, idp)
- || mShape != null) {
+ || mShapeKey != null) {
// Start the migration
PreviewContext previewContext = new PreviewContext(inflationContext, idp);
- if (mShape != null) {
- IconShape.INSTANCE.get(previewContext).setShapeOverride(mShape);
+ if (mShapeKey != null) {
+ LauncherPrefs.INSTANCE.get(previewContext).put(PREF_ICON_SHAPE, mShapeKey);
}
// Copy existing data to preview DB
LauncherDbUtils.copyTable(LauncherAppState.getInstance(mContext)
diff --git a/src/com/android/launcher3/graphics/ThemeManager.kt b/src/com/android/launcher3/graphics/ThemeManager.kt
index 989471f..f24c2ab 100644
--- a/src/com/android/launcher3/graphics/ThemeManager.kt
+++ b/src/com/android/launcher3/graphics/ThemeManager.kt
@@ -25,10 +25,9 @@
import com.android.launcher3.dagger.ApplicationContext
import com.android.launcher3.dagger.LauncherAppComponent
import com.android.launcher3.dagger.LauncherAppSingleton
-import com.android.launcher3.graphics.IconShape.Companion.KEY_ICON_SHAPE
-import com.android.launcher3.graphics.IconShape.Companion.PREF_ICON_SHAPE
import com.android.launcher3.icons.IconThemeController
import com.android.launcher3.icons.mono.MonoIconThemeController
+import com.android.launcher3.shapes.IconShapesProvider
import com.android.launcher3.util.DaggerSingletonObject
import com.android.launcher3.util.DaggerSingletonTracker
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
@@ -93,20 +92,26 @@
fun removeChangeListener(listener: ThemeChangeListener) = listeners.remove(listener)
private fun parseIconState(): IconState {
- val shapeOverride = prefs.get(PREF_ICON_SHAPE)
+ val shapeModel =
+ prefs.get(PREF_ICON_SHAPE).let { shapeOverride ->
+ IconShapesProvider.shapes.values.firstOrNull { it.key == shapeOverride }
+ }
+ val iconMask =
+ when {
+ shapeModel != null -> shapeModel.pathString
+ CONFIG_ICON_MASK_RES_ID == Resources.ID_NULL -> ""
+ else -> context.resources.getString(CONFIG_ICON_MASK_RES_ID)
+ }
return IconState(
- iconMask =
- when {
- shapeOverride.isNotEmpty() -> shapeOverride
- CONFIG_ICON_MASK_RES_ID == Resources.ID_NULL -> ""
- else -> context.resources.getString(CONFIG_ICON_MASK_RES_ID)
- },
+ iconMask = iconMask,
+ folderShapeMask = shapeModel?.folderPathString ?: iconMask,
isMonoTheme = isMonoThemeEnabled,
)
}
data class IconState(
val iconMask: String,
+ val folderShapeMask: String,
val isMonoTheme: Boolean,
val themeCode: String = if (isMonoTheme) "with-theme" else "no-theme",
) {
@@ -121,9 +126,11 @@
companion object {
@JvmField val INSTANCE = DaggerSingletonObject(LauncherAppComponent::getThemeManager)
+ const val KEY_ICON_SHAPE = "icon_shape_model"
const val KEY_THEMED_ICONS = "themed_icons"
@JvmField val THEMED_ICONS = backedUpItem(KEY_THEMED_ICONS, false, EncryptionType.ENCRYPTED)
+ @JvmField val PREF_ICON_SHAPE = backedUpItem(KEY_ICON_SHAPE, "", EncryptionType.ENCRYPTED)
private const val ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED"
private val CONFIG_ICON_MASK_RES_ID: Int =
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index 2ffbeb8..59fff62 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -17,22 +17,18 @@
package com.android.launcher3.icons;
import android.content.Context;
-import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.os.UserHandle;
import androidx.annotation.NonNull;
-import androidx.core.graphics.PathParser;
import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.shapes.IconShapeModel;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.UserIconInfo;
@@ -87,22 +83,7 @@
@Override
public Path getShapePath(AdaptiveIconDrawable drawable, Rect iconBounds) {
if (!Flags.enableLauncherIconShapes()) return drawable.getIconMask();
-
- IconShapeModel shapeOverride = IconShape.INSTANCE.get(mContext).getShapeOverride();
- if (shapeOverride != null) {
- Path maskPath = PathParser.createPathFromPathData(shapeOverride.getPathString());
- Matrix matrix = new Matrix();
- // Assuming Path is in [0, 0, 100, 100] coordinate space.
- matrix.setRectToRect(
- new RectF(0, 0, 100, 100),
- new RectF(iconBounds),
- Matrix.ScaleToFit.CENTER // Todo: CENTER or FILL?
- );
- maskPath.transform(matrix);
- return maskPath;
- } else {
- return drawable.getIconMask();
- }
+ return IconShape.INSTANCE.get(mContext).getShape().getPath(iconBounds);
}
@Override
diff --git a/src/com/android/launcher3/shapes/IconShapeModel.kt b/src/com/android/launcher3/shapes/IconShapeModel.kt
index 10b7f91..dd6c432 100644
--- a/src/com/android/launcher3/shapes/IconShapeModel.kt
+++ b/src/com/android/launcher3/shapes/IconShapeModel.kt
@@ -16,4 +16,9 @@
package com.android.launcher3.shapes
-data class IconShapeModel(val key: String, val title: String, val pathString: String)
+data class IconShapeModel(
+ val key: String,
+ val title: String,
+ val pathString: String,
+ val folderPathString: String = pathString,
+)
diff --git a/src/com/android/launcher3/shapes/IconShapesProvider.kt b/src/com/android/launcher3/shapes/IconShapesProvider.kt
index a190e22..8608437 100644
--- a/src/com/android/launcher3/shapes/IconShapesProvider.kt
+++ b/src/com/android/launcher3/shapes/IconShapesProvider.kt
@@ -28,7 +28,7 @@
key = "arch",
title = "arch",
pathString =
- "M50 0C77.614 0 100 22.386 100 50C100 85.471 100 86.476 99.9 87.321 99.116 93.916 93.916 99.116 87.321 99.9 86.476 100 85.471 100 83.46 100H16.54C14.529 100 13.524 100 12.679 99.9 6.084 99.116.884 93.916.1 87.321 0 86.476 0 85.471 0 83.46L0 50C0 22.386 22.386 0 50 0Z",
+ "M50 0C77.614 0 100 22.386 100 50C100 85.471 100 86.476 99.9 87.321 99.116 93.916 93.916 99.116 87.321 99.9 86.476 100 85.471 100 83.46 100H16.54C14.529 100 13.524 100 12.679 99.9 6.084 99.116 .884 93.916 .1 87.321 0 86.476 0 85.471 0 83.46L0 50C0 22.386 22.386 0 50 0Z",
),
"4_sided_cookie" to
IconShapeModel(
@@ -42,7 +42,7 @@
key = "seven_sided_cookie",
title = "7 sided cookie",
pathString =
- "M35.209 4.878C36.326 3.895 36.884 3.404 37.397 3.006 44.82 -2.742 55.18 -2.742 62.603 3.006 63.116 3.404 63.674 3.895 64.791 4.878 65.164 5.207 65.351 5.371 65.539 5.529 68.167 7.734 71.303 9.248 74.663 9.932 74.902 9.981 75.147 10.025 75.637 10.113 77.1 10.375 77.831 10.506 78.461 10.66 87.573 12.893 94.032 21.011 94.176 30.412 94.186 31.062 94.151 31.805 94.08 33.293 94.057 33.791 94.045 34.04 94.039 34.285 93.958 37.72 94.732 41.121 96.293 44.18 96.404 44.399 96.522 44.618 96.759 45.056 97.467 46.366 97.821 47.021 98.093 47.611 102.032 56.143 99.727 66.266 92.484 72.24 91.983 72.653 91.381 73.089 90.177 73.961 89.774 74.254 89.572 74.4 89.377 74.548 86.647 76.626 84.477 79.353 83.063 82.483 82.962 82.707 82.865 82.936 82.671 83.395 82.091 84.766 81.8 85.451 81.51 86.033 77.31 94.44 67.977 98.945 58.801 96.994 58.166 96.859 57.451 96.659 56.019 96.259 55.54 96.125 55.3 96.058 55.063 95.998 51.74 95.154 48.26 95.154 44.937 95.998 44.699 96.058 44.46 96.125 43.981 96.259 42.549 96.659 41.834 96.859 41.199 96.994 32.023 98.945 22.69 94.44 18.49 86.033 18.2 85.451 17.909 84.766 17.329 83.395 17.135 82.936 17.038 82.707 16.937 82.483 15.523 79.353 13.353 76.626 10.623 74.548 10.428 74.4 10.226 74.254 9.823 73.961 8.619 73.089 8.017 72.653 7.516 72.24.273 66.266 -2.032 56.143 1.907 47.611 2.179 47.021 2.533 46.366 3.241 45.056 3.478 44.618 3.596 44.399 3.707 44.18 5.268 41.121 6.042 37.72 5.961 34.285 5.955 34.04 5.943 33.791 5.92 33.293 5.849 31.805 5.814 31.062 5.824 30.412 5.968 21.011 12.427 12.893 21.539 10.66 22.169 10.506 22.9 10.375 24.363 10.113 24.853 10.025 25.098 9.981 25.337 9.932 28.697 9.248 31.833 7.734 34.461 5.529 34.649 5.371 34.836 5.207 35.209 4.878Z",
+ "M35.209 4.878C36.326 3.895 36.884 3.404 37.397 3.006 44.82 -2.742 55.18 -2.742 62.603 3.006 63.116 3.404 63.674 3.895 64.791 4.878 65.164 5.207 65.351 5.371 65.539 5.529 68.167 7.734 71.303 9.248 74.663 9.932 74.902 9.981 75.147 10.025 75.637 10.113 77.1 10.375 77.831 10.506 78.461 10.66 87.573 12.893 94.032 21.011 94.176 30.412 94.186 31.062 94.151 31.805 94.08 33.293 94.057 33.791 94.045 34.04 94.039 34.285 93.958 37.72 94.732 41.121 96.293 44.18 96.404 44.399 96.522 44.618 96.759 45.056 97.467 46.366 97.821 47.021 98.093 47.611 102.032 56.143 99.727 66.266 92.484 72.24 91.983 72.653 91.381 73.089 90.177 73.961 89.774 74.254 89.572 74.4 89.377 74.548 86.647 76.626 84.477 79.353 83.063 82.483 82.962 82.707 82.865 82.936 82.671 83.395 82.091 84.766 81.8 85.451 81.51 86.033 77.31 94.44 67.977 98.945 58.801 96.994 58.166 96.859 57.451 96.659 56.019 96.259 55.54 96.125 55.3 96.058 55.063 95.998 51.74 95.154 48.26 95.154 44.937 95.998 44.699 96.058 44.46 96.125 43.981 96.259 42.549 96.659 41.834 96.859 41.199 96.994 32.023 98.945 22.69 94.44 18.49 86.033 18.2 85.451 17.909 84.766 17.329 83.395 17.135 82.936 17.038 82.707 16.937 82.483 15.523 79.353 13.353 76.626 10.623 74.548 10.428 74.4 10.226 74.254 9.823 73.961 8.619 73.089 8.017 72.653 7.516 72.24 .273 66.266 -2.032 56.143 1.907 47.611 2.179 47.021 2.533 46.366 3.241 45.056 3.478 44.618 3.596 44.399 3.707 44.18 5.268 41.121 6.042 37.72 5.961 34.285 5.955 34.04 5.943 33.791 5.92 33.293 5.849 31.805 5.814 31.062 5.824 30.412 5.968 21.011 12.427 12.893 21.539 10.66 22.169 10.506 22.9 10.375 24.363 10.113 24.853 10.025 25.098 9.981 25.337 9.932 28.697 9.248 31.833 7.734 34.461 5.529 34.649 5.371 34.836 5.207 35.209 4.878Z",
),
"sunny" to
IconShapeModel(
@@ -62,7 +62,7 @@
key = "square",
title = "square",
pathString =
- "M53.689 0.82L53.689.82C67.434.82 74.306.82 79.758 2.978 87.649 6.103 93.897 12.351 97.022 20.242 99.18 25.694 99.18 32.566 99.18 46.311V53.689C99.18 67.434 99.18 74.306 97.022 79.758 93.897 87.649 87.649 93.897 79.758 97.022 74.306 99.18 67.434 99.18 53.689 99.18H46.311C32.566 99.18 25.694 99.18 20.242 97.022 12.351 93.897 6.103 87.649 2.978 79.758.82 74.306.82 67.434.82 53.689L.82 46.311C.82 32.566.82 25.694 2.978 20.242 6.103 12.351 12.351 6.103 20.242 2.978 25.694.82 32.566.82 46.311.82Z",
+ "M53.689 0.82 L53.689 .82 C67.434 .82 74.306 .82 79.758 2.978 87.649 6.103 93.897 12.351 97.022 20.242 99.18 25.694 99.18 32.566 99.18 46.311 V53.689 C99.18 67.434 99.18 74.306 97.022 79.758 93.897 87.649 87.649 93.897 79.758 97.022 74.306 99.18 67.434 99.18 53.689 99.18 H46.311 C32.566 99.18 25.694 99.18 20.242 97.022 12.351 93.897 6.103 87.649 2.978 79.758 .82 74.306 .82 67.434 .82 53.689 L.82 46.311 C.82 32.566 .82 25.694 2.978 20.242 6.103 12.351 12.351 6.103 20.242 2.978 25.694 .82 32.566 .82 46.311 .82Z",
),
)
} else {