Loading iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java +48 −25 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; } /** Loading @@ -74,15 +94,21 @@ public class DotRenderer { return; } canvas.save(); 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. 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); canvas.translate(dotCenterX + offsetX, dotCenterY + offsetY); canvas.scale(params.scale, params.scale); mCirclePaint.setColor(Color.BLACK); Loading @@ -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; Loading src/com/android/launcher3/BubbleTextView.java +3 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading src/com/android/launcher3/DeviceProfile.java +3 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading src/com/android/launcher3/folder/FolderIcon.java +5 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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}. */ Loading Loading @@ -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); } Loading Loading
iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java +48 −25 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; } /** Loading @@ -74,15 +94,21 @@ public class DotRenderer { return; } canvas.save(); 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. 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); canvas.translate(dotCenterX + offsetX, dotCenterY + offsetY); canvas.scale(params.scale, params.scale); mCirclePaint.setColor(Color.BLACK); Loading @@ -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; Loading
src/com/android/launcher3/BubbleTextView.java +3 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading
src/com/android/launcher3/DeviceProfile.java +3 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading
src/com/android/launcher3/folder/FolderIcon.java +5 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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}. */ Loading Loading @@ -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); } Loading