diff options
author | Dianne Hackborn <hackbod@google.com> | 2012-04-13 16:47:38 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-04-13 16:47:38 -0700 |
commit | 2a07308ed27b7323377142c69975b019244e38d7 (patch) | |
tree | 5d3b5b2bf0b85fb2af963173fbdfcfb66aeeccca | |
parent | a94afeb5b3c3c95c6d2cf0cd4d69587aba569cfb (diff) | |
parent | acb69bb909d098cea284df47d794c17171d84c91 (diff) | |
download | frameworks_base-2a07308ed27b7323377142c69975b019244e38d7.zip frameworks_base-2a07308ed27b7323377142c69975b019244e38d7.tar.gz frameworks_base-2a07308ed27b7323377142c69975b019244e38d7.tar.bz2 |
Merge "Add direct support for HTML formatted text in ClipData etc."
-rw-r--r-- | api/current.txt | 9 | ||||
-rw-r--r-- | core/java/android/content/ClipData.java | 299 | ||||
-rw-r--r-- | core/java/android/content/ClipDescription.java | 5 | ||||
-rw-r--r-- | core/java/android/content/ContentResolver.java | 2 | ||||
-rw-r--r-- | core/java/android/content/Intent.java | 36 | ||||
-rw-r--r-- | core/java/android/text/Html.java | 11 | ||||
-rw-r--r-- | core/java/android/widget/Editor.java | 2 | ||||
-rw-r--r-- | core/java/android/widget/TextView.java | 2 |
8 files changed, 348 insertions, 18 deletions
diff --git a/api/current.txt b/api/current.txt index eacce52..d0ab197 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4833,6 +4833,7 @@ package android.content { method public android.content.ClipDescription getDescription(); method public android.content.ClipData.Item getItemAt(int); method public int getItemCount(); + method public static android.content.ClipData newHtmlText(java.lang.CharSequence, java.lang.CharSequence, java.lang.String); method public static android.content.ClipData newIntent(java.lang.CharSequence, android.content.Intent); method public static android.content.ClipData newPlainText(java.lang.CharSequence, java.lang.CharSequence); method public static android.content.ClipData newRawUri(java.lang.CharSequence, android.net.Uri); @@ -4843,10 +4844,15 @@ package android.content { public static class ClipData.Item { ctor public ClipData.Item(java.lang.CharSequence); + ctor public ClipData.Item(java.lang.CharSequence, java.lang.String); ctor public ClipData.Item(android.content.Intent); ctor public ClipData.Item(android.net.Uri); ctor public ClipData.Item(java.lang.CharSequence, android.content.Intent, android.net.Uri); + ctor public ClipData.Item(java.lang.CharSequence, java.lang.String, android.content.Intent, android.net.Uri); + method public java.lang.String coerceToHtmlText(android.content.Context); + method public java.lang.CharSequence coerceToStyledText(android.content.Context); method public java.lang.CharSequence coerceToText(android.content.Context); + method public java.lang.String getHtmlText(); method public android.content.Intent getIntent(); method public java.lang.CharSequence getText(); method public android.net.Uri getUri(); @@ -4864,6 +4870,7 @@ package android.content { method public boolean hasMimeType(java.lang.String); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; + field public static final java.lang.String MIMETYPE_TEXT_HTML = "text/html"; field public static final java.lang.String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent"; field public static final java.lang.String MIMETYPE_TEXT_PLAIN = "text/plain"; field public static final java.lang.String MIMETYPE_TEXT_URILIST = "text/uri-list"; @@ -5701,6 +5708,7 @@ package android.content { field public static final int EXTRA_DOCK_STATE_UNDOCKED = 0; // 0x0 field public static final java.lang.String EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP"; field public static final java.lang.String EXTRA_EMAIL = "android.intent.extra.EMAIL"; + field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT"; field public static final java.lang.String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS"; field public static final java.lang.String EXTRA_INSTALLER_PACKAGE_NAME = "android.intent.extra.INSTALLER_PACKAGE_NAME"; field public static final java.lang.String EXTRA_INTENT = "android.intent.extra.INTENT"; @@ -20406,6 +20414,7 @@ package android.text { } public class Html { + method public static java.lang.String escapeHtml(java.lang.CharSequence); method public static android.text.Spanned fromHtml(java.lang.String); method public static android.text.Spanned fromHtml(java.lang.String, android.text.Html.ImageGetter, android.text.Html.TagHandler); method public static java.lang.String toHtml(android.text.Spanned); diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java index a655dd4..1866830 100644 --- a/core/java/android/content/ClipData.java +++ b/core/java/android/content/ClipData.java @@ -21,7 +21,12 @@ import android.graphics.Bitmap; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import android.text.Html; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.Spanned; import android.text.TextUtils; +import android.text.style.URLSpan; import android.util.Log; import java.io.FileInputStream; @@ -144,6 +149,8 @@ import java.util.ArrayList; public class ClipData implements Parcelable { static final String[] MIMETYPES_TEXT_PLAIN = new String[] { ClipDescription.MIMETYPE_TEXT_PLAIN }; + static final String[] MIMETYPES_TEXT_HTML = new String[] { + ClipDescription.MIMETYPE_TEXT_HTML }; static final String[] MIMETYPES_TEXT_URILIST = new String[] { ClipDescription.MIMETYPE_TEXT_URILIST }; static final String[] MIMETYPES_TEXT_INTENT = new String[] { @@ -176,6 +183,7 @@ public class ClipData implements Parcelable { */ public static class Item { final CharSequence mText; + final String mHtmlText; final Intent mIntent; final Uri mUri; @@ -184,6 +192,20 @@ public class ClipData implements Parcelable { */ public Item(CharSequence text) { mText = text; + mHtmlText = null; + mIntent = null; + mUri = null; + } + + /** + * Create an Item consisting of a single block of (possibly styled) text, + * with an alternative HTML formatted representation. You <em>must</em> + * supply a plain text representation in addition to HTML text; coercion + * will not be done from HTML formated text into plain text. + */ + public Item(CharSequence text, String htmlText) { + mText = text; + mHtmlText = htmlText; mIntent = null; mUri = null; } @@ -193,6 +215,7 @@ public class ClipData implements Parcelable { */ public Item(Intent intent) { mText = null; + mHtmlText = null; mIntent = intent; mUri = null; } @@ -202,16 +225,35 @@ public class ClipData implements Parcelable { */ public Item(Uri uri) { mText = null; + mHtmlText = null; mIntent = null; mUri = uri; } /** * Create a complex Item, containing multiple representations of - * text, intent, and/or URI. + * text, Intent, and/or URI. */ public Item(CharSequence text, Intent intent, Uri uri) { mText = text; + mHtmlText = null; + mIntent = intent; + mUri = uri; + } + + /** + * Create a complex Item, containing multiple representations of + * text, HTML text, Intent, and/or URI. If providing HTML text, you + * <em>must</em> supply a plain text representation as well; coercion + * will not be done from HTML formated text into plain text. + */ + public Item(CharSequence text, String htmlText, Intent intent, Uri uri) { + if (htmlText != null && text == null) { + throw new IllegalArgumentException( + "Plain text must be supplied if HTML text is supplied"); + } + mText = text; + mHtmlText = htmlText; mIntent = intent; mUri = uri; } @@ -224,6 +266,13 @@ public class ClipData implements Parcelable { } /** + * Retrieve the raw HTML text contained in this Item. + */ + public String getHtmlText() { + return mHtmlText; + } + + /** * Retrieve the raw Intent contained in this Item. */ public Intent getIntent() { @@ -250,7 +299,7 @@ public class ClipData implements Parcelable { * the content provider does not supply a text representation, return * the raw URI as a string. * <li> If {@link #getIntent} is non-null, convert that to an intent: - * URI and returnit. + * URI and return it. * <li> Otherwise, return an empty string. * </ul> * @@ -261,12 +310,14 @@ public class ClipData implements Parcelable { //BEGIN_INCLUDE(coerceToText) public CharSequence coerceToText(Context context) { // If this Item has an explicit textual value, simply return that. - if (mText != null) { - return mText; + CharSequence text = getText(); + if (text != null) { + return text; } // If this Item has a URI value, try using that. - if (mUri != null) { + Uri uri = getUri(); + if (uri != null) { // First see if the URI can be opened as a plain text stream // (of any sub-type). If so, this is the best textual @@ -275,7 +326,7 @@ public class ClipData implements Parcelable { try { // Ask for a stream of the desired type. AssetFileDescriptor descr = context.getContentResolver() - .openTypedAssetFileDescriptor(mUri, "text/*", null); + .openTypedAssetFileDescriptor(uri, "text/*", null); stream = descr.createInputStream(); InputStreamReader reader = new InputStreamReader(stream, "UTF-8"); @@ -308,13 +359,14 @@ public class ClipData implements Parcelable { // If we couldn't open the URI as a stream, then the URI itself // probably serves fairly well as a textual representation. - return mUri.toString(); + return uri.toString(); } // Finally, if all we have is an Intent, then we can just turn that // into text. Not the most user-friendly thing, but it's something. - if (mIntent != null) { - return mIntent.toUri(Intent.URI_INTENT_SCHEME); + Intent intent = getIntent(); + if (intent != null) { + return intent.toUri(Intent.URI_INTENT_SCHEME); } // Shouldn't get here, but just in case... @@ -322,6 +374,210 @@ public class ClipData implements Parcelable { } //END_INCLUDE(coerceToText) + /** + * Like {@link #coerceToHtmlText(Context)}, but any text that would + * be returned as HTML formatting will be returned as text with + * style spans. + * @param context The caller's Context, from which its ContentResolver + * and other things can be retrieved. + * @return Returns the item's textual representation. + */ + public CharSequence coerceToStyledText(Context context) { + CharSequence text = getText(); + if (text instanceof Spanned) { + return text; + } + String htmlText = getHtmlText(); + if (htmlText != null) { + try { + CharSequence newText = Html.fromHtml(htmlText); + if (newText != null) { + return newText; + } + } catch (RuntimeException e) { + // If anything bad happens, we'll fall back on the plain text. + } + } + + if (text != null) { + return text; + } + return coerceToHtmlOrStyledText(context, true); + } + + /** + * Turn this item into HTML text, regardless of the type of data it + * actually contains. + * + * <p>The algorithm for deciding what text to return is: + * <ul> + * <li> If {@link #getHtmlText} is non-null, return that. + * <li> If {@link #getText} is non-null, return that, converting to + * valid HTML text. If this text contains style spans, + * {@link Html#toHtml(Spanned) Html.toHtml(Spanned)} is used to + * convert them to HTML formatting. + * <li> If {@link #getUri} is non-null, try to retrieve its data + * as a text stream from its content provider. If the provider can + * supply text/html data, that will be preferred and returned as-is. + * Otherwise, any text/* data will be returned and escaped to HTML. + * If it is not a content: URI or the content provider does not supply + * a text representation, HTML text containing a link to the URI + * will be returned. + * <li> If {@link #getIntent} is non-null, convert that to an intent: + * URI and return as an HTML link. + * <li> Otherwise, return an empty string. + * </ul> + * + * @param context The caller's Context, from which its ContentResolver + * and other things can be retrieved. + * @return Returns the item's representation as HTML text. + */ + public String coerceToHtmlText(Context context) { + // If the item has an explicit HTML value, simply return that. + String htmlText = getHtmlText(); + if (htmlText != null) { + return htmlText; + } + + // If this Item has a plain text value, return it as HTML. + CharSequence text = getText(); + if (text != null) { + if (text instanceof Spanned) { + return Html.toHtml((Spanned)text); + } + return Html.escapeHtml(text); + } + + text = coerceToHtmlOrStyledText(context, false); + return text != null ? text.toString() : null; + } + + private CharSequence coerceToHtmlOrStyledText(Context context, boolean styled) { + // If this Item has a URI value, try using that. + if (mUri != null) { + + // Check to see what data representations the content + // provider supports. We would like HTML text, but if that + // is not possible we'll live with plan text. + String[] types = context.getContentResolver().getStreamTypes(mUri, "text/*"); + boolean hasHtml = false; + boolean hasText = false; + if (types != null) { + for (String type : types) { + if ("text/html".equals(type)) { + hasHtml = true; + } else if (type.startsWith("text/")) { + hasText = true; + } + } + } + + // If the provider can serve data we can use, open and load it. + if (hasHtml || hasText) { + FileInputStream stream = null; + try { + // Ask for a stream of the desired type. + AssetFileDescriptor descr = context.getContentResolver() + .openTypedAssetFileDescriptor(mUri, + hasHtml ? "text/html" : "text/plain", null); + stream = descr.createInputStream(); + InputStreamReader reader = new InputStreamReader(stream, "UTF-8"); + + // Got it... copy the stream into a local string and return it. + StringBuilder builder = new StringBuilder(128); + char[] buffer = new char[8192]; + int len; + while ((len=reader.read(buffer)) > 0) { + builder.append(buffer, 0, len); + } + String text = builder.toString(); + if (hasHtml) { + if (styled) { + // We loaded HTML formatted text and the caller + // want styled text, convert it. + try { + CharSequence newText = Html.fromHtml(text); + return newText != null ? newText : text; + } catch (RuntimeException e) { + return text; + } + } else { + // We loaded HTML formatted text and that is what + // the caller wants, just return it. + return text.toString(); + } + } + if (styled) { + // We loaded plain text and the caller wants styled + // text, that is all we have so return it. + return text; + } else { + // We loaded plain text and the caller wants HTML + // text, escape it for HTML. + return Html.escapeHtml(text); + } + + } catch (FileNotFoundException e) { + // Unable to open content URI as text... not really an + // error, just something to ignore. + + } catch (IOException e) { + // Something bad has happened. + Log.w("ClippedData", "Failure loading text", e); + return Html.escapeHtml(e.toString()); + + } finally { + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + } + } + } + } + + // If we couldn't open the URI as a stream, then we can build + // some HTML text with the URI itself. + // probably serves fairly well as a textual representation. + if (styled) { + return uriToStyledText(mUri.toString()); + } else { + return uriToHtml(mUri.toString()); + } + } + + // Finally, if all we have is an Intent, then we can just turn that + // into text. Not the most user-friendly thing, but it's something. + if (mIntent != null) { + if (styled) { + return uriToStyledText(mIntent.toUri(Intent.URI_INTENT_SCHEME)); + } else { + return uriToHtml(mIntent.toUri(Intent.URI_INTENT_SCHEME)); + } + } + + // Shouldn't get here, but just in case... + return ""; + } + + private String uriToHtml(String uri) { + StringBuilder builder = new StringBuilder(256); + builder.append("<a href=\""); + builder.append(uri); + builder.append("\">"); + builder.append(Html.escapeHtml(uri)); + builder.append("</a>"); + return builder.toString(); + } + + private CharSequence uriToStyledText(String uri) { + SpannableStringBuilder builder = new SpannableStringBuilder(); + builder.append(uri); + builder.setSpan(new URLSpan(uri), 0, builder.length(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + return builder; + } + @Override public String toString() { StringBuilder b = new StringBuilder(128); @@ -335,7 +591,10 @@ public class ClipData implements Parcelable { /** @hide */ public void toShortString(StringBuilder b) { - if (mText != null) { + if (mHtmlText != null) { + b.append("H:"); + b.append(mHtmlText); + } else if (mText != null) { b.append("T:"); b.append(mText); } else if (mUri != null) { @@ -409,6 +668,22 @@ public class ClipData implements Parcelable { } /** + * Create a new ClipData holding data of the type + * {@link ClipDescription#MIMETYPE_TEXT_HTML}. + * + * @param label User-visible label for the clip data. + * @param text The text of clip as plain text, for receivers that don't + * handle HTML. This is required. + * @param htmlText The actual HTML text in the clip. + * @return Returns a new ClipData containing the specified data. + */ + static public ClipData newHtmlText(CharSequence label, CharSequence text, + String htmlText) { + Item item = new Item(text, htmlText); + return new ClipData(label, MIMETYPES_TEXT_HTML, item); + } + + /** * Create a new ClipData holding an Intent with MIME type * {@link ClipDescription#MIMETYPE_TEXT_INTENT}. * @@ -574,6 +849,7 @@ public class ClipData implements Parcelable { for (int i=0; i<N; i++) { Item item = mItems.get(i); TextUtils.writeToParcel(item.mText, dest, flags); + dest.writeString(item.mHtmlText); if (item.mIntent != null) { dest.writeInt(1); item.mIntent.writeToParcel(dest, flags); @@ -600,9 +876,10 @@ public class ClipData implements Parcelable { final int N = in.readInt(); for (int i=0; i<N; i++) { CharSequence text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + String htmlText = in.readString(); Intent intent = in.readInt() != 0 ? Intent.CREATOR.createFromParcel(in) : null; Uri uri = in.readInt() != 0 ? Uri.CREATOR.createFromParcel(in) : null; - mItems.add(new Item(text, intent, uri)); + mItems.add(new Item(text, htmlText, intent, uri)); } } diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java index c6b51ef..5cb6e77 100644 --- a/core/java/android/content/ClipDescription.java +++ b/core/java/android/content/ClipDescription.java @@ -41,6 +41,11 @@ public class ClipDescription implements Parcelable { public static final String MIMETYPE_TEXT_PLAIN = "text/plain"; /** + * The MIME type for a clip holding HTML text. + */ + public static final String MIMETYPE_TEXT_HTML = "text/html"; + + /** * The MIME type for a clip holding one or more URIs. This should be * used for URIs that are meaningful to a user (such as an http: URI). * It should <em>not</em> be used for a content: URI that references some diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 2930998..722fdc6 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -248,7 +248,7 @@ public abstract class ContentResolver { * @param mimeTypeFilter The desired MIME type. This may be a pattern, * such as *\/*, to query for all available MIME types that match the * pattern. - * @return Returns an array of MIME type strings for all availablle + * @return Returns an array of MIME type strings for all available * data streams that match the given mimeTypeFilter. If there are none, * null is returned. */ diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 18d682d..19e4372 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -954,7 +954,18 @@ public class Intent implements Parcelable, Cloneable { * using EXTRA_TEXT, the MIME type should be "text/plain"; otherwise it * should be the MIME type of the data in EXTRA_STREAM. Use {@literal *}/* * if the MIME type is unknown (this will only allow senders that can - * handle generic data streams). + * handle generic data streams). If using {@link #EXTRA_TEXT}, you can + * also optionally supply {@link #EXTRA_HTML_TEXT} for clients to retrieve + * your text with HTML formatting. + * <p> + * As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, the data + * being sent can be supplied through {@link #setClipData(ClipData)}. This + * allows you to use {@link #FLAG_GRANT_READ_URI_PERMISSION} when sharing + * content: URIs and other advanced features of {@link ClipData}. If + * using this approach, you still must supply the same data through the + * {@link #EXTRA_TEXT} or {@link #EXTRA_STREAM} fields described below + * for compatibility with old applications. If you don't set a ClipData, + * it will be copied there for you when calling {@link Context#startActivity(Intent)}. * <p> * Optional standard extras, which may be interpreted by some recipients as * appropriate, are: {@link #EXTRA_EMAIL}, {@link #EXTRA_CC}, @@ -967,11 +978,13 @@ public class Intent implements Parcelable, Cloneable { /** * Activity Action: Deliver multiple data to someone else. * <p> - * Like ACTION_SEND, except the data is multiple. + * Like {@link #ACTION_SEND}, except the data is multiple. * <p> * Input: {@link #getType} is the MIME type of the data being sent. * get*ArrayListExtra can have either a {@link #EXTRA_TEXT} or {@link - * #EXTRA_STREAM} field, containing the data to be sent. + * #EXTRA_STREAM} field, containing the data to be sent. If using + * {@link #EXTRA_TEXT}, you can also optionally supply {@link #EXTRA_HTML_TEXT} + * for clients to retrieve your text with HTML formatting. * <p> * Multiple types are supported, and receivers should handle mixed types * whenever possible. The right way for the receiver to check them is to @@ -983,6 +996,15 @@ public class Intent implements Parcelable, Cloneable { * be image/jpg, but if you are sending image/jpg and image/png, then the * intent's type should be image/*. * <p> + * As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, the data + * being sent can be supplied through {@link #setClipData(ClipData)}. This + * allows you to use {@link #FLAG_GRANT_READ_URI_PERMISSION} when sharing + * content: URIs and other advanced features of {@link ClipData}. If + * using this approach, you still must supply the same data through the + * {@link #EXTRA_TEXT} or {@link #EXTRA_STREAM} fields described below + * for compatibility with old applications. If you don't set a ClipData, + * it will be copied there for you when calling {@link Context#startActivity(Intent)}. + * <p> * Optional standard extras, which may be interpreted by some recipients as * appropriate, are: {@link #EXTRA_EMAIL}, {@link #EXTRA_CC}, * {@link #EXTRA_BCC}, {@link #EXTRA_SUBJECT}. @@ -2501,6 +2523,14 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_TEXT = "android.intent.extra.TEXT"; /** + * A constant String that is associated with the Intent, used with + * {@link #ACTION_SEND} to supply an alternative to {@link #EXTRA_TEXT} + * as HTML formatted text. Note that you <em>must</em> also supply + * {@link #EXTRA_TEXT}. + */ + public static final String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT"; + + /** * A content: URI holding a stream of data associated with the Intent, * used with {@link #ACTION_SEND} to supply the data being sent. */ diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java index 8c97293..35e2e4a 100644 --- a/core/java/android/text/Html.java +++ b/core/java/android/text/Html.java @@ -147,6 +147,15 @@ public class Html { return out.toString(); } + /** + * Returns an HTML escaped representation of the given plain text. + */ + public static String escapeHtml(CharSequence text) { + StringBuilder out = new StringBuilder(); + withinStyle(out, text, 0, text.length()); + return out.toString(); + } + private static void withinHtml(StringBuilder out, Spanned text) { int len = text.length(); @@ -370,7 +379,7 @@ public class Html { } } - private static void withinStyle(StringBuilder out, Spanned text, + private static void withinStyle(StringBuilder out, CharSequence text, int start, int end) { for (int i = start; i < end; i++) { char c = text.charAt(i); diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index cbff58c..040a385 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -1681,7 +1681,7 @@ public class Editor { final int itemCount = clipData.getItemCount(); for (int i=0; i < itemCount; i++) { Item item = clipData.getItemAt(i); - content.append(item.coerceToText(mTextView.getContext())); + content.append(item.coerceToStyledText(mTextView.getContext())); } final int offset = mTextView.getOffsetForPosition(event.getX(), event.getY()); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 9867e47..3b0fb36 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -7710,7 +7710,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (clip != null) { boolean didFirst = false; for (int i=0; i<clip.getItemCount(); i++) { - CharSequence paste = clip.getItemAt(i).coerceToText(getContext()); + CharSequence paste = clip.getItemAt(i).coerceToStyledText(getContext()); if (paste != null) { if (!didFirst) { long minMax = prepareSpacesAroundPaste(min, max, paste); |