From 5cb668c61c95857fa9c25eef018e327deb9bb30e Mon Sep 17 00:00:00 2001 From: althafvly Date: Thu, 30 May 2024 11:13:32 +0530 Subject: [PATCH 01/14] bl3: make it buildable Signed-off-by: Saalim Quadri --- animationlib/.gitignore | 13 ++++++++++ animationlib/build.gradle | 43 ---------------------------------- animationlib/build.gradle.kts | 37 +++++++++++++++++++++++++++++ iconloaderlib/build.gradle.kts | 29 ++++++++++++++++++----- 4 files changed, 73 insertions(+), 49 deletions(-) create mode 100644 animationlib/.gitignore delete mode 100644 animationlib/build.gradle create mode 100644 animationlib/build.gradle.kts diff --git a/animationlib/.gitignore b/animationlib/.gitignore new file mode 100644 index 00000000..6213826a --- /dev/null +++ b/animationlib/.gitignore @@ -0,0 +1,13 @@ +*.iml +.project +.classpath +.project.properties +gen/ +bin/ +.idea/ +.gradle/ +local.properties +gradle/ +build/ +gradlew* +.DS_Store diff --git a/animationlib/build.gradle b/animationlib/build.gradle deleted file mode 100644 index bd5c575e..00000000 --- a/animationlib/build.gradle +++ /dev/null @@ -1,43 +0,0 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' - -android { - namespace = "com.android.app.animation" - testNamespace = "com.android.app.animation.tests" - defaultConfig { - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - sourceSets { - main { - java.srcDirs = ['src'] - res.srcDirs = ['res'] - manifest.srcFile 'AndroidManifest.xml' - } - androidTest { - java.srcDirs = ["tests/src", "tests/robolectric/src"] - manifest.srcFile 'tests/AndroidManifest.xml' - } - } - - lintOptions { - abortOnError false - } - tasks.lint.enabled = false - tasks.withType(JavaCompile) { - options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" - } - kotlinOptions { - jvmTarget = '1.8' - freeCompilerArgs = ["-Xjvm-default=all"] - } -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0" - implementation "androidx.core:core-animation:1.0.0-alpha02" - implementation "androidx.core:core-ktx:1.9.0" - androidTestImplementation libs.robolectric - androidTestImplementation "androidx.test.ext:junit:1.1.3" - androidTestImplementation "androidx.test:rules:1.4.0" -} diff --git a/animationlib/build.gradle.kts b/animationlib/build.gradle.kts new file mode 100644 index 00000000..4025b81f --- /dev/null +++ b/animationlib/build.gradle.kts @@ -0,0 +1,37 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin) +} + +android { + namespace = "com.android.app.animation" + compileSdk = 35 + + defaultConfig { + minSdk = 35 + vectorDrawables.useSupportLibrary = true + } + + lint { + abortOnError = true + checkReleaseBuilds = false + } + + sourceSets { + named("main") { + java.srcDirs("src") + res.srcDirs("res") + manifest.srcFile("AndroidManifest.xml") + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } +} + +dependencies { + implementation(libs.androidx.core.animation) + implementation(libs.androidx.core) +} diff --git a/iconloaderlib/build.gradle.kts b/iconloaderlib/build.gradle.kts index 26784331..853bebae 100644 --- a/iconloaderlib/build.gradle.kts +++ b/iconloaderlib/build.gradle.kts @@ -1,20 +1,37 @@ plugins { - id(libs.plugins.android.library.get().pluginId) - id(libs.plugins.kotlin.android.get().pluginId) + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin) } android { namespace = "com.android.launcher3.icons" + compileSdk = 35 + + defaultConfig { + minSdk = 35 + vectorDrawables.useSupportLibrary = true + } + + lint { + abortOnError = true + checkReleaseBuilds = false + } + sourceSets { named("main") { - java.setSrcDirs(listOf("src", "src_full_lib")) + java.srcDirs("src") manifest.srcFile("AndroidManifest.xml") - res.setSrcDirs(listOf("res")) + res.srcDirs("res") } } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } } dependencies { - implementation("androidx.core:core") - api(project(":NexusLauncher.Flags")) + implementation(libs.androidx.core) + implementation(files("${rootDir}/libs/com_android_launcher3_flags_lib.jar")) } -- GitLab From 4143d28cba4d930b9d21bce2e557db0ab18a1079 Mon Sep 17 00:00:00 2001 From: Yash Garg Date: Mon, 27 Mar 2023 21:17:45 +0530 Subject: [PATCH 02/14] feat(dots): notification count support for launcher3 app badges Signed-off-by: Saalim Quadri --- .../android/launcher3/icons/DotRenderer.java | 116 +++++++++++++++++- 1 file changed, 110 insertions(+), 6 deletions(-) diff --git a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java index a42232e3..1941dac1 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java +++ b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java @@ -27,9 +27,12 @@ import android.graphics.Path; import android.graphics.PathMeasure; import android.graphics.Rect; import android.graphics.RectF; +import android.graphics.Typeface; import android.util.Log; import android.view.ViewDebug; +import androidx.core.graphics.ColorUtils; + /** * Used to draw a notification dot on top of an icon. */ @@ -38,21 +41,30 @@ 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.228f; + private static final float SIZE_PERCENTAGE = 0.21f; + private static final float SIZE_PERCENTAGE_WITH_COUNT = 0.28f; + + // The max number to draw on dots + private static final int MAX_COUNT = 99; - private final float mCircleRadius; + private float mCircleRadius; private final Paint mCirclePaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG); + private final Paint mTextPaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG); - private final Bitmap mBackgroundWithShadow; - private final float mBitmapOffset; + private Bitmap mBackgroundWithShadow; + private float mBitmapOffset; // 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; private static final int MIN_DOT_SIZE = 1; - public DotRenderer(int iconSizePx, Path iconShapePath, int pathSize) { - int size = Math.round(SIZE_PERCENTAGE * iconSizePx); + private final Rect mTextRect = new Rect(); + private boolean mDisplayCount; + + public DotRenderer(int iconSizePx, Path iconShapePath, int pathSize, Boolean displayCount, Typeface typeface) { + mDisplayCount = displayCount; + int size = Math.round((displayCount ? SIZE_PERCENTAGE_WITH_COUNT : SIZE_PERCENTAGE) * iconSizePx); if (size <= 0) { size = MIN_DOT_SIZE; } @@ -66,6 +78,21 @@ public class DotRenderer { // 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); + + mTextPaint.setTextSize(size * 0.65f); + mTextPaint.setTextAlign(Paint.Align.LEFT); + mTextPaint.setTypeface(typeface); + } + + public DotRenderer(int iconSizePx, Path iconShapePath, int pathSize) { + int size = Math.round(SIZE_PERCENTAGE * iconSizePx); + + if (size <= 0) { + size = MIN_DOT_SIZE; + } + // 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) { @@ -127,9 +154,64 @@ public class DotRenderer { canvas.drawBitmap(mBackgroundWithShadow, mBitmapOffset, mBitmapOffset, mCirclePaint); mCirclePaint.setColor(params.dotColor); canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint); + + canvas.restore(); + } + + /** + * Draw a circle on top of the canvas according to the given params. + */ + public void draw(Canvas canvas, DrawParams params, int numNotifications) { + if (params == null) { + Log.e(TAG, "Invalid null argument(s) passed in call to draw."); + 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. + canvas.translate(dotCenterX + offsetX, dotCenterY + offsetY); + canvas.scale(params.scale, params.scale); + + mCirclePaint.setColor(Color.BLACK); + canvas.drawBitmap(mBackgroundWithShadow, mBitmapOffset, mBitmapOffset, mCirclePaint); + mCirclePaint.setColor(params.dotColor); + canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint); + + if (mDisplayCount && numNotifications > 0) { + // Draw the numNotifications text + mTextPaint.setColor(getCounterTextColor(Color.WHITE)); + String text = String.valueOf(Math.min(numNotifications, MAX_COUNT)); + mTextPaint.getTextBounds(text, 0, text.length(), mTextRect); + float x = (-mTextRect.width() / 2f - mTextRect.left) * getAdjustment(numNotifications); + float y = mTextRect.height() / 2f - mTextRect.bottom; + canvas.drawText(text, x, y, mTextPaint); + } + canvas.restore(); } + /** + * Returns the color to use for the counter text based on the dot's background color. + * + * @param dotBackgroundColor The color of the dot background. + * @return The color to use on the counter text. + */ + private int getCounterTextColor(int dotBackgroundColor) { + return ColorUtils.setAlphaComponent(dotBackgroundColor, 0xFF); + } + public static class DrawParams { /** The color (possibly based on the icon) to use for the dot. */ @ViewDebug.ExportedProperty(category = "notification dot", formatToHexString = true) @@ -147,4 +229,26 @@ public class DotRenderer { @ViewDebug.ExportedProperty(category = "notification dot") public boolean leftAlign; } + + /** + * An attempt to adjust digits to their perceived center, they were tuned with Roboto but should + * (hopefully) work with other OEM fonts as well. + */ + private float getAdjustment(int number) { + switch (number) { + case 1: + return 1.01f; + case 2: + return 0.99f; + case 3: + case 4: + case 6: + return 0.98f; + case 7: + return 1.02f; + case 9: + return 0.9f; + } + return 1f; + } } -- GitLab From 3ff12dc316620d416d8bc49075f72bdcd023a93b Mon Sep 17 00:00:00 2001 From: TheScarastic Date: Tue, 4 Apr 2023 19:32:07 +0530 Subject: [PATCH 03/14] feat(icons): Add month icon support for calendar Signed-off-by: Saalim Quadri --- .../android/launcher3/icons/IconProvider.java | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/iconloaderlib/src/com/android/launcher3/icons/IconProvider.java b/iconloaderlib/src/com/android/launcher3/icons/IconProvider.java index 594db358..80a314aa 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/IconProvider.java +++ b/iconloaderlib/src/com/android/launcher3/icons/IconProvider.java @@ -71,6 +71,8 @@ public class IconProvider { public static final boolean ATLEAST_T = BuildCompat.isAtLeastT(); private static final String ICON_METADATA_KEY_PREFIX = ".dynamic_icons"; + private static final String MONTH_BG_ICON_METADATA_KEY_PREFIX = ".month_dynamic_icons"; + private static final String DAY_FG_ICON_METADATA_KEY_PREFIX = ".day_dynamic_icons"; private static final String SYSTEM_STATE_SEPARATOR = " "; @@ -207,7 +209,9 @@ public class IconProvider { .metaData; final Resources resources = pm.getResourcesForApplication(mCalendar.getPackageName()); final int id = getDynamicIconId(metadata, resources); - if (id != ID_NULL) { + final int[] monthDayIds = getDynamicBackgroundIconId(metadata, resources); + if (id != ID_NULL && (monthDayIds == null || + monthDayIds[0] == ID_NULL || monthDayIds[1] == ID_NULL )) { if (DEBUG) Log.d(TAG, "Got icon #" + id); Drawable drawable = resources.getDrawableForDensity(id, iconDpi, null /* theme */); if (ATLEAST_T && drawable instanceof AdaptiveIconDrawable && td != null) { @@ -225,6 +229,10 @@ public class IconProvider { } } return drawable; + } else if (monthDayIds != null) { + Drawable background = resources.getDrawableForDensity(monthDayIds[0], iconDpi, null /* theme */); + Drawable foreground = resources.getDrawableForDensity(monthDayIds[1], iconDpi, null /* theme */); + return new AdaptiveIconDrawable(background, foreground); } } catch (PackageManager.NameNotFoundException e) { if (DEBUG) { @@ -268,6 +276,28 @@ public class IconProvider { } } + private int[] getDynamicBackgroundIconId(Bundle metadata, Resources resources) { + if (metadata == null) { + return null; + } + String bgKey = mCalendar.getPackageName() + MONTH_BG_ICON_METADATA_KEY_PREFIX; + String fgKey = mCalendar.getPackageName() + DAY_FG_ICON_METADATA_KEY_PREFIX; + final int bgArrayId = metadata.getInt(bgKey, ID_NULL); + final int fgArrayId = metadata.getInt(fgKey, ID_NULL); + if (bgArrayId == ID_NULL || fgArrayId == ID_NULL) { + return null; + } + try { + return new int[] {resources.obtainTypedArray(bgArrayId).getResourceId(getMonth(), ID_NULL), + resources.obtainTypedArray(fgArrayId).getResourceId(getDay(), ID_NULL)}; + } catch (Resources.NotFoundException e) { + if (DEBUG) { + Log.d(TAG, "package defines '" + bgKey + " and " + fgKey + "' but corresponding array not found"); + } + return null; + } + } + /** * Refreshes the system state definition used to check the validity of an app icon. It * incorporates all the properties that can affect the app icon like the list of enabled locale @@ -285,6 +315,10 @@ public class IconProvider { return Calendar.getInstance().get(Calendar.DAY_OF_MONTH) - 1; } + private static int getMonth() { + return Calendar.getInstance().get(Calendar.MONTH); + } + private static ComponentName parseComponentOrNull(Context context, int resId) { String cn = context.getString(resId); return TextUtils.isEmpty(cn) ? null : ComponentName.unflattenFromString(cn); -- GitLab From a5e730342c0f278eb06bf3840aed8e5b4197f59d Mon Sep 17 00:00:00 2001 From: TheScarastic Date: Thu, 13 Apr 2023 18:49:02 +0530 Subject: [PATCH 04/14] iconloader: Add unchanges constructor for aosp Saalim: Adapt to a15 Signed-off-by: Saalim Quadri --- .../android/launcher3/icons/DotRenderer.java | 44 +++---------------- 1 file changed, 5 insertions(+), 39 deletions(-) diff --git a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java index 1941dac1..c04c0f4e 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java +++ b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java @@ -62,6 +62,10 @@ public class DotRenderer { private final Rect mTextRect = new Rect(); private boolean mDisplayCount; + public DotRenderer(int iconSizePx, Path iconShapePath, int pathSize) { + this(iconSizePx, iconShapePath, pathSize, false, null); + } + public DotRenderer(int iconSizePx, Path iconShapePath, int pathSize, Boolean displayCount, Typeface typeface) { mDisplayCount = displayCount; int size = Math.round((displayCount ? SIZE_PERCENTAGE_WITH_COUNT : SIZE_PERCENTAGE) * iconSizePx); @@ -84,17 +88,6 @@ public class DotRenderer { mTextPaint.setTypeface(typeface); } - public DotRenderer(int iconSizePx, Path iconShapePath, int pathSize) { - int size = Math.round(SIZE_PERCENTAGE * iconSizePx); - - if (size <= 0) { - size = MIN_DOT_SIZE; - } - // 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 @@ -128,34 +121,7 @@ public class DotRenderer { * Draw a circle on top of the canvas according to the given params. */ public void draw(Canvas canvas, DrawParams params) { - if (params == null) { - Log.e(TAG, "Invalid null argument(s) passed in call to draw."); - 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. - canvas.translate(dotCenterX + offsetX, dotCenterY + offsetY); - canvas.scale(params.scale, params.scale); - - mCirclePaint.setColor(Color.BLACK); - canvas.drawBitmap(mBackgroundWithShadow, mBitmapOffset, mBitmapOffset, mCirclePaint); - mCirclePaint.setColor(params.dotColor); - canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint); - - canvas.restore(); + draw(canvas, params, 0); } /** -- GitLab From 38109b740e72064bef74ec814e8a3aa71120e9e5 Mon Sep 17 00:00:00 2001 From: Yash Garg Date: Sun, 26 Nov 2023 23:03:41 +0530 Subject: [PATCH 05/14] feat: convert badge to round rect when count > 99 Signed-off-by: Saalim Quadri --- .../android/launcher3/icons/DotRenderer.java | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java index c04c0f4e..61397214 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java +++ b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java @@ -45,7 +45,7 @@ public class DotRenderer { private static final float SIZE_PERCENTAGE_WITH_COUNT = 0.28f; // The max number to draw on dots - private static final int MAX_COUNT = 99; + private static final int MAX_COUNT = 999; private float mCircleRadius; private final Paint mCirclePaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG); @@ -147,18 +147,34 @@ public class DotRenderer { float offsetY = Math.max(0, canvasBounds.top - (dotCenterY + mBitmapOffset)); // We draw the dot relative to its center. - canvas.translate(dotCenterX + offsetX, dotCenterY + offsetY); + float dx = dotCenterX + offsetX; + float dy = dotCenterY + offsetY - 15f; + + if (numNotifications > 9 && numNotifications < 100) { + canvas.translate(dx - 3f, dy); + } else if (numNotifications > 99 && numNotifications < 1000) { + canvas.translate(dx + 6f, dy); + } else { + canvas.translate(dx - 12f, dy); + } + canvas.scale(params.scale, params.scale); - mCirclePaint.setColor(Color.BLACK); - canvas.drawBitmap(mBackgroundWithShadow, mBitmapOffset, mBitmapOffset, mCirclePaint); mCirclePaint.setColor(params.dotColor); - canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint); + if (numNotifications > 9 && numNotifications < 100) { + canvas.drawRoundRect(new RectF(-mCircleRadius - 10, -mCircleRadius, mCircleRadius + 10, mCircleRadius), 50, 50, mCirclePaint); + } else if (numNotifications > 99 && numNotifications < 1000) { + canvas.drawRoundRect(new RectF(-mCircleRadius - 20, -mCircleRadius, mCircleRadius + 20, mCircleRadius), 50, 50, mCirclePaint); + } else { + canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint); + } if (mDisplayCount && numNotifications > 0) { // Draw the numNotifications text mTextPaint.setColor(getCounterTextColor(Color.WHITE)); - String text = String.valueOf(Math.min(numNotifications, MAX_COUNT)); + mTextPaint.setTextSize(42f); + mTextPaint.setTypeface(Typeface.DEFAULT_BOLD); + String text = numToNotation(numNotifications); mTextPaint.getTextBounds(text, 0, text.length(), mTextRect); float x = (-mTextRect.width() / 2f - mTextRect.left) * getAdjustment(numNotifications); float y = mTextRect.height() / 2f - mTextRect.bottom; @@ -168,6 +184,14 @@ public class DotRenderer { canvas.restore(); } + private String numToNotation(int num) { + if (num < 1000) { + return String.valueOf(num); + } else { + return num / 1000 + "k"; + } + } + /** * Returns the color to use for the counter text based on the dot's background color. * -- GitLab From 7abded72c9274bc11dd499f3689e629f6693c6f9 Mon Sep 17 00:00:00 2001 From: TheScarastic Date: Tue, 12 Dec 2023 14:08:26 +0530 Subject: [PATCH 06/14] DotRenderer: Add shadow to notification dots Signed-off-by: Saalim Quadri --- .../src/com/android/launcher3/icons/DotRenderer.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java index 61397214..76f30f2d 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java +++ b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java @@ -49,6 +49,8 @@ public class DotRenderer { private float mCircleRadius; private final Paint mCirclePaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG); + private final Paint mCircleShadowPaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG); + private final Paint mTextPaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG); private Bitmap mBackgroundWithShadow; @@ -161,11 +163,15 @@ public class DotRenderer { canvas.scale(params.scale, params.scale); mCirclePaint.setColor(params.dotColor); + mCircleShadowPaint.setColor(params.shadowDotColor); if (numNotifications > 9 && numNotifications < 100) { + canvas.drawRoundRect(new RectF(-mCircleRadius - 10, -mCircleRadius, mCircleRadius + 10, mCircleRadius + 3), 50, 50, mCircleShadowPaint); canvas.drawRoundRect(new RectF(-mCircleRadius - 10, -mCircleRadius, mCircleRadius + 10, mCircleRadius), 50, 50, mCirclePaint); } else if (numNotifications > 99 && numNotifications < 1000) { + canvas.drawRoundRect(new RectF(-mCircleRadius - 20, -mCircleRadius, mCircleRadius + 20, mCircleRadius + 3), 50, 50, mCircleShadowPaint); canvas.drawRoundRect(new RectF(-mCircleRadius - 20, -mCircleRadius, mCircleRadius + 20, mCircleRadius), 50, 50, mCirclePaint); } else { + canvas.drawCircle(0, 3, mCircleRadius, mCircleShadowPaint); canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint); } @@ -218,6 +224,8 @@ public class DotRenderer { /** 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; + @ViewDebug.ExportedProperty(category = "notification dot", formatToHexString = true) + public int shadowDotColor; } /** -- GitLab From cb0b37827c01bedc7bae1c45df6ca835f38b6762 Mon Sep 17 00:00:00 2001 From: althafvly Date: Thu, 29 Feb 2024 21:22:20 +0530 Subject: [PATCH 07/14] feat: adjust dot size accordingly Signed-off-by: Saalim Quadri --- .../android/launcher3/icons/DotRenderer.java | 51 +++++++------------ 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java index 76f30f2d..d02cce00 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java +++ b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java @@ -76,7 +76,7 @@ public class DotRenderer { } ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.TRANSPARENT); builder.ambientShadowAlpha = 88; - mBackgroundWithShadow = builder.setupBlurForSize(size).createPill(size, size); + mBackgroundWithShadow = builder.setupBlurForSize(size).createPill(size, displayCount ? (size - 25) : size); mCircleRadius = builder.radius; mBitmapOffset = -mBackgroundWithShadow.getHeight() * 0.5f; // Same as width. @@ -164,27 +164,32 @@ public class DotRenderer { mCirclePaint.setColor(params.dotColor); mCircleShadowPaint.setColor(params.shadowDotColor); - if (numNotifications > 9 && numNotifications < 100) { - canvas.drawRoundRect(new RectF(-mCircleRadius - 10, -mCircleRadius, mCircleRadius + 10, mCircleRadius + 3), 50, 50, mCircleShadowPaint); - canvas.drawRoundRect(new RectF(-mCircleRadius - 10, -mCircleRadius, mCircleRadius + 10, mCircleRadius), 50, 50, mCirclePaint); - } else if (numNotifications > 99 && numNotifications < 1000) { - canvas.drawRoundRect(new RectF(-mCircleRadius - 20, -mCircleRadius, mCircleRadius + 20, mCircleRadius + 3), 50, 50, mCircleShadowPaint); - canvas.drawRoundRect(new RectF(-mCircleRadius - 20, -mCircleRadius, mCircleRadius + 20, mCircleRadius), 50, 50, mCirclePaint); + + if (numNotifications >= 10 && numNotifications < 1000) { + canvas.drawRoundRect(new RectF(-mCircleRadius + 10, -mCircleRadius, mCircleRadius + 20, mCircleRadius), 50, 50, mCircleShadowPaint); + canvas.drawRoundRect(new RectF(-mCircleRadius + 10, -mCircleRadius, mCircleRadius + 20, mCircleRadius), 50, 50, mCirclePaint); } else { - canvas.drawCircle(0, 3, mCircleRadius, mCircleShadowPaint); - canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint); + canvas.drawCircle(5, 10, mCircleRadius, mCircleShadowPaint); + canvas.drawCircle(5, 10, mCircleRadius, mCirclePaint); } if (mDisplayCount && numNotifications > 0) { // Draw the numNotifications text mTextPaint.setColor(getCounterTextColor(Color.WHITE)); - mTextPaint.setTextSize(42f); mTextPaint.setTypeface(Typeface.DEFAULT_BOLD); + mTextPaint.setTextSize(32f); String text = numToNotation(numNotifications); mTextPaint.getTextBounds(text, 0, text.length(), mTextRect); - float x = (-mTextRect.width() / 2f - mTextRect.left) * getAdjustment(numNotifications); float y = mTextRect.height() / 2f - mTextRect.bottom; - canvas.drawText(text, x, y, mTextPaint); + if (numNotifications < 10) { + canvas.drawText(text, -4f, 22f, mTextPaint); + } else if (numNotifications < 100) { + canvas.drawText(text, -4f, y, mTextPaint); + } else if (numNotifications >= 1000) { + canvas.drawText(text, -14f, 20f, mTextPaint); + } else { + canvas.drawText(text, -14f, y, mTextPaint); + } } canvas.restore(); @@ -227,26 +232,4 @@ public class DotRenderer { @ViewDebug.ExportedProperty(category = "notification dot", formatToHexString = true) public int shadowDotColor; } - - /** - * An attempt to adjust digits to their perceived center, they were tuned with Roboto but should - * (hopefully) work with other OEM fonts as well. - */ - private float getAdjustment(int number) { - switch (number) { - case 1: - return 1.01f; - case 2: - return 0.99f; - case 3: - case 4: - case 6: - return 0.98f; - case 7: - return 1.02f; - case 9: - return 0.9f; - } - return 1f; - } } -- GitLab From 9384f988862cad1237a23d5e58c04ca21c7dcd31 Mon Sep 17 00:00:00 2001 From: althafvly Date: Tue, 16 Apr 2024 10:06:22 +0530 Subject: [PATCH 08/14] feat: adjust position of dots and add checks feat: Don't draw empty notification dot Signed-off-by: Saalim Quadri --- .../com/android/launcher3/icons/DotRenderer.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java index d02cce00..2a304715 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java +++ b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java @@ -152,11 +152,9 @@ public class DotRenderer { float dx = dotCenterX + offsetX; float dy = dotCenterY + offsetY - 15f; - if (numNotifications > 9 && numNotifications < 100) { - canvas.translate(dx - 3f, dy); - } else if (numNotifications > 99 && numNotifications < 1000) { - canvas.translate(dx + 6f, dy); - } else { + if (numNotifications > 9 && numNotifications < 1000) { + canvas.translate(dx - 17f, dy); + } else if (numNotifications > 0) { canvas.translate(dx - 12f, dy); } @@ -168,7 +166,7 @@ public class DotRenderer { if (numNotifications >= 10 && numNotifications < 1000) { canvas.drawRoundRect(new RectF(-mCircleRadius + 10, -mCircleRadius, mCircleRadius + 20, mCircleRadius), 50, 50, mCircleShadowPaint); canvas.drawRoundRect(new RectF(-mCircleRadius + 10, -mCircleRadius, mCircleRadius + 20, mCircleRadius), 50, 50, mCirclePaint); - } else { + } else if (numNotifications > 0) { canvas.drawCircle(5, 10, mCircleRadius, mCircleShadowPaint); canvas.drawCircle(5, 10, mCircleRadius, mCirclePaint); } @@ -184,11 +182,11 @@ public class DotRenderer { if (numNotifications < 10) { canvas.drawText(text, -4f, 22f, mTextPaint); } else if (numNotifications < 100) { - canvas.drawText(text, -4f, y, mTextPaint); + canvas.drawText(text, -3f, y, mTextPaint); } else if (numNotifications >= 1000) { canvas.drawText(text, -14f, 20f, mTextPaint); } else { - canvas.drawText(text, -14f, y, mTextPaint); + canvas.drawText(text, -12f, y, mTextPaint); } } -- GitLab From 23639dd6e172076617017a1ad916943a8fe48cb8 Mon Sep 17 00:00:00 2001 From: althafvly Date: Tue, 8 Apr 2025 21:16:24 +0530 Subject: [PATCH 09/14] systemui: Use msdllib and contextualeducationlib Signed-off-by: Saalim Quadri --- contextualeducationlib/.gitignore | 13 +++++++++ contextualeducationlib/build.gradle | 30 --------------------- contextualeducationlib/build.gradle.kts | 35 +++++++++++++++++++++++++ msdllib/.gitignore | 13 +++++++++ msdllib/build.gradle.kts | 35 +++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 30 deletions(-) create mode 100644 contextualeducationlib/.gitignore delete mode 100644 contextualeducationlib/build.gradle create mode 100644 contextualeducationlib/build.gradle.kts create mode 100644 msdllib/.gitignore create mode 100644 msdllib/build.gradle.kts diff --git a/contextualeducationlib/.gitignore b/contextualeducationlib/.gitignore new file mode 100644 index 00000000..6213826a --- /dev/null +++ b/contextualeducationlib/.gitignore @@ -0,0 +1,13 @@ +*.iml +.project +.classpath +.project.properties +gen/ +bin/ +.idea/ +.gradle/ +local.properties +gradle/ +build/ +gradlew* +.DS_Store diff --git a/contextualeducationlib/build.gradle b/contextualeducationlib/build.gradle deleted file mode 100644 index 909304dd..00000000 --- a/contextualeducationlib/build.gradle +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2024 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. - */ - -plugins { - id 'com.android.library' - id 'org.jetbrains.kotlin.android' -} -android { - namespace = "com.android.systemui.contextualeducation" - - sourceSets { - main { - java.srcDirs = ['src'] - manifest.srcFile 'AndroidManifest.xml' - } - } -} \ No newline at end of file diff --git a/contextualeducationlib/build.gradle.kts b/contextualeducationlib/build.gradle.kts new file mode 100644 index 00000000..ec5ca21d --- /dev/null +++ b/contextualeducationlib/build.gradle.kts @@ -0,0 +1,35 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin) +} + +android { + namespace = "com.android.systemui.contextualeducation" + compileSdk = 35 + + defaultConfig { + minSdk = 35 + vectorDrawables.useSupportLibrary = true + } + + lint { + abortOnError = true + checkReleaseBuilds = false + } + + sourceSets { + named("main") { + java.srcDirs("src") + manifest.srcFile("AndroidManifest.xml") + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } +} + +dependencies { + implementation(libs.androidx.core) +} diff --git a/msdllib/.gitignore b/msdllib/.gitignore new file mode 100644 index 00000000..6213826a --- /dev/null +++ b/msdllib/.gitignore @@ -0,0 +1,13 @@ +*.iml +.project +.classpath +.project.properties +gen/ +bin/ +.idea/ +.gradle/ +local.properties +gradle/ +build/ +gradlew* +.DS_Store diff --git a/msdllib/build.gradle.kts b/msdllib/build.gradle.kts new file mode 100644 index 00000000..ec488e8b --- /dev/null +++ b/msdllib/build.gradle.kts @@ -0,0 +1,35 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin) +} + +android { + namespace = "com.google.android.msdl" + compileSdk = 35 + + defaultConfig { + minSdk = 35 + vectorDrawables.useSupportLibrary = true + } + + lint { + abortOnError = true + checkReleaseBuilds = false + } + + sourceSets { + named("main") { + java.srcDirs("src") + manifest.srcFile("AndroidManifest.xml") + } + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } +} + +dependencies { + implementation(libs.androidx.core) +} -- GitLab From c9902d351d97baf8bd6afdc20567f8ba45110460 Mon Sep 17 00:00:00 2001 From: Kshitij Gupta Date: Fri, 4 Oct 2024 18:44:50 +0530 Subject: [PATCH 10/14] fix: Properly handle tablet notification icons [1/2] Signed-off-by: Saalim Quadri --- .../com/android/launcher3/icons/DotRenderer.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java index 2a304715..1e0db132 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java +++ b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java @@ -130,6 +130,11 @@ public class DotRenderer { * Draw a circle on top of the canvas according to the given params. */ public void draw(Canvas canvas, DrawParams params, int numNotifications) { + draw(canvas, params, numNotifications, false, false); + } + + public void draw(Canvas canvas, DrawParams params, int numNotifications, + boolean isTablet, boolean isTaskbar) { if (params == null) { Log.e(TAG, "Invalid null argument(s) passed in call to draw."); return; @@ -149,8 +154,8 @@ public class DotRenderer { float offsetY = Math.max(0, canvasBounds.top - (dotCenterY + mBitmapOffset)); // We draw the dot relative to its center. - float dx = dotCenterX + offsetX; - float dy = dotCenterY + offsetY - 15f; + float dx = dotCenterX + offsetX - (isTablet && !isTaskbar ? 15f : 0f); + float dy = dotCenterY + offsetY - (isTablet ? 20f : 15f); if (numNotifications > 9 && numNotifications < 1000) { canvas.translate(dx - 17f, dy); @@ -158,12 +163,13 @@ public class DotRenderer { canvas.translate(dx - 12f, dy); } - canvas.scale(params.scale, params.scale); + float scale = params.scale * (isTablet && !isTaskbar ? 0.75f : 1f); + canvas.scale(scale, scale); mCirclePaint.setColor(params.dotColor); mCircleShadowPaint.setColor(params.shadowDotColor); - if (numNotifications >= 10 && numNotifications < 1000) { + if (!isTaskbar && numNotifications >= 10 && numNotifications < 1000) { canvas.drawRoundRect(new RectF(-mCircleRadius + 10, -mCircleRadius, mCircleRadius + 20, mCircleRadius), 50, 50, mCircleShadowPaint); canvas.drawRoundRect(new RectF(-mCircleRadius + 10, -mCircleRadius, mCircleRadius + 20, mCircleRadius), 50, 50, mCirclePaint); } else if (numNotifications > 0) { @@ -171,7 +177,7 @@ public class DotRenderer { canvas.drawCircle(5, 10, mCircleRadius, mCirclePaint); } - if (mDisplayCount && numNotifications > 0) { + if (mDisplayCount && numNotifications > 0 && !isTaskbar) { // Draw the numNotifications text mTextPaint.setColor(getCounterTextColor(Color.WHITE)); mTextPaint.setTypeface(Typeface.DEFAULT_BOLD); -- GitLab From b6f66c031a206a1fbbd4cf83f6753847c8c38201 Mon Sep 17 00:00:00 2001 From: Kshitij Gupta Date: Wed, 27 Nov 2024 12:56:17 +0530 Subject: [PATCH 11/14] fix: DotRenderer: Tune further for tablet Signed-off-by: Saalim Quadri --- .../android/launcher3/icons/DotRenderer.java | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java index 1e0db132..979c5393 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java +++ b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java @@ -42,7 +42,7 @@ public class DotRenderer { // The dot size is defined as a percentage of the app icon size. private static final float SIZE_PERCENTAGE = 0.21f; - private static final float SIZE_PERCENTAGE_WITH_COUNT = 0.28f; + private static final float SIZE_PERCENTAGE_WITH_COUNT = 0.24f; // The max number to draw on dots private static final int MAX_COUNT = 999; @@ -148,19 +148,22 @@ public class DotRenderer { // 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 dx = dotCenterX + offsetX - (isTablet && !isTaskbar ? 15f : 0f); - float dy = dotCenterY + offsetY - (isTablet ? 20f : 15f); - - if (numNotifications > 9 && numNotifications < 1000) { - canvas.translate(dx - 17f, dy); + float availableWidth = (float) (canvasBounds.width() - iconBounds.width()) / 2; + + // We check if the dot is too close to the edge of the screen and nudge it if necessary. + boolean mustNudge = isTablet && availableWidth < mCircleRadius; + float dx = mustNudge? mCircleRadius: dotCenterX; + + // Taskbar icons are too small and do not show the dot text. + // We will displace the dot to the correct position. + if (isTaskbar) { + canvas.translate(dotCenterX - mCircleRadius, dotCenterY - mCircleRadius); + } else if (numNotifications > 9 && numNotifications < 1000) { + canvas.translate(dx - (mustNudge ? 12f : 17f), dotCenterY); } else if (numNotifications > 0) { - canvas.translate(dx - 12f, dy); + canvas.translate(dx - 12f, dotCenterY); } float scale = params.scale * (isTablet && !isTaskbar ? 0.75f : 1f); -- GitLab From 6c90ac100377060e44df65f5b14bce3cd1c912ee Mon Sep 17 00:00:00 2001 From: Kshitij Gupta Date: Mon, 27 Jan 2025 14:55:10 +0530 Subject: [PATCH 12/14] fix: Revert badge size and position to pre-tablet [2/2] Signed-off-by: Saalim Quadri --- .../android/launcher3/icons/DotRenderer.java | 56 ++++++++++++------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java index 979c5393..149dfc9a 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java +++ b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java @@ -42,7 +42,8 @@ public class DotRenderer { // The dot size is defined as a percentage of the app icon size. private static final float SIZE_PERCENTAGE = 0.21f; - private static final float SIZE_PERCENTAGE_WITH_COUNT = 0.24f; + private static final float SIZE_PERCENTAGE_WITH_COUNT = 0.28f; + private static final float SIZE_PERCENTAGE_WITH_COUNT_TABLET = 0.24f; // The max number to draw on dots private static final int MAX_COUNT = 999; @@ -65,12 +66,12 @@ public class DotRenderer { private boolean mDisplayCount; public DotRenderer(int iconSizePx, Path iconShapePath, int pathSize) { - this(iconSizePx, iconShapePath, pathSize, false, null); + this(iconSizePx, iconShapePath, pathSize, false, null, false); } - public DotRenderer(int iconSizePx, Path iconShapePath, int pathSize, Boolean displayCount, Typeface typeface) { + public DotRenderer(int iconSizePx, Path iconShapePath, int pathSize, Boolean displayCount, Typeface typeface, boolean isTablet) { mDisplayCount = displayCount; - int size = Math.round((displayCount ? SIZE_PERCENTAGE_WITH_COUNT : SIZE_PERCENTAGE) * iconSizePx); + int size = Math.round((displayCount ? (isTablet ? SIZE_PERCENTAGE_WITH_COUNT_TABLET : SIZE_PERCENTAGE_WITH_COUNT) : SIZE_PERCENTAGE) * iconSizePx); if (size <= 0) { size = MIN_DOT_SIZE; } @@ -149,21 +150,38 @@ public class DotRenderer { // Ensure dot fits entirely in canvas clip bounds. Rect canvasBounds = canvas.getClipBounds(); - // We draw the dot relative to its center. - float availableWidth = (float) (canvasBounds.width() - iconBounds.width()) / 2; - - // We check if the dot is too close to the edge of the screen and nudge it if necessary. - boolean mustNudge = isTablet && availableWidth < mCircleRadius; - float dx = mustNudge? mCircleRadius: dotCenterX; - - // Taskbar icons are too small and do not show the dot text. - // We will displace the dot to the correct position. - if (isTaskbar) { - canvas.translate(dotCenterX - mCircleRadius, dotCenterY - mCircleRadius); - } else if (numNotifications > 9 && numNotifications < 1000) { - canvas.translate(dx - (mustNudge ? 12f : 17f), dotCenterY); - } else if (numNotifications > 0) { - canvas.translate(dx - 12f, dotCenterY); + if (isTablet) { + // We draw the dot relative to its center. + float availableWidth = (float) (canvasBounds.width() - iconBounds.width()) / 2; + + // We check if the dot is too close to the edge of the screen and nudge it if necessary. + boolean mustNudge = availableWidth < mCircleRadius; + float dx = mustNudge ? mCircleRadius : dotCenterX; + + // Taskbar icons are too small and do not show the dot text. + // We will displace the dot to the correct position. + if (isTaskbar) { + canvas.translate(dotCenterX - mCircleRadius, dotCenterY - mCircleRadius); + } else if (numNotifications > 9 && numNotifications < 1000) { + canvas.translate(dx - (mustNudge ? 12f : 17f), dotCenterY); + } else if (numNotifications > 0) { + canvas.translate(dx - 12f, dotCenterY); + } + } else { + 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 dx = dotCenterX + offsetX; + float dy = dotCenterY + offsetY - 15f; + + if (numNotifications > 9 && numNotifications < 1000) { + canvas.translate(dx - 17f, dy); + } else if (numNotifications > 0) { + canvas.translate(dx - 12f, dy); + } } float scale = params.scale * (isTablet && !isTaskbar ? 0.75f : 1f); -- GitLab From 08aeb58a1ee48e0fd8b50e3b334c870ae887ba5a Mon Sep 17 00:00:00 2001 From: Saalim Quadri Date: Tue, 25 Mar 2025 09:58:24 +0530 Subject: [PATCH 13/14] fix: Return intended notification count Signed-off-by: Saalim Quadri --- .../src/com/android/launcher3/icons/DotRenderer.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java index 149dfc9a..0c163a80 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java +++ b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java @@ -223,8 +223,16 @@ public class DotRenderer { private String numToNotation(int num) { if (num < 1000) { return String.valueOf(num); + } else if (num < 10000) { + int t = (num/100) * 100; + double v = t / 1000.0; + if (t % 1000 == 0) { + return String.format("%dk", (int)v); + } else { + return String.format("%.1fk", v); + } } else { - return num / 1000 + "k"; + return "10k+"; } } -- GitLab From 4518b0598becfc57257a614d79c8ece2fe48cee8 Mon Sep 17 00:00:00 2001 From: Saalim Quadri Date: Wed, 26 Mar 2025 01:25:13 +0530 Subject: [PATCH 14/14] (fix): Render notificationBadges as intended Signed-off-by: Saalim Quadri --- .../android/launcher3/icons/DotRenderer.java | 61 +++++++++++-------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java index 0c163a80..c100220c 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java +++ b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java @@ -162,10 +162,8 @@ public class DotRenderer { // We will displace the dot to the correct position. if (isTaskbar) { canvas.translate(dotCenterX - mCircleRadius, dotCenterY - mCircleRadius); - } else if (numNotifications > 9 && numNotifications < 1000) { - canvas.translate(dx - (mustNudge ? 12f : 17f), dotCenterY); - } else if (numNotifications > 0) { - canvas.translate(dx - 12f, dotCenterY); + } else { + canvas.translate(dx, dotCenterY - 10f); } } else { float offsetX = params.leftAlign @@ -177,11 +175,7 @@ public class DotRenderer { float dx = dotCenterX + offsetX; float dy = dotCenterY + offsetY - 15f; - if (numNotifications > 9 && numNotifications < 1000) { - canvas.translate(dx - 17f, dy); - } else if (numNotifications > 0) { - canvas.translate(dx - 12f, dy); - } + canvas.translate(dx, dy); } float scale = params.scale * (isTablet && !isTaskbar ? 0.75f : 1f); @@ -190,12 +184,8 @@ public class DotRenderer { mCirclePaint.setColor(params.dotColor); mCircleShadowPaint.setColor(params.shadowDotColor); - if (!isTaskbar && numNotifications >= 10 && numNotifications < 1000) { - canvas.drawRoundRect(new RectF(-mCircleRadius + 10, -mCircleRadius, mCircleRadius + 20, mCircleRadius), 50, 50, mCircleShadowPaint); - canvas.drawRoundRect(new RectF(-mCircleRadius + 10, -mCircleRadius, mCircleRadius + 20, mCircleRadius), 50, 50, mCirclePaint); - } else if (numNotifications > 0) { - canvas.drawCircle(5, 10, mCircleRadius, mCircleShadowPaint); - canvas.drawCircle(5, 10, mCircleRadius, mCirclePaint); + if (!isTaskbar) { + drawBadge(canvas, numNotifications, isTablet); } if (mDisplayCount && numNotifications > 0 && !isTaskbar) { @@ -206,20 +196,43 @@ public class DotRenderer { String text = numToNotation(numNotifications); mTextPaint.getTextBounds(text, 0, text.length(), mTextRect); float y = mTextRect.height() / 2f - mTextRect.bottom; - if (numNotifications < 10) { - canvas.drawText(text, -4f, 22f, mTextPaint); - } else if (numNotifications < 100) { - canvas.drawText(text, -3f, y, mTextPaint); - } else if (numNotifications >= 1000) { - canvas.drawText(text, -14f, 20f, mTextPaint); - } else { - canvas.drawText(text, -12f, y, mTextPaint); - } + canvas.drawText(text, -10f, 21f, mTextPaint); } canvas.restore(); } + private void drawBadge(Canvas canvas, int numNotifications, boolean isTablet) { + if (numNotifications < 10) { + float centerY = 9f; + canvas.drawCircle(0, centerY, mCircleRadius, mCircleShadowPaint); + canvas.drawCircle(0, centerY, mCircleRadius, mCirclePaint); + } else { + RectF rect; + if (numNotifications < 100) { + rect = new RectF(-mCircleRadius + 2, -mCircleRadius + 10, mCircleRadius + 17, mCircleRadius + 8); + } else if (numNotifications < 1000) { + float b = (isTablet) ? mCircleRadius + 10: mCircleRadius + 8; + rect = new RectF(-mCircleRadius -2, -mCircleRadius + 10, mCircleRadius + 40, b); + } else if (numNotifications < 9999) { + int tt = (numNotifications/100)*100; + if (tt % 1000 == 0) { // num like 2000,3000,4000 + rect = new RectF(-mCircleRadius + 2, -mCircleRadius + 10, mCircleRadius + 17, mCircleRadius + 8); + } else if (isTablet) { // 999 < num < 10000 + rect = new RectF(-mCircleRadius - 2, -mCircleRadius+ 10, mCircleRadius + 40, mCircleRadius + 10); + } else { + rect = new RectF(-mCircleRadius + 2, -mCircleRadius + 10, mCircleRadius + 45, mCircleRadius + 10); + } + } else { // num >= 10000 + float t = (isTablet) ? -mCircleRadius + 11: -mCircleRadius + 10; + float b = (isTablet) ? mCircleRadius + 11: mCircleRadius + 10; + rect = new RectF(-mCircleRadius + 2, t, mCircleRadius + 45, b); + } + canvas.drawRoundRect(rect, 50, 50, mCircleShadowPaint); + canvas.drawRoundRect(rect, 50, 50, mCirclePaint); + } + } + private String numToNotation(int num) { if (num < 1000) { return String.valueOf(num); -- GitLab