Index: chrome/android/java_staging/src/org/chromium/chrome/browser/snackbar/TemplatePreservingTextView.java |
diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/snackbar/TemplatePreservingTextView.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/snackbar/TemplatePreservingTextView.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..36f4759d422ccde16430d13b43f0edeb6704d5bd |
--- /dev/null |
+++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/snackbar/TemplatePreservingTextView.java |
@@ -0,0 +1,91 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser.snackbar; |
+ |
+import android.content.Context; |
+import android.support.v7.widget.AppCompatTextView; |
+import android.text.TextPaint; |
+import android.text.TextUtils; |
+import android.text.TextUtils.TruncateAt; |
+import android.util.AttributeSet; |
+import android.widget.TextView; |
+ |
+/** |
+ * A {@link AppCompatTextView} that properly clips content within a template, instead of clipping |
+ * the templated content. |
+ */ |
+public class TemplatePreservingTextView extends AppCompatTextView { |
+ private static final String DEFAULT_TEMPLATE = "%1$s"; |
+ private String mTemplate; |
+ private CharSequence mContent; |
+ |
+ /** |
+ * Builds an instance of an {@link TemplatePreservingTextView}. |
+ * @param context A {@link Context} instance to build this {@link TextView} in. |
+ * @param attrs An {@link AttributeSet} instance. |
+ */ |
+ public TemplatePreservingTextView(Context context, AttributeSet attrs) { |
+ super(context, attrs); |
+ } |
+ |
+ /** |
+ * Update template for undo text |
+ * @param template Template format string (eg. "Close %s") |
+ */ |
+ public void setTemplate(String template) { |
+ mTemplate = TextUtils.isEmpty(template) ? DEFAULT_TEMPLATE : template; |
+ } |
+ |
+ /** |
+ * This will take {@code text} and apply it to the internal template, building a new |
+ * {@link String} to set. This {code text} will be automatically truncated to fit within |
+ * the template as best as possible, making sure the template does not get clipped. |
+ */ |
+ @Override |
+ public void setText(CharSequence text, BufferType type) { |
+ final int availWidth = getWidth() - getPaddingLeft() - getPaddingRight(); |
+ setClippedText(text, availWidth); |
+ } |
+ |
+ @Override |
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
+ final int availWidth = |
+ MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); |
+ setClippedText(mContent, availWidth); |
+ |
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec); |
+ } |
+ |
+ private void setClippedText(CharSequence text, int availWidth) { |
+ mContent = text != null ? text : ""; |
+ |
+ if (mTemplate == null) { |
+ super.setText(mContent, BufferType.SPANNABLE); |
+ return; |
+ } |
+ |
+ final TextPaint paint = getPaint(); |
+ |
+ // Calculate the width the template takes. |
+ final String emptyTemplate = String.format(mTemplate, ""); |
+ final float emptyTemplateWidth = paint.measureText(emptyTemplate); |
+ |
+ // Calculate the available width for the content. |
+ final float contentWidth = Math.max(availWidth - emptyTemplateWidth, 0.f); |
+ |
+ // Ellipsize the content to the available width. |
+ CharSequence clipped = TextUtils.ellipsize(mContent, paint, contentWidth, TruncateAt.END); |
+ |
+ // Build the full string, which should fit within availWidth. |
+ String finalContent = String.format(mTemplate, clipped); |
+ |
+ // BufferType.SPANNABLE is required so that TextView.getIterableTextForAccessibility() |
+ // doesn't call our custom setText(). See crbug.com/449311 |
+ super.setText(finalContent, BufferType.SPANNABLE); |
+ |
+ // Set the content description to the non-ellipsized text |
+ setContentDescription(String.format(mTemplate, mContent)); |
+ } |
+} |