Adding support for dynamic letter spacing for icon labels
Bug: 201697936
Test: Manual
Change-Id: I0ab81291c40afcac30c5caf7b5638889908775f8
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 9da2b79..eec6e0a 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -16,6 +16,7 @@
package com.android.launcher3;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_ICON_LABEL_AUTO_SCALING;
import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
@@ -33,6 +34,7 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.icu.text.MessageFormat;
+import android.text.TextPaint;
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.util.Property;
@@ -88,6 +90,9 @@
private static final int DISPLAY_SEARCH_RESULT = 6;
private static final int DISPLAY_SEARCH_RESULT_SMALL = 7;
+ private static final float MIN_LETTER_SPACING = -0.5f;
+ private static final int MAX_SEARCH_LOOP_COUNT = 20;
+
private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
private static final float HIGHLIGHT_SCALE = 1.16f;
@@ -454,6 +459,75 @@
return result;
}
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ checkForEllipsis();
+ }
+
+ @Override
+ protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
+ super.onTextChanged(text, start, lengthBefore, lengthAfter);
+ checkForEllipsis();
+ }
+
+ private void checkForEllipsis() {
+ if (!ENABLE_ICON_LABEL_AUTO_SCALING.get()) {
+ return;
+ }
+ float width = getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight();
+ if (width <= 0) {
+ return;
+ }
+ setLetterSpacing(0);
+
+ String text = getText().toString();
+ TextPaint paint = getPaint();
+ if (paint.measureText(text) < width) {
+ return;
+ }
+
+ float spacing = findBestSpacingValue(paint, text, width, MIN_LETTER_SPACING);
+ // Reset the paint value so that the call to TextView does appropriate diff.
+ paint.setLetterSpacing(0);
+ setLetterSpacing(spacing);
+ }
+
+ /**
+ * Find the appropriate text spacing to display the provided text
+ * @param paint the paint used by the text view
+ * @param text the text to display
+ * @param allowedWidthPx available space to render the text
+ * @param minSpacingEm minimum spacing allowed between characters
+ * @return the final textSpacing value
+ *
+ * @see #setLetterSpacing(float)
+ */
+ private float findBestSpacingValue(TextPaint paint, String text, float allowedWidthPx,
+ float minSpacingEm) {
+ paint.setLetterSpacing(minSpacingEm);
+ if (paint.measureText(text) > allowedWidthPx) {
+ // If there is no result at high limit, we can do anything more
+ return minSpacingEm;
+ }
+
+ float lowLimit = 0;
+ float highLimit = minSpacingEm;
+
+ for (int i = 0; i < MAX_SEARCH_LOOP_COUNT; i++) {
+ float value = (lowLimit + highLimit) / 2;
+ paint.setLetterSpacing(value);
+ if (paint.measureText(text) < allowedWidthPx) {
+ highLimit = value;
+ } else {
+ lowLimit = value;
+ }
+ }
+
+ // At the end error on the higher side
+ return highLimit;
+ }
+
@SuppressWarnings("wrongcall")
protected void drawWithoutDot(Canvas canvas) {
super.onDraw(canvas);
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 0acafc0..f41c19b 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -269,6 +269,10 @@
"ENABLE_BACK_SWIPE_HOME_ANIMATION", true,
"Enables home animation to icon when user swipes back.");
+ public static final BooleanFlag ENABLE_ICON_LABEL_AUTO_SCALING = getDebugFlag(
+ "ENABLE_ICON_LABEL_AUTO_SCALING", true,
+ "Enables scaling/spacing for icon labels to make more characters visible");
+
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {