diff options
Diffstat (limited to 'src/com/android/launcher3/BubbleTextView.java')
-rw-r--r-- | src/com/android/launcher3/BubbleTextView.java | 217 |
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); + } } |