summaryrefslogtreecommitdiff
path: root/src/com/android/launcher3/BubbleTextView.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/BubbleTextView.java')
-rw-r--r--src/com/android/launcher3/BubbleTextView.java217
1 files changed, 188 insertions, 29 deletions
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index ddbd425b55..1f1d57adc9 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;
@@ -32,6 +33,8 @@ import android.graphics.PointF;
import android.graphics.Rect;
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;
@@ -65,9 +68,12 @@ import com.android.launcher3.model.data.SearchActionItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.BubbleTextHolder;
import com.android.launcher3.views.IconLabelDotView;
import java.text.NumberFormat;
+import java.util.HashMap;
+import java.util.Locale;
/**
* TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan
@@ -84,12 +90,19 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
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.05f;
+ 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;
private final PointF mTranslationForReorderBounce = new PointF(0, 0);
private final PointF mTranslationForReorderPreview = new PointF(0, 0);
+ private float mTranslationXForTaskbarAlignmentAnimation = 0f;
+
+ private final PointF mTranslationForMoveFromCenterAnimation = new PointF(0, 0);
+
private float mScaleForReorderBounce = 1f;
private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY
@@ -128,6 +141,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
private final CheckLongPressHelper mLongPressHelper;
private final boolean mLayoutHorizontal;
+ private final boolean mIsRtl;
private final int mIconSize;
@ViewDebug.ExportedProperty(category = "launcher")
@@ -155,6 +169,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
private HandlerRunnable mIconLoadRequest;
private boolean mEnableIconUpdateAnimation = false;
+ private BubbleTextHolder mBubbleTextHolder;
public BubbleTextView(Context context) {
this(context, null, 0);
@@ -171,6 +186,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.BubbleTextView, defStyle, 0);
mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);
+ mIsRtl = (getResources().getConfiguration().getLayoutDirection()
+ == View.LAYOUT_DIRECTION_RTL);
DeviceProfile grid = mActivity.getDeviceProfile();
mDisplay = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);
@@ -254,9 +271,27 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
@UiThread
public void applyFromWorkspaceItem(WorkspaceItemInfo info) {
+ applyFromWorkspaceItem(info, /* animate = */ false, /* staggerIndex = */ 0);
+ }
+
+ @UiThread
+ public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean animate, int staggerIndex) {
applyFromWorkspaceItem(info, false);
}
+ /**
+ * Returns whether the newInfo differs from the current getTag().
+ */
+ public boolean shouldAnimateIconChange(WorkspaceItemInfo newInfo) {
+ WorkspaceItemInfo oldInfo = getTag() instanceof WorkspaceItemInfo
+ ? (WorkspaceItemInfo) getTag()
+ : null;
+ boolean changedIcons = oldInfo != null && oldInfo.getTargetComponent() != null
+ && newInfo.getTargetComponent() != null
+ && !oldInfo.getTargetComponent().equals(newInfo.getTargetComponent());
+ return changedIcons && isShown();
+ }
+
@Override
public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
if (delegate instanceof LauncherAccessibilityDelegate) {
@@ -272,7 +307,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
@UiThread
public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean promiseStateChanged) {
applyIconAndLabel(info);
- setTag(info);
+ setItemInfo(info);
applyLoadingState(promiseStateChanged);
applyDotState(info, false /* animate */);
setDownloadStateContentDescription(info, info.getProgressLevel());
@@ -283,7 +318,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
applyIconAndLabel(info);
// We don't need to check the info since it's not a WorkspaceItemInfo
- super.setTag(info);
+ setItemInfo(info);
+
// Verify high res immediately
verifyHighRes();
@@ -302,7 +338,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
public void applyFromItemInfoWithIcon(ItemInfoWithIcon info) {
applyIconAndLabel(info);
// We don't need to check the info since it's not a WorkspaceItemInfo
- super.setTag(info);
+ setItemInfo(info);
// Verify high res immediately
verifyHighRes();
@@ -310,18 +346,22 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
setDownloadStateContentDescription(info, info.getProgressLevel());
}
- /**
- * Apply label and tag using a {@link SearchActionItemInfo}
- */
- @UiThread
- public void applyFromSearchActionItemInfo(SearchActionItemInfo searchActionItemInfo) {
- applyIconAndLabel(searchActionItemInfo);
- setTag(searchActionItemInfo);
+ private void setItemInfo(ItemInfoWithIcon itemInfo) {
+ setTag(itemInfo);
+ if (mBubbleTextHolder != null) {
+ mBubbleTextHolder.onItemInfoUpdated(itemInfo);
+ }
+ }
+
+ public void setBubbleTextHolder(
+ BubbleTextHolder bubbleTextHolder) {
+ mBubbleTextHolder = bubbleTextHolder;
}
@UiThread
protected void applyIconAndLabel(ItemInfoWithIcon info) {
- boolean useTheme = mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER;
+ boolean useTheme = mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER
+ || mDisplay == DISPLAY_TASKBAR;
FastBitmapDrawable iconDrawable = info.newIcon(getContext(), useTheme);
mDotParams.color = IconPalette.getMutedColor(iconDrawable.getIconColor(), 0.54f);
@@ -388,6 +428,10 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
* Returns true if the touch down at the provided position be ignored
*/
protected boolean shouldIgnoreTouchDown(float x, float y) {
+ if (mDisplay == DISPLAY_TASKBAR) {
+ // Allow touching within padding on taskbar, given icon sizes are smaller.
+ return false;
+ }
return y < getPaddingTop()
|| x < getPaddingLeft()
|| y > getHeight() - getPaddingBottom()
@@ -407,7 +451,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
}
}
- void clearPressedBackground() {
+ public void clearPressedBackground() {
setPressed(false);
setStayPressed(false);
}
@@ -424,6 +468,75 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
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);
@@ -471,19 +584,29 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
return mDotInfo != null;
}
+ /**
+ * Get the icon bounds on the view depending on the layout type.
+ */
public void getIconBounds(Rect outBounds) {
- getIconBounds(this, outBounds, mIconSize);
+ getIconBounds(mIconSize, outBounds);
}
- public static void getIconBounds(View iconView, Rect outBounds, int iconSize) {
- int top = iconView.getPaddingTop();
- int left = (iconView.getWidth() - iconSize) / 2;
- int right = left + iconSize;
- int bottom = top + iconSize;
- outBounds.set(left, top, right, bottom);
+ /**
+ * Get the icon bounds on the view depending on the layout type.
+ */
+ public void getIconBounds(int iconSize, Rect outBounds) {
+ Utilities.setRectToViewCenter(this, iconSize, outBounds);
+ if (mLayoutHorizontal) {
+ if (mIsRtl) {
+ outBounds.offsetTo(getWidth() - iconSize - getPaddingRight(), outBounds.top);
+ } else {
+ outBounds.offsetTo(getPaddingLeft(), outBounds.top);
+ }
+ } else {
+ outBounds.offsetTo(outBounds.left, getPaddingTop());
+ }
}
-
/**
* Sets whether to vertically center the content.
*/
@@ -668,8 +791,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
itemInfo.contentDescription));
} else if (hasDot()) {
int count = mDotInfo.getNotificationCount();
- setContentDescription(getContext().getResources().getQuantityString(
- R.plurals.dotted_app_label, count, itemInfo.contentDescription, count));
+ setContentDescription(
+ getAppLabelPluralString(itemInfo.contentDescription.toString(), count));
} else {
setContentDescription(itemInfo.contentDescription);
}
@@ -769,7 +892,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
} else if (info instanceof PackageItemInfo) {
applyFromItemInfoWithIcon((PackageItemInfo) info);
} else if (info instanceof SearchActionItemInfo) {
- applyFromSearchActionItemInfo((SearchActionItemInfo) info);
+ applyFromItemInfoWithIcon((SearchActionItemInfo) info);
}
mDisableRelayout = false;
@@ -799,8 +922,11 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
}
private void updateTranslation() {
- super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x);
- super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y);
+ super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x
+ + mTranslationForMoveFromCenterAnimation.x
+ + mTranslationXForTaskbarAlignmentAnimation);
+ super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y
+ + mTranslationForMoveFromCenterAnimation.y);
}
public void setReorderBounceOffset(float x, float y) {
@@ -833,6 +959,29 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
return mScaleForReorderBounce;
}
+ /**
+ * Sets translation values for move from center animation
+ */
+ public void setTranslationForMoveFromCenterAnimation(float x, float y) {
+ mTranslationForMoveFromCenterAnimation.set(x, y);
+ updateTranslation();
+ }
+
+ /**
+ * Sets translationX for taskbar to launcher alignment animation
+ */
+ public void setTranslationXForTaskbarAlignmentAnimation(float translationX) {
+ mTranslationXForTaskbarAlignmentAnimation = translationX;
+ updateTranslation();
+ }
+
+ /**
+ * Returns translationX value for taskbar to launcher alignment animation
+ */
+ public float getTranslationXForTaskbarAlignmentAnimation() {
+ return mTranslationXForTaskbarAlignmentAnimation;
+ }
+
public View getView() {
return this;
}
@@ -844,8 +993,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
@Override
public void getWorkspaceVisualDragBounds(Rect bounds) {
- DeviceProfile grid = mActivity.getDeviceProfile();
- BubbleTextView.getIconBounds(this, bounds, grid.iconSizePx);
+ getIconBounds(mIconSize, bounds);
}
private int getIconSizeForDisplay(int display) {
@@ -853,15 +1001,16 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
switch (display) {
case DISPLAY_ALL_APPS:
return grid.allAppsIconSizePx;
- case DISPLAY_WORKSPACE:
case DISPLAY_FOLDER:
+ return grid.folderChildIconSizePx;
+ case DISPLAY_WORKSPACE:
default:
return grid.iconSizePx;
}
}
public void getSourceVisualDragBounds(Rect bounds) {
- BubbleTextView.getIconBounds(this, bounds, getIconSizeForDisplay(mDisplay));
+ getIconBounds(mIconSize, bounds);
}
@Override
@@ -884,4 +1033,14 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
setCompoundDrawables(null, newIcon, null, null);
}
}
+
+ private String getAppLabelPluralString(String appName, int notificationCount) {
+ MessageFormat icuCountFormat = new MessageFormat(
+ getResources().getString(R.string.dotted_app_label),
+ Locale.getDefault());
+ HashMap<String, Object> args = new HashMap();
+ args.put("app_name", appName);
+ args.put("count", notificationCount);
+ return icuCountFormat.format(args);
+ }
}