OSDN Git Service

2cfdb7693a38c5a24c2d716351976bfa67e20fd4
[android-x86/frameworks-base.git] / core / java / android / widget / TextView.java
1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package android.widget;
18
19 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH;
20 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
21 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
22 import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
23
24 import android.R;
25 import android.annotation.CheckResult;
26 import android.annotation.ColorInt;
27 import android.annotation.DrawableRes;
28 import android.annotation.FloatRange;
29 import android.annotation.IntDef;
30 import android.annotation.IntRange;
31 import android.annotation.NonNull;
32 import android.annotation.Nullable;
33 import android.annotation.Px;
34 import android.annotation.Size;
35 import android.annotation.StringRes;
36 import android.annotation.StyleRes;
37 import android.annotation.XmlRes;
38 import android.app.Activity;
39 import android.app.assist.AssistStructure;
40 import android.content.ClipData;
41 import android.content.ClipDescription;
42 import android.content.ClipboardManager;
43 import android.content.Context;
44 import android.content.Intent;
45 import android.content.UndoManager;
46 import android.content.res.ColorStateList;
47 import android.content.res.CompatibilityInfo;
48 import android.content.res.Configuration;
49 import android.content.res.ResourceId;
50 import android.content.res.Resources;
51 import android.content.res.TypedArray;
52 import android.content.res.XmlResourceParser;
53 import android.graphics.BaseCanvas;
54 import android.graphics.Canvas;
55 import android.graphics.Insets;
56 import android.graphics.Paint;
57 import android.graphics.Paint.FontMetricsInt;
58 import android.graphics.Path;
59 import android.graphics.PorterDuff;
60 import android.graphics.Rect;
61 import android.graphics.RectF;
62 import android.graphics.Typeface;
63 import android.graphics.drawable.Drawable;
64 import android.graphics.fonts.FontVariationAxis;
65 import android.icu.text.DecimalFormatSymbols;
66 import android.os.AsyncTask;
67 import android.os.Build.VERSION_CODES;
68 import android.os.Bundle;
69 import android.os.LocaleList;
70 import android.os.Parcel;
71 import android.os.Parcelable;
72 import android.os.ParcelableParcel;
73 import android.os.SystemClock;
74 import android.provider.Settings;
75 import android.text.BoringLayout;
76 import android.text.DynamicLayout;
77 import android.text.Editable;
78 import android.text.GetChars;
79 import android.text.GraphicsOperations;
80 import android.text.InputFilter;
81 import android.text.InputType;
82 import android.text.Layout;
83 import android.text.ParcelableSpan;
84 import android.text.PrecomputedText;
85 import android.text.Selection;
86 import android.text.SpanWatcher;
87 import android.text.Spannable;
88 import android.text.SpannableStringBuilder;
89 import android.text.Spanned;
90 import android.text.SpannedString;
91 import android.text.StaticLayout;
92 import android.text.TextDirectionHeuristic;
93 import android.text.TextDirectionHeuristics;
94 import android.text.TextPaint;
95 import android.text.TextUtils;
96 import android.text.TextUtils.TruncateAt;
97 import android.text.TextWatcher;
98 import android.text.method.AllCapsTransformationMethod;
99 import android.text.method.ArrowKeyMovementMethod;
100 import android.text.method.DateKeyListener;
101 import android.text.method.DateTimeKeyListener;
102 import android.text.method.DialerKeyListener;
103 import android.text.method.DigitsKeyListener;
104 import android.text.method.KeyListener;
105 import android.text.method.LinkMovementMethod;
106 import android.text.method.MetaKeyKeyListener;
107 import android.text.method.MovementMethod;
108 import android.text.method.PasswordTransformationMethod;
109 import android.text.method.SingleLineTransformationMethod;
110 import android.text.method.TextKeyListener;
111 import android.text.method.TimeKeyListener;
112 import android.text.method.TransformationMethod;
113 import android.text.method.TransformationMethod2;
114 import android.text.method.WordIterator;
115 import android.text.style.CharacterStyle;
116 import android.text.style.ClickableSpan;
117 import android.text.style.ParagraphStyle;
118 import android.text.style.SpellCheckSpan;
119 import android.text.style.SuggestionSpan;
120 import android.text.style.URLSpan;
121 import android.text.style.UpdateAppearance;
122 import android.text.util.Linkify;
123 import android.util.AttributeSet;
124 import android.util.DisplayMetrics;
125 import android.util.IntArray;
126 import android.util.Log;
127 import android.util.SparseIntArray;
128 import android.util.TypedValue;
129 import android.view.AccessibilityIterators.TextSegmentIterator;
130 import android.view.ActionMode;
131 import android.view.Choreographer;
132 import android.view.ContextMenu;
133 import android.view.DragEvent;
134 import android.view.Gravity;
135 import android.view.HapticFeedbackConstants;
136 import android.view.InputDevice;
137 import android.view.KeyCharacterMap;
138 import android.view.KeyEvent;
139 import android.view.MotionEvent;
140 import android.view.PointerIcon;
141 import android.view.View;
142 import android.view.ViewConfiguration;
143 import android.view.ViewDebug;
144 import android.view.ViewGroup.LayoutParams;
145 import android.view.ViewHierarchyEncoder;
146 import android.view.ViewParent;
147 import android.view.ViewRootImpl;
148 import android.view.ViewStructure;
149 import android.view.ViewTreeObserver;
150 import android.view.accessibility.AccessibilityEvent;
151 import android.view.accessibility.AccessibilityManager;
152 import android.view.accessibility.AccessibilityNodeInfo;
153 import android.view.animation.AnimationUtils;
154 import android.view.autofill.AutofillManager;
155 import android.view.autofill.AutofillValue;
156 import android.view.inputmethod.BaseInputConnection;
157 import android.view.inputmethod.CompletionInfo;
158 import android.view.inputmethod.CorrectionInfo;
159 import android.view.inputmethod.CursorAnchorInfo;
160 import android.view.inputmethod.EditorInfo;
161 import android.view.inputmethod.ExtractedText;
162 import android.view.inputmethod.ExtractedTextRequest;
163 import android.view.inputmethod.InputConnection;
164 import android.view.inputmethod.InputMethodManager;
165 import android.view.textclassifier.TextClassificationManager;
166 import android.view.textclassifier.TextClassifier;
167 import android.view.textclassifier.TextLinks;
168 import android.view.textservice.SpellCheckerSubtype;
169 import android.view.textservice.TextServicesManager;
170 import android.widget.RemoteViews.RemoteView;
171
172 import com.android.internal.annotations.VisibleForTesting;
173 import com.android.internal.logging.MetricsLogger;
174 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
175 import com.android.internal.util.FastMath;
176 import com.android.internal.util.Preconditions;
177 import com.android.internal.widget.EditableInputConnection;
178
179 import libcore.util.EmptyArray;
180
181 import org.xmlpull.v1.XmlPullParserException;
182
183 import java.io.IOException;
184 import java.lang.annotation.Retention;
185 import java.lang.annotation.RetentionPolicy;
186 import java.lang.ref.WeakReference;
187 import java.util.ArrayList;
188 import java.util.Arrays;
189 import java.util.Locale;
190
191 /**
192  * A user interface element that displays text to the user.
193  * To provide user-editable text, see {@link EditText}.
194  * <p>
195  * The following code sample shows a typical use, with an XML layout
196  * and code to modify the contents of the text view:
197  * </p>
198
199  * <pre>
200  * &lt;LinearLayout
201        xmlns:android="http://schemas.android.com/apk/res/android"
202        android:layout_width="match_parent"
203        android:layout_height="match_parent"&gt;
204  *    &lt;TextView
205  *        android:id="@+id/text_view_id"
206  *        android:layout_height="wrap_content"
207  *        android:layout_width="wrap_content"
208  *        android:text="@string/hello" /&gt;
209  * &lt;/LinearLayout&gt;
210  * </pre>
211  * <p>
212  * This code sample demonstrates how to modify the contents of the text view
213  * defined in the previous XML layout:
214  * </p>
215  * <pre>
216  * public class MainActivity extends Activity {
217  *
218  *    protected void onCreate(Bundle savedInstanceState) {
219  *         super.onCreate(savedInstanceState);
220  *         setContentView(R.layout.activity_main);
221  *         final TextView helloTextView = (TextView) findViewById(R.id.text_view_id);
222  *         helloTextView.setText(R.string.user_greeting);
223  *     }
224  * }
225  * </pre>
226  * <p>
227  * To customize the appearance of TextView, see <a href="https://developer.android.com/guide/topics/ui/themes.html">Styles and Themes</a>.
228  * </p>
229  * <p>
230  * <b>XML attributes</b>
231  * <p>
232  * See {@link android.R.styleable#TextView TextView Attributes},
233  * {@link android.R.styleable#View View Attributes}
234  *
235  * @attr ref android.R.styleable#TextView_text
236  * @attr ref android.R.styleable#TextView_bufferType
237  * @attr ref android.R.styleable#TextView_hint
238  * @attr ref android.R.styleable#TextView_textColor
239  * @attr ref android.R.styleable#TextView_textColorHighlight
240  * @attr ref android.R.styleable#TextView_textColorHint
241  * @attr ref android.R.styleable#TextView_textAppearance
242  * @attr ref android.R.styleable#TextView_textColorLink
243  * @attr ref android.R.styleable#TextView_textSize
244  * @attr ref android.R.styleable#TextView_textScaleX
245  * @attr ref android.R.styleable#TextView_fontFamily
246  * @attr ref android.R.styleable#TextView_typeface
247  * @attr ref android.R.styleable#TextView_textStyle
248  * @attr ref android.R.styleable#TextView_cursorVisible
249  * @attr ref android.R.styleable#TextView_maxLines
250  * @attr ref android.R.styleable#TextView_maxHeight
251  * @attr ref android.R.styleable#TextView_lines
252  * @attr ref android.R.styleable#TextView_height
253  * @attr ref android.R.styleable#TextView_minLines
254  * @attr ref android.R.styleable#TextView_minHeight
255  * @attr ref android.R.styleable#TextView_maxEms
256  * @attr ref android.R.styleable#TextView_maxWidth
257  * @attr ref android.R.styleable#TextView_ems
258  * @attr ref android.R.styleable#TextView_width
259  * @attr ref android.R.styleable#TextView_minEms
260  * @attr ref android.R.styleable#TextView_minWidth
261  * @attr ref android.R.styleable#TextView_gravity
262  * @attr ref android.R.styleable#TextView_scrollHorizontally
263  * @attr ref android.R.styleable#TextView_password
264  * @attr ref android.R.styleable#TextView_singleLine
265  * @attr ref android.R.styleable#TextView_selectAllOnFocus
266  * @attr ref android.R.styleable#TextView_includeFontPadding
267  * @attr ref android.R.styleable#TextView_maxLength
268  * @attr ref android.R.styleable#TextView_shadowColor
269  * @attr ref android.R.styleable#TextView_shadowDx
270  * @attr ref android.R.styleable#TextView_shadowDy
271  * @attr ref android.R.styleable#TextView_shadowRadius
272  * @attr ref android.R.styleable#TextView_autoLink
273  * @attr ref android.R.styleable#TextView_linksClickable
274  * @attr ref android.R.styleable#TextView_numeric
275  * @attr ref android.R.styleable#TextView_digits
276  * @attr ref android.R.styleable#TextView_phoneNumber
277  * @attr ref android.R.styleable#TextView_inputMethod
278  * @attr ref android.R.styleable#TextView_capitalize
279  * @attr ref android.R.styleable#TextView_autoText
280  * @attr ref android.R.styleable#TextView_editable
281  * @attr ref android.R.styleable#TextView_freezesText
282  * @attr ref android.R.styleable#TextView_ellipsize
283  * @attr ref android.R.styleable#TextView_drawableTop
284  * @attr ref android.R.styleable#TextView_drawableBottom
285  * @attr ref android.R.styleable#TextView_drawableRight
286  * @attr ref android.R.styleable#TextView_drawableLeft
287  * @attr ref android.R.styleable#TextView_drawableStart
288  * @attr ref android.R.styleable#TextView_drawableEnd
289  * @attr ref android.R.styleable#TextView_drawablePadding
290  * @attr ref android.R.styleable#TextView_drawableTint
291  * @attr ref android.R.styleable#TextView_drawableTintMode
292  * @attr ref android.R.styleable#TextView_lineSpacingExtra
293  * @attr ref android.R.styleable#TextView_lineSpacingMultiplier
294  * @attr ref android.R.styleable#TextView_marqueeRepeatLimit
295  * @attr ref android.R.styleable#TextView_inputType
296  * @attr ref android.R.styleable#TextView_imeOptions
297  * @attr ref android.R.styleable#TextView_privateImeOptions
298  * @attr ref android.R.styleable#TextView_imeActionLabel
299  * @attr ref android.R.styleable#TextView_imeActionId
300  * @attr ref android.R.styleable#TextView_editorExtras
301  * @attr ref android.R.styleable#TextView_elegantTextHeight
302  * @attr ref android.R.styleable#TextView_fallbackLineSpacing
303  * @attr ref android.R.styleable#TextView_letterSpacing
304  * @attr ref android.R.styleable#TextView_fontFeatureSettings
305  * @attr ref android.R.styleable#TextView_breakStrategy
306  * @attr ref android.R.styleable#TextView_hyphenationFrequency
307  * @attr ref android.R.styleable#TextView_autoSizeTextType
308  * @attr ref android.R.styleable#TextView_autoSizeMinTextSize
309  * @attr ref android.R.styleable#TextView_autoSizeMaxTextSize
310  * @attr ref android.R.styleable#TextView_autoSizeStepGranularity
311  * @attr ref android.R.styleable#TextView_autoSizePresetSizes
312  * @attr ref android.R.styleable#TextView_accessibilityHeading
313  */
314 @RemoteView
315 public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
316     static final String LOG_TAG = "TextView";
317     static final boolean DEBUG_EXTRACT = false;
318     private static final float[] TEMP_POSITION = new float[2];
319
320     // Enum for the "typeface" XML parameter.
321     // TODO: How can we get this from the XML instead of hardcoding it here?
322     private static final int SANS = 1;
323     private static final int SERIF = 2;
324     private static final int MONOSPACE = 3;
325
326     // Enum for the "ellipsize" XML parameter.
327     private static final int ELLIPSIZE_NOT_SET = -1;
328     private static final int ELLIPSIZE_NONE = 0;
329     private static final int ELLIPSIZE_START = 1;
330     private static final int ELLIPSIZE_MIDDLE = 2;
331     private static final int ELLIPSIZE_END = 3;
332     private static final int ELLIPSIZE_MARQUEE = 4;
333
334     // Bitfield for the "numeric" XML parameter.
335     // TODO: How can we get this from the XML instead of hardcoding it here?
336     private static final int SIGNED = 2;
337     private static final int DECIMAL = 4;
338
339     /**
340      * Draw marquee text with fading edges as usual
341      */
342     private static final int MARQUEE_FADE_NORMAL = 0;
343
344     /**
345      * Draw marquee text as ellipsize end while inactive instead of with the fade.
346      * (Useful for devices where the fade can be expensive if overdone)
347      */
348     private static final int MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS = 1;
349
350     /**
351      * Draw marquee text with fading edges because it is currently active/animating.
352      */
353     private static final int MARQUEE_FADE_SWITCH_SHOW_FADE = 2;
354
355     private static final int LINES = 1;
356     private static final int EMS = LINES;
357     private static final int PIXELS = 2;
358
359     private static final RectF TEMP_RECTF = new RectF();
360
361     /** @hide */
362     static final int VERY_WIDE = 1024 * 1024; // XXX should be much larger
363     private static final int ANIMATED_SCROLL_GAP = 250;
364
365     private static final InputFilter[] NO_FILTERS = new InputFilter[0];
366     private static final Spanned EMPTY_SPANNED = new SpannedString("");
367
368     private static final int CHANGE_WATCHER_PRIORITY = 100;
369
370     // New state used to change background based on whether this TextView is multiline.
371     private static final int[] MULTILINE_STATE_SET = { R.attr.state_multiline };
372
373     // Accessibility action to share selected text.
374     private static final int ACCESSIBILITY_ACTION_SHARE = 0x10000000;
375
376     /**
377      * @hide
378      */
379     // Accessibility action start id for "process text" actions.
380     static final int ACCESSIBILITY_ACTION_PROCESS_TEXT_START_ID = 0x10000100;
381
382     /**
383      * @hide
384      */
385     static final int PROCESS_TEXT_REQUEST_CODE = 100;
386
387     /**
388      *  Return code of {@link #doKeyDown}.
389      */
390     private static final int KEY_EVENT_NOT_HANDLED = 0;
391     private static final int KEY_EVENT_HANDLED = -1;
392     private static final int KEY_DOWN_HANDLED_BY_KEY_LISTENER = 1;
393     private static final int KEY_DOWN_HANDLED_BY_MOVEMENT_METHOD = 2;
394
395     private static final int FLOATING_TOOLBAR_SELECT_ALL_REFRESH_DELAY = 500;
396
397     // System wide time for last cut, copy or text changed action.
398     static long sLastCutCopyOrTextChangedTime;
399
400     private ColorStateList mTextColor;
401     private ColorStateList mHintTextColor;
402     private ColorStateList mLinkTextColor;
403     @ViewDebug.ExportedProperty(category = "text")
404     private int mCurTextColor;
405     private int mCurHintTextColor;
406     private boolean mFreezesText;
407     private boolean mIsAccessibilityHeading;
408
409     private Editable.Factory mEditableFactory = Editable.Factory.getInstance();
410     private Spannable.Factory mSpannableFactory = Spannable.Factory.getInstance();
411
412     private float mShadowRadius, mShadowDx, mShadowDy;
413     private int mShadowColor;
414
415     private boolean mPreDrawRegistered;
416     private boolean mPreDrawListenerDetached;
417
418     private TextClassifier mTextClassifier;
419
420     // A flag to prevent repeated movements from escaping the enclosing text view. The idea here is
421     // that if a user is holding down a movement key to traverse text, we shouldn't also traverse
422     // the view hierarchy. On the other hand, if the user is using the movement key to traverse
423     // views (i.e. the first movement was to traverse out of this view, or this view was traversed
424     // into by the user holding the movement key down) then we shouldn't prevent the focus from
425     // changing.
426     private boolean mPreventDefaultMovement;
427
428     private TextUtils.TruncateAt mEllipsize;
429
430     static class Drawables {
431         static final int LEFT = 0;
432         static final int TOP = 1;
433         static final int RIGHT = 2;
434         static final int BOTTOM = 3;
435
436         static final int DRAWABLE_NONE = -1;
437         static final int DRAWABLE_RIGHT = 0;
438         static final int DRAWABLE_LEFT = 1;
439
440         final Rect mCompoundRect = new Rect();
441
442         final Drawable[] mShowing = new Drawable[4];
443
444         ColorStateList mTintList;
445         PorterDuff.Mode mTintMode;
446         boolean mHasTint;
447         boolean mHasTintMode;
448
449         Drawable mDrawableStart, mDrawableEnd, mDrawableError, mDrawableTemp;
450         Drawable mDrawableLeftInitial, mDrawableRightInitial;
451
452         boolean mIsRtlCompatibilityMode;
453         boolean mOverride;
454
455         int mDrawableSizeTop, mDrawableSizeBottom, mDrawableSizeLeft, mDrawableSizeRight,
456                 mDrawableSizeStart, mDrawableSizeEnd, mDrawableSizeError, mDrawableSizeTemp;
457
458         int mDrawableWidthTop, mDrawableWidthBottom, mDrawableHeightLeft, mDrawableHeightRight,
459                 mDrawableHeightStart, mDrawableHeightEnd, mDrawableHeightError, mDrawableHeightTemp;
460
461         int mDrawablePadding;
462
463         int mDrawableSaved = DRAWABLE_NONE;
464
465         public Drawables(Context context) {
466             final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
467             mIsRtlCompatibilityMode = targetSdkVersion < VERSION_CODES.JELLY_BEAN_MR1
468                     || !context.getApplicationInfo().hasRtlSupport();
469             mOverride = false;
470         }
471
472         /**
473          * @return {@code true} if this object contains metadata that needs to
474          *         be retained, {@code false} otherwise
475          */
476         public boolean hasMetadata() {
477             return mDrawablePadding != 0 || mHasTintMode || mHasTint;
478         }
479
480         /**
481          * Updates the list of displayed drawables to account for the current
482          * layout direction.
483          *
484          * @param layoutDirection the current layout direction
485          * @return {@code true} if the displayed drawables changed
486          */
487         public boolean resolveWithLayoutDirection(int layoutDirection) {
488             final Drawable previousLeft = mShowing[Drawables.LEFT];
489             final Drawable previousRight = mShowing[Drawables.RIGHT];
490
491             // First reset "left" and "right" drawables to their initial values
492             mShowing[Drawables.LEFT] = mDrawableLeftInitial;
493             mShowing[Drawables.RIGHT] = mDrawableRightInitial;
494
495             if (mIsRtlCompatibilityMode) {
496                 // Use "start" drawable as "left" drawable if the "left" drawable was not defined
497                 if (mDrawableStart != null && mShowing[Drawables.LEFT] == null) {
498                     mShowing[Drawables.LEFT] = mDrawableStart;
499                     mDrawableSizeLeft = mDrawableSizeStart;
500                     mDrawableHeightLeft = mDrawableHeightStart;
501                 }
502                 // Use "end" drawable as "right" drawable if the "right" drawable was not defined
503                 if (mDrawableEnd != null && mShowing[Drawables.RIGHT] == null) {
504                     mShowing[Drawables.RIGHT] = mDrawableEnd;
505                     mDrawableSizeRight = mDrawableSizeEnd;
506                     mDrawableHeightRight = mDrawableHeightEnd;
507                 }
508             } else {
509                 // JB-MR1+ normal case: "start" / "end" drawables are overriding "left" / "right"
510                 // drawable if and only if they have been defined
511                 switch(layoutDirection) {
512                     case LAYOUT_DIRECTION_RTL:
513                         if (mOverride) {
514                             mShowing[Drawables.RIGHT] = mDrawableStart;
515                             mDrawableSizeRight = mDrawableSizeStart;
516                             mDrawableHeightRight = mDrawableHeightStart;
517
518                             mShowing[Drawables.LEFT] = mDrawableEnd;
519                             mDrawableSizeLeft = mDrawableSizeEnd;
520                             mDrawableHeightLeft = mDrawableHeightEnd;
521                         }
522                         break;
523
524                     case LAYOUT_DIRECTION_LTR:
525                     default:
526                         if (mOverride) {
527                             mShowing[Drawables.LEFT] = mDrawableStart;
528                             mDrawableSizeLeft = mDrawableSizeStart;
529                             mDrawableHeightLeft = mDrawableHeightStart;
530
531                             mShowing[Drawables.RIGHT] = mDrawableEnd;
532                             mDrawableSizeRight = mDrawableSizeEnd;
533                             mDrawableHeightRight = mDrawableHeightEnd;
534                         }
535                         break;
536                 }
537             }
538
539             applyErrorDrawableIfNeeded(layoutDirection);
540
541             return mShowing[Drawables.LEFT] != previousLeft
542                     || mShowing[Drawables.RIGHT] != previousRight;
543         }
544
545         public void setErrorDrawable(Drawable dr, TextView tv) {
546             if (mDrawableError != dr && mDrawableError != null) {
547                 mDrawableError.setCallback(null);
548             }
549             mDrawableError = dr;
550
551             if (mDrawableError != null) {
552                 final Rect compoundRect = mCompoundRect;
553                 final int[] state = tv.getDrawableState();
554
555                 mDrawableError.setState(state);
556                 mDrawableError.copyBounds(compoundRect);
557                 mDrawableError.setCallback(tv);
558                 mDrawableSizeError = compoundRect.width();
559                 mDrawableHeightError = compoundRect.height();
560             } else {
561                 mDrawableSizeError = mDrawableHeightError = 0;
562             }
563         }
564
565         private void applyErrorDrawableIfNeeded(int layoutDirection) {
566             // first restore the initial state if needed
567             switch (mDrawableSaved) {
568                 case DRAWABLE_LEFT:
569                     mShowing[Drawables.LEFT] = mDrawableTemp;
570                     mDrawableSizeLeft = mDrawableSizeTemp;
571                     mDrawableHeightLeft = mDrawableHeightTemp;
572                     break;
573                 case DRAWABLE_RIGHT:
574                     mShowing[Drawables.RIGHT] = mDrawableTemp;
575                     mDrawableSizeRight = mDrawableSizeTemp;
576                     mDrawableHeightRight = mDrawableHeightTemp;
577                     break;
578                 case DRAWABLE_NONE:
579                 default:
580             }
581             // then, if needed, assign the Error drawable to the correct location
582             if (mDrawableError != null) {
583                 switch(layoutDirection) {
584                     case LAYOUT_DIRECTION_RTL:
585                         mDrawableSaved = DRAWABLE_LEFT;
586
587                         mDrawableTemp = mShowing[Drawables.LEFT];
588                         mDrawableSizeTemp = mDrawableSizeLeft;
589                         mDrawableHeightTemp = mDrawableHeightLeft;
590
591                         mShowing[Drawables.LEFT] = mDrawableError;
592                         mDrawableSizeLeft = mDrawableSizeError;
593                         mDrawableHeightLeft = mDrawableHeightError;
594                         break;
595                     case LAYOUT_DIRECTION_LTR:
596                     default:
597                         mDrawableSaved = DRAWABLE_RIGHT;
598
599                         mDrawableTemp = mShowing[Drawables.RIGHT];
600                         mDrawableSizeTemp = mDrawableSizeRight;
601                         mDrawableHeightTemp = mDrawableHeightRight;
602
603                         mShowing[Drawables.RIGHT] = mDrawableError;
604                         mDrawableSizeRight = mDrawableSizeError;
605                         mDrawableHeightRight = mDrawableHeightError;
606                         break;
607                 }
608             }
609         }
610     }
611
612     Drawables mDrawables;
613
614     private CharWrapper mCharWrapper;
615
616     private Marquee mMarquee;
617     private boolean mRestartMarquee;
618
619     private int mMarqueeRepeatLimit = 3;
620
621     private int mLastLayoutDirection = -1;
622
623     /**
624      * On some devices the fading edges add a performance penalty if used
625      * extensively in the same layout. This mode indicates how the marquee
626      * is currently being shown, if applicable. (mEllipsize will == MARQUEE)
627      */
628     private int mMarqueeFadeMode = MARQUEE_FADE_NORMAL;
629
630     /**
631      * When mMarqueeFadeMode is not MARQUEE_FADE_NORMAL, this stores
632      * the layout that should be used when the mode switches.
633      */
634     private Layout mSavedMarqueeModeLayout;
635
636     @ViewDebug.ExportedProperty(category = "text")
637     private CharSequence mText;
638     private CharSequence mTransformed;
639     private BufferType mBufferType = BufferType.NORMAL;
640
641     private CharSequence mHint;
642     private Layout mHintLayout;
643
644     private MovementMethod mMovement;
645
646     private TransformationMethod mTransformation;
647     private boolean mAllowTransformationLengthChange;
648     private ChangeWatcher mChangeWatcher;
649
650     private ArrayList<TextWatcher> mListeners;
651
652     // display attributes
653     private final TextPaint mTextPaint;
654     private boolean mUserSetTextScaleX;
655     private Layout mLayout;
656     private boolean mLocalesChanged = false;
657
658     // True if setKeyListener() has been explicitly called
659     private boolean mListenerChanged = false;
660     // True if internationalized input should be used for numbers and date and time.
661     private final boolean mUseInternationalizedInput;
662     // True if fallback fonts that end up getting used should be allowed to affect line spacing.
663     /* package */ boolean mUseFallbackLineSpacing;
664
665     @ViewDebug.ExportedProperty(category = "text")
666     private int mGravity = Gravity.TOP | Gravity.START;
667     private boolean mHorizontallyScrolling;
668
669     private int mAutoLinkMask;
670     private boolean mLinksClickable = true;
671
672     private float mSpacingMult = 1.0f;
673     private float mSpacingAdd = 0.0f;
674
675     private int mBreakStrategy;
676     private int mHyphenationFrequency;
677     private int mJustificationMode;
678
679     private int mMaximum = Integer.MAX_VALUE;
680     private int mMaxMode = LINES;
681     private int mMinimum = 0;
682     private int mMinMode = LINES;
683
684     private int mOldMaximum = mMaximum;
685     private int mOldMaxMode = mMaxMode;
686
687     private int mMaxWidth = Integer.MAX_VALUE;
688     private int mMaxWidthMode = PIXELS;
689     private int mMinWidth = 0;
690     private int mMinWidthMode = PIXELS;
691
692     private boolean mSingleLine;
693     private int mDesiredHeightAtMeasure = -1;
694     private boolean mIncludePad = true;
695     private int mDeferScroll = -1;
696
697     // tmp primitives, so we don't alloc them on each draw
698     private Rect mTempRect;
699     private long mLastScroll;
700     private Scroller mScroller;
701     private TextPaint mTempTextPaint;
702
703     private BoringLayout.Metrics mBoring, mHintBoring;
704     private BoringLayout mSavedLayout, mSavedHintLayout;
705
706     private TextDirectionHeuristic mTextDir;
707
708     private InputFilter[] mFilters = NO_FILTERS;
709
710     private volatile Locale mCurrentSpellCheckerLocaleCache;
711
712     // It is possible to have a selection even when mEditor is null (programmatically set, like when
713     // a link is pressed). These highlight-related fields do not go in mEditor.
714     int mHighlightColor = 0x6633B5E5;
715     private Path mHighlightPath;
716     private final Paint mHighlightPaint;
717     private boolean mHighlightPathBogus = true;
718
719     // Although these fields are specific to editable text, they are not added to Editor because
720     // they are defined by the TextView's style and are theme-dependent.
721     int mCursorDrawableRes;
722     // These six fields, could be moved to Editor, since we know their default values and we
723     // could condition the creation of the Editor to a non standard value. This is however
724     // brittle since the hardcoded values here (such as
725     // com.android.internal.R.drawable.text_select_handle_left) would have to be updated if the
726     // default style is modified.
727     int mTextSelectHandleLeftRes;
728     int mTextSelectHandleRightRes;
729     int mTextSelectHandleRes;
730     int mTextEditSuggestionItemLayout;
731     int mTextEditSuggestionContainerLayout;
732     int mTextEditSuggestionHighlightStyle;
733
734     /**
735      * {@link EditText} specific data, created on demand when one of the Editor fields is used.
736      * See {@link #createEditorIfNeeded()}.
737      */
738     private Editor mEditor;
739
740     private static final int DEVICE_PROVISIONED_UNKNOWN = 0;
741     private static final int DEVICE_PROVISIONED_NO = 1;
742     private static final int DEVICE_PROVISIONED_YES = 2;
743
744     /**
745      * Some special options such as sharing selected text should only be shown if the device
746      * is provisioned. Only check the provisioned state once for a given view instance.
747      */
748     private int mDeviceProvisionedState = DEVICE_PROVISIONED_UNKNOWN;
749
750     /**
751      * The TextView does not auto-size text (default).
752      */
753     public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0;
754
755     /**
756      * The TextView scales text size both horizontally and vertically to fit within the
757      * container.
758      */
759     public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1;
760
761     /** @hide */
762     @IntDef(prefix = { "AUTO_SIZE_TEXT_TYPE_" }, value = {
763             AUTO_SIZE_TEXT_TYPE_NONE,
764             AUTO_SIZE_TEXT_TYPE_UNIFORM
765     })
766     @Retention(RetentionPolicy.SOURCE)
767     public @interface AutoSizeTextType {}
768     // Default minimum size for auto-sizing text in scaled pixels.
769     private static final int DEFAULT_AUTO_SIZE_MIN_TEXT_SIZE_IN_SP = 12;
770     // Default maximum size for auto-sizing text in scaled pixels.
771     private static final int DEFAULT_AUTO_SIZE_MAX_TEXT_SIZE_IN_SP = 112;
772     // Default value for the step size in pixels.
773     private static final int DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX = 1;
774     // Use this to specify that any of the auto-size configuration int values have not been set.
775     private static final float UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE = -1f;
776     // Auto-size text type.
777     private int mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_NONE;
778     // Specify if auto-size text is needed.
779     private boolean mNeedsAutoSizeText = false;
780     // Step size for auto-sizing in pixels.
781     private float mAutoSizeStepGranularityInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
782     // Minimum text size for auto-sizing in pixels.
783     private float mAutoSizeMinTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
784     // Maximum text size for auto-sizing in pixels.
785     private float mAutoSizeMaxTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
786     // Contains a (specified or computed) distinct sorted set of text sizes in pixels to pick from
787     // when auto-sizing text.
788     private int[] mAutoSizeTextSizesInPx = EmptyArray.INT;
789     // Specifies whether auto-size should use the provided auto size steps set or if it should
790     // build the steps set using mAutoSizeMinTextSizeInPx, mAutoSizeMaxTextSizeInPx and
791     // mAutoSizeStepGranularityInPx.
792     private boolean mHasPresetAutoSizeValues = false;
793
794     // Autofill-related attributes
795     //
796     // Indicates whether the text was set statically or dynamically, so it can be used to
797     // sanitize autofill requests.
798     private boolean mTextSetFromXmlOrResourceId = false;
799     // Resource id used to set the text.
800     private @StringRes int mTextId = ResourceId.ID_NULL;
801     // Last value used on AFM.notifyValueChanged(), used to optimize autofill workflow by avoiding
802     // calls when the value did not change
803     private CharSequence mLastValueSentToAutofillManager;
804     //
805     // End of autofill-related attributes
806
807     /**
808      * Kick-start the font cache for the zygote process (to pay the cost of
809      * initializing freetype for our default font only once).
810      * @hide
811      */
812     public static void preloadFontCache() {
813         Paint p = new Paint();
814         p.setAntiAlias(true);
815         // Ensure that the Typeface is loaded here.
816         // Typically, Typeface is preloaded by zygote but not on all devices, e.g. Android Auto.
817         // So, sets Typeface.DEFAULT explicitly here for ensuring that the Typeface is loaded here
818         // since Paint.measureText can not be called without Typeface static initializer.
819         p.setTypeface(Typeface.DEFAULT);
820         // We don't care about the result, just the side-effect of measuring.
821         p.measureText("H");
822     }
823
824     /**
825      * Interface definition for a callback to be invoked when an action is
826      * performed on the editor.
827      */
828     public interface OnEditorActionListener {
829         /**
830          * Called when an action is being performed.
831          *
832          * @param v The view that was clicked.
833          * @param actionId Identifier of the action.  This will be either the
834          * identifier you supplied, or {@link EditorInfo#IME_NULL
835          * EditorInfo.IME_NULL} if being called due to the enter key
836          * being pressed.
837          * @param event If triggered by an enter key, this is the event;
838          * otherwise, this is null.
839          * @return Return true if you have consumed the action, else false.
840          */
841         boolean onEditorAction(TextView v, int actionId, KeyEvent event);
842     }
843
844     public TextView(Context context) {
845         this(context, null);
846     }
847
848     public TextView(Context context, @Nullable AttributeSet attrs) {
849         this(context, attrs, com.android.internal.R.attr.textViewStyle);
850     }
851
852     public TextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
853         this(context, attrs, defStyleAttr, 0);
854     }
855
856     @SuppressWarnings("deprecation")
857     public TextView(
858             Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
859         super(context, attrs, defStyleAttr, defStyleRes);
860
861         // TextView is important by default, unless app developer overrode attribute.
862         if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
863             setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
864         }
865
866         mText = "";
867
868         final Resources res = getResources();
869         final CompatibilityInfo compat = res.getCompatibilityInfo();
870
871         mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
872         mTextPaint.density = res.getDisplayMetrics().density;
873         mTextPaint.setCompatibilityScaling(compat.applicationScale);
874
875         mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
876         mHighlightPaint.setCompatibilityScaling(compat.applicationScale);
877
878         mMovement = getDefaultMovementMethod();
879
880         mTransformation = null;
881
882         final TextAppearanceAttributes attributes = new TextAppearanceAttributes();
883         attributes.mTextColor = ColorStateList.valueOf(0xFF000000);
884         attributes.mTextSize = 15;
885         mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
886         mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
887         mJustificationMode = Layout.JUSTIFICATION_MODE_NONE;
888
889         final Resources.Theme theme = context.getTheme();
890
891         /*
892          * Look the appearance up without checking first if it exists because
893          * almost every TextView has one and it greatly simplifies the logic
894          * to be able to parse the appearance first and then let specific tags
895          * for this View override it.
896          */
897         TypedArray a = theme.obtainStyledAttributes(attrs,
898                 com.android.internal.R.styleable.TextViewAppearance, defStyleAttr, defStyleRes);
899         TypedArray appearance = null;
900         int ap = a.getResourceId(
901                 com.android.internal.R.styleable.TextViewAppearance_textAppearance, -1);
902         a.recycle();
903         if (ap != -1) {
904             appearance = theme.obtainStyledAttributes(
905                     ap, com.android.internal.R.styleable.TextAppearance);
906         }
907         if (appearance != null) {
908             readTextAppearance(context, appearance, attributes, false /* styleArray */);
909             attributes.mFontFamilyExplicit = false;
910             appearance.recycle();
911         }
912
913         boolean editable = getDefaultEditable();
914         CharSequence inputMethod = null;
915         int numeric = 0;
916         CharSequence digits = null;
917         boolean phone = false;
918         boolean autotext = false;
919         int autocap = -1;
920         int buffertype = 0;
921         boolean selectallonfocus = false;
922         Drawable drawableLeft = null, drawableTop = null, drawableRight = null,
923                 drawableBottom = null, drawableStart = null, drawableEnd = null;
924         ColorStateList drawableTint = null;
925         PorterDuff.Mode drawableTintMode = null;
926         int drawablePadding = 0;
927         int ellipsize = ELLIPSIZE_NOT_SET;
928         boolean singleLine = false;
929         int maxlength = -1;
930         CharSequence text = "";
931         CharSequence hint = null;
932         boolean password = false;
933         float autoSizeMinTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
934         float autoSizeMaxTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
935         float autoSizeStepGranularityInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
936         int inputType = EditorInfo.TYPE_NULL;
937         a = theme.obtainStyledAttributes(
938                     attrs, com.android.internal.R.styleable.TextView, defStyleAttr, defStyleRes);
939         int firstBaselineToTopHeight = -1;
940         int lastBaselineToBottomHeight = -1;
941         int lineHeight = -1;
942
943         readTextAppearance(context, a, attributes, true /* styleArray */);
944
945         int n = a.getIndexCount();
946
947         // Must set id in a temporary variable because it will be reset by setText()
948         boolean textIsSetFromXml = false;
949         for (int i = 0; i < n; i++) {
950             int attr = a.getIndex(i);
951
952             switch (attr) {
953                 case com.android.internal.R.styleable.TextView_editable:
954                     editable = a.getBoolean(attr, editable);
955                     break;
956
957                 case com.android.internal.R.styleable.TextView_inputMethod:
958                     inputMethod = a.getText(attr);
959                     break;
960
961                 case com.android.internal.R.styleable.TextView_numeric:
962                     numeric = a.getInt(attr, numeric);
963                     break;
964
965                 case com.android.internal.R.styleable.TextView_digits:
966                     digits = a.getText(attr);
967                     break;
968
969                 case com.android.internal.R.styleable.TextView_phoneNumber:
970                     phone = a.getBoolean(attr, phone);
971                     break;
972
973                 case com.android.internal.R.styleable.TextView_autoText:
974                     autotext = a.getBoolean(attr, autotext);
975                     break;
976
977                 case com.android.internal.R.styleable.TextView_capitalize:
978                     autocap = a.getInt(attr, autocap);
979                     break;
980
981                 case com.android.internal.R.styleable.TextView_bufferType:
982                     buffertype = a.getInt(attr, buffertype);
983                     break;
984
985                 case com.android.internal.R.styleable.TextView_selectAllOnFocus:
986                     selectallonfocus = a.getBoolean(attr, selectallonfocus);
987                     break;
988
989                 case com.android.internal.R.styleable.TextView_autoLink:
990                     mAutoLinkMask = a.getInt(attr, 0);
991                     break;
992
993                 case com.android.internal.R.styleable.TextView_linksClickable:
994                     mLinksClickable = a.getBoolean(attr, true);
995                     break;
996
997                 case com.android.internal.R.styleable.TextView_drawableLeft:
998                     drawableLeft = a.getDrawable(attr);
999                     break;
1000
1001                 case com.android.internal.R.styleable.TextView_drawableTop:
1002                     drawableTop = a.getDrawable(attr);
1003                     break;
1004
1005                 case com.android.internal.R.styleable.TextView_drawableRight:
1006                     drawableRight = a.getDrawable(attr);
1007                     break;
1008
1009                 case com.android.internal.R.styleable.TextView_drawableBottom:
1010                     drawableBottom = a.getDrawable(attr);
1011                     break;
1012
1013                 case com.android.internal.R.styleable.TextView_drawableStart:
1014                     drawableStart = a.getDrawable(attr);
1015                     break;
1016
1017                 case com.android.internal.R.styleable.TextView_drawableEnd:
1018                     drawableEnd = a.getDrawable(attr);
1019                     break;
1020
1021                 case com.android.internal.R.styleable.TextView_drawableTint:
1022                     drawableTint = a.getColorStateList(attr);
1023                     break;
1024
1025                 case com.android.internal.R.styleable.TextView_drawableTintMode:
1026                     drawableTintMode = Drawable.parseTintMode(a.getInt(attr, -1), drawableTintMode);
1027                     break;
1028
1029                 case com.android.internal.R.styleable.TextView_drawablePadding:
1030                     drawablePadding = a.getDimensionPixelSize(attr, drawablePadding);
1031                     break;
1032
1033                 case com.android.internal.R.styleable.TextView_maxLines:
1034                     setMaxLines(a.getInt(attr, -1));
1035                     break;
1036
1037                 case com.android.internal.R.styleable.TextView_maxHeight:
1038                     setMaxHeight(a.getDimensionPixelSize(attr, -1));
1039                     break;
1040
1041                 case com.android.internal.R.styleable.TextView_lines:
1042                     setLines(a.getInt(attr, -1));
1043                     break;
1044
1045                 case com.android.internal.R.styleable.TextView_height:
1046                     setHeight(a.getDimensionPixelSize(attr, -1));
1047                     break;
1048
1049                 case com.android.internal.R.styleable.TextView_minLines:
1050                     setMinLines(a.getInt(attr, -1));
1051                     break;
1052
1053                 case com.android.internal.R.styleable.TextView_minHeight:
1054                     setMinHeight(a.getDimensionPixelSize(attr, -1));
1055                     break;
1056
1057                 case com.android.internal.R.styleable.TextView_maxEms:
1058                     setMaxEms(a.getInt(attr, -1));
1059                     break;
1060
1061                 case com.android.internal.R.styleable.TextView_maxWidth:
1062                     setMaxWidth(a.getDimensionPixelSize(attr, -1));
1063                     break;
1064
1065                 case com.android.internal.R.styleable.TextView_ems:
1066                     setEms(a.getInt(attr, -1));
1067                     break;
1068
1069                 case com.android.internal.R.styleable.TextView_width:
1070                     setWidth(a.getDimensionPixelSize(attr, -1));
1071                     break;
1072
1073                 case com.android.internal.R.styleable.TextView_minEms:
1074                     setMinEms(a.getInt(attr, -1));
1075                     break;
1076
1077                 case com.android.internal.R.styleable.TextView_minWidth:
1078                     setMinWidth(a.getDimensionPixelSize(attr, -1));
1079                     break;
1080
1081                 case com.android.internal.R.styleable.TextView_gravity:
1082                     setGravity(a.getInt(attr, -1));
1083                     break;
1084
1085                 case com.android.internal.R.styleable.TextView_hint:
1086                     hint = a.getText(attr);
1087                     break;
1088
1089                 case com.android.internal.R.styleable.TextView_text:
1090                     textIsSetFromXml = true;
1091                     mTextId = a.getResourceId(attr, ResourceId.ID_NULL);
1092                     text = a.getText(attr);
1093                     break;
1094
1095                 case com.android.internal.R.styleable.TextView_scrollHorizontally:
1096                     if (a.getBoolean(attr, false)) {
1097                         setHorizontallyScrolling(true);
1098                     }
1099                     break;
1100
1101                 case com.android.internal.R.styleable.TextView_singleLine:
1102                     singleLine = a.getBoolean(attr, singleLine);
1103                     break;
1104
1105                 case com.android.internal.R.styleable.TextView_ellipsize:
1106                     ellipsize = a.getInt(attr, ellipsize);
1107                     break;
1108
1109                 case com.android.internal.R.styleable.TextView_marqueeRepeatLimit:
1110                     setMarqueeRepeatLimit(a.getInt(attr, mMarqueeRepeatLimit));
1111                     break;
1112
1113                 case com.android.internal.R.styleable.TextView_includeFontPadding:
1114                     if (!a.getBoolean(attr, true)) {
1115                         setIncludeFontPadding(false);
1116                     }
1117                     break;
1118
1119                 case com.android.internal.R.styleable.TextView_cursorVisible:
1120                     if (!a.getBoolean(attr, true)) {
1121                         setCursorVisible(false);
1122                     }
1123                     break;
1124
1125                 case com.android.internal.R.styleable.TextView_maxLength:
1126                     maxlength = a.getInt(attr, -1);
1127                     break;
1128
1129                 case com.android.internal.R.styleable.TextView_textScaleX:
1130                     setTextScaleX(a.getFloat(attr, 1.0f));
1131                     break;
1132
1133                 case com.android.internal.R.styleable.TextView_freezesText:
1134                     mFreezesText = a.getBoolean(attr, false);
1135                     break;
1136
1137                 case com.android.internal.R.styleable.TextView_enabled:
1138                     setEnabled(a.getBoolean(attr, isEnabled()));
1139                     break;
1140
1141                 case com.android.internal.R.styleable.TextView_password:
1142                     password = a.getBoolean(attr, password);
1143                     break;
1144
1145                 case com.android.internal.R.styleable.TextView_lineSpacingExtra:
1146                     mSpacingAdd = a.getDimensionPixelSize(attr, (int) mSpacingAdd);
1147                     break;
1148
1149                 case com.android.internal.R.styleable.TextView_lineSpacingMultiplier:
1150                     mSpacingMult = a.getFloat(attr, mSpacingMult);
1151                     break;
1152
1153                 case com.android.internal.R.styleable.TextView_inputType:
1154                     inputType = a.getInt(attr, EditorInfo.TYPE_NULL);
1155                     break;
1156
1157                 case com.android.internal.R.styleable.TextView_allowUndo:
1158                     createEditorIfNeeded();
1159                     mEditor.mAllowUndo = a.getBoolean(attr, true);
1160                     break;
1161
1162                 case com.android.internal.R.styleable.TextView_imeOptions:
1163                     createEditorIfNeeded();
1164                     mEditor.createInputContentTypeIfNeeded();
1165                     mEditor.mInputContentType.imeOptions = a.getInt(attr,
1166                             mEditor.mInputContentType.imeOptions);
1167                     break;
1168
1169                 case com.android.internal.R.styleable.TextView_imeActionLabel:
1170                     createEditorIfNeeded();
1171                     mEditor.createInputContentTypeIfNeeded();
1172                     mEditor.mInputContentType.imeActionLabel = a.getText(attr);
1173                     break;
1174
1175                 case com.android.internal.R.styleable.TextView_imeActionId:
1176                     createEditorIfNeeded();
1177                     mEditor.createInputContentTypeIfNeeded();
1178                     mEditor.mInputContentType.imeActionId = a.getInt(attr,
1179                             mEditor.mInputContentType.imeActionId);
1180                     break;
1181
1182                 case com.android.internal.R.styleable.TextView_privateImeOptions:
1183                     setPrivateImeOptions(a.getString(attr));
1184                     break;
1185
1186                 case com.android.internal.R.styleable.TextView_editorExtras:
1187                     try {
1188                         setInputExtras(a.getResourceId(attr, 0));
1189                     } catch (XmlPullParserException e) {
1190                         Log.w(LOG_TAG, "Failure reading input extras", e);
1191                     } catch (IOException e) {
1192                         Log.w(LOG_TAG, "Failure reading input extras", e);
1193                     }
1194                     break;
1195
1196                 case com.android.internal.R.styleable.TextView_textCursorDrawable:
1197                     mCursorDrawableRes = a.getResourceId(attr, 0);
1198                     break;
1199
1200                 case com.android.internal.R.styleable.TextView_textSelectHandleLeft:
1201                     mTextSelectHandleLeftRes = a.getResourceId(attr, 0);
1202                     break;
1203
1204                 case com.android.internal.R.styleable.TextView_textSelectHandleRight:
1205                     mTextSelectHandleRightRes = a.getResourceId(attr, 0);
1206                     break;
1207
1208                 case com.android.internal.R.styleable.TextView_textSelectHandle:
1209                     mTextSelectHandleRes = a.getResourceId(attr, 0);
1210                     break;
1211
1212                 case com.android.internal.R.styleable.TextView_textEditSuggestionItemLayout:
1213                     mTextEditSuggestionItemLayout = a.getResourceId(attr, 0);
1214                     break;
1215
1216                 case com.android.internal.R.styleable.TextView_textEditSuggestionContainerLayout:
1217                     mTextEditSuggestionContainerLayout = a.getResourceId(attr, 0);
1218                     break;
1219
1220                 case com.android.internal.R.styleable.TextView_textEditSuggestionHighlightStyle:
1221                     mTextEditSuggestionHighlightStyle = a.getResourceId(attr, 0);
1222                     break;
1223
1224                 case com.android.internal.R.styleable.TextView_textIsSelectable:
1225                     setTextIsSelectable(a.getBoolean(attr, false));
1226                     break;
1227
1228                 case com.android.internal.R.styleable.TextView_breakStrategy:
1229                     mBreakStrategy = a.getInt(attr, Layout.BREAK_STRATEGY_SIMPLE);
1230                     break;
1231
1232                 case com.android.internal.R.styleable.TextView_hyphenationFrequency:
1233                     mHyphenationFrequency = a.getInt(attr, Layout.HYPHENATION_FREQUENCY_NONE);
1234                     break;
1235
1236                 case com.android.internal.R.styleable.TextView_autoSizeTextType:
1237                     mAutoSizeTextType = a.getInt(attr, AUTO_SIZE_TEXT_TYPE_NONE);
1238                     break;
1239
1240                 case com.android.internal.R.styleable.TextView_autoSizeStepGranularity:
1241                     autoSizeStepGranularityInPx = a.getDimension(attr,
1242                         UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE);
1243                     break;
1244
1245                 case com.android.internal.R.styleable.TextView_autoSizeMinTextSize:
1246                     autoSizeMinTextSizeInPx = a.getDimension(attr,
1247                         UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE);
1248                     break;
1249
1250                 case com.android.internal.R.styleable.TextView_autoSizeMaxTextSize:
1251                     autoSizeMaxTextSizeInPx = a.getDimension(attr,
1252                         UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE);
1253                     break;
1254
1255                 case com.android.internal.R.styleable.TextView_autoSizePresetSizes:
1256                     final int autoSizeStepSizeArrayResId = a.getResourceId(attr, 0);
1257                     if (autoSizeStepSizeArrayResId > 0) {
1258                         final TypedArray autoSizePresetTextSizes = a.getResources()
1259                                 .obtainTypedArray(autoSizeStepSizeArrayResId);
1260                         setupAutoSizeUniformPresetSizes(autoSizePresetTextSizes);
1261                         autoSizePresetTextSizes.recycle();
1262                     }
1263                     break;
1264                 case com.android.internal.R.styleable.TextView_justificationMode:
1265                     mJustificationMode = a.getInt(attr, Layout.JUSTIFICATION_MODE_NONE);
1266                     break;
1267
1268                 case com.android.internal.R.styleable.TextView_firstBaselineToTopHeight:
1269                     firstBaselineToTopHeight = a.getDimensionPixelSize(attr, -1);
1270                     break;
1271
1272                 case com.android.internal.R.styleable.TextView_lastBaselineToBottomHeight:
1273                     lastBaselineToBottomHeight = a.getDimensionPixelSize(attr, -1);
1274                     break;
1275
1276                 case com.android.internal.R.styleable.TextView_lineHeight:
1277                     lineHeight = a.getDimensionPixelSize(attr, -1);
1278                     break;
1279                 case com.android.internal.R.styleable.TextView_accessibilityHeading:
1280                     mIsAccessibilityHeading = a.getBoolean(attr, false);
1281             }
1282         }
1283
1284         a.recycle();
1285
1286         BufferType bufferType = BufferType.EDITABLE;
1287
1288         final int variation =
1289                 inputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION);
1290         final boolean passwordInputType = variation
1291                 == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);
1292         final boolean webPasswordInputType = variation
1293                 == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD);
1294         final boolean numberPasswordInputType = variation
1295                 == (EditorInfo.TYPE_CLASS_NUMBER | EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD);
1296
1297         final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
1298         mUseInternationalizedInput = targetSdkVersion >= VERSION_CODES.O;
1299         mUseFallbackLineSpacing = targetSdkVersion >= VERSION_CODES.P;
1300
1301         if (inputMethod != null) {
1302             Class<?> c;
1303
1304             try {
1305                 c = Class.forName(inputMethod.toString());
1306             } catch (ClassNotFoundException ex) {
1307                 throw new RuntimeException(ex);
1308             }
1309
1310             try {
1311                 createEditorIfNeeded();
1312                 mEditor.mKeyListener = (KeyListener) c.newInstance();
1313             } catch (InstantiationException ex) {
1314                 throw new RuntimeException(ex);
1315             } catch (IllegalAccessException ex) {
1316                 throw new RuntimeException(ex);
1317             }
1318             try {
1319                 mEditor.mInputType = inputType != EditorInfo.TYPE_NULL
1320                         ? inputType
1321                         : mEditor.mKeyListener.getInputType();
1322             } catch (IncompatibleClassChangeError e) {
1323                 mEditor.mInputType = EditorInfo.TYPE_CLASS_TEXT;
1324             }
1325         } else if (digits != null) {
1326             createEditorIfNeeded();
1327             mEditor.mKeyListener = DigitsKeyListener.getInstance(digits.toString());
1328             // If no input type was specified, we will default to generic
1329             // text, since we can't tell the IME about the set of digits
1330             // that was selected.
1331             mEditor.mInputType = inputType != EditorInfo.TYPE_NULL
1332                     ? inputType : EditorInfo.TYPE_CLASS_TEXT;
1333         } else if (inputType != EditorInfo.TYPE_NULL) {
1334             setInputType(inputType, true);
1335             // If set, the input type overrides what was set using the deprecated singleLine flag.
1336             singleLine = !isMultilineInputType(inputType);
1337         } else if (phone) {
1338             createEditorIfNeeded();
1339             mEditor.mKeyListener = DialerKeyListener.getInstance();
1340             mEditor.mInputType = inputType = EditorInfo.TYPE_CLASS_PHONE;
1341         } else if (numeric != 0) {
1342             createEditorIfNeeded();
1343             mEditor.mKeyListener = DigitsKeyListener.getInstance(
1344                     null,  // locale
1345                     (numeric & SIGNED) != 0,
1346                     (numeric & DECIMAL) != 0);
1347             inputType = mEditor.mKeyListener.getInputType();
1348             mEditor.mInputType = inputType;
1349         } else if (autotext || autocap != -1) {
1350             TextKeyListener.Capitalize cap;
1351
1352             inputType = EditorInfo.TYPE_CLASS_TEXT;
1353
1354             switch (autocap) {
1355                 case 1:
1356                     cap = TextKeyListener.Capitalize.SENTENCES;
1357                     inputType |= EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES;
1358                     break;
1359
1360                 case 2:
1361                     cap = TextKeyListener.Capitalize.WORDS;
1362                     inputType |= EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;
1363                     break;
1364
1365                 case 3:
1366                     cap = TextKeyListener.Capitalize.CHARACTERS;
1367                     inputType |= EditorInfo.TYPE_TEXT_FLAG_CAP_CHARACTERS;
1368                     break;
1369
1370                 default:
1371                     cap = TextKeyListener.Capitalize.NONE;
1372                     break;
1373             }
1374
1375             createEditorIfNeeded();
1376             mEditor.mKeyListener = TextKeyListener.getInstance(autotext, cap);
1377             mEditor.mInputType = inputType;
1378         } else if (editable) {
1379             createEditorIfNeeded();
1380             mEditor.mKeyListener = TextKeyListener.getInstance();
1381             mEditor.mInputType = EditorInfo.TYPE_CLASS_TEXT;
1382         } else if (isTextSelectable()) {
1383             // Prevent text changes from keyboard.
1384             if (mEditor != null) {
1385                 mEditor.mKeyListener = null;
1386                 mEditor.mInputType = EditorInfo.TYPE_NULL;
1387             }
1388             bufferType = BufferType.SPANNABLE;
1389             // So that selection can be changed using arrow keys and touch is handled.
1390             setMovementMethod(ArrowKeyMovementMethod.getInstance());
1391         } else {
1392             if (mEditor != null) mEditor.mKeyListener = null;
1393
1394             switch (buffertype) {
1395                 case 0:
1396                     bufferType = BufferType.NORMAL;
1397                     break;
1398                 case 1:
1399                     bufferType = BufferType.SPANNABLE;
1400                     break;
1401                 case 2:
1402                     bufferType = BufferType.EDITABLE;
1403                     break;
1404             }
1405         }
1406
1407         if (mEditor != null) {
1408             mEditor.adjustInputType(password, passwordInputType, webPasswordInputType,
1409                     numberPasswordInputType);
1410         }
1411
1412         if (selectallonfocus) {
1413             createEditorIfNeeded();
1414             mEditor.mSelectAllOnFocus = true;
1415
1416             if (bufferType == BufferType.NORMAL) {
1417                 bufferType = BufferType.SPANNABLE;
1418             }
1419         }
1420
1421         // Set up the tint (if needed) before setting the drawables so that it
1422         // gets applied correctly.
1423         if (drawableTint != null || drawableTintMode != null) {
1424             if (mDrawables == null) {
1425                 mDrawables = new Drawables(context);
1426             }
1427             if (drawableTint != null) {
1428                 mDrawables.mTintList = drawableTint;
1429                 mDrawables.mHasTint = true;
1430             }
1431             if (drawableTintMode != null) {
1432                 mDrawables.mTintMode = drawableTintMode;
1433                 mDrawables.mHasTintMode = true;
1434             }
1435         }
1436
1437         // This call will save the initial left/right drawables
1438         setCompoundDrawablesWithIntrinsicBounds(
1439                 drawableLeft, drawableTop, drawableRight, drawableBottom);
1440         setRelativeDrawablesIfNeeded(drawableStart, drawableEnd);
1441         setCompoundDrawablePadding(drawablePadding);
1442
1443         // Same as setSingleLine(), but make sure the transformation method and the maximum number
1444         // of lines of height are unchanged for multi-line TextViews.
1445         setInputTypeSingleLine(singleLine);
1446         applySingleLine(singleLine, singleLine, singleLine);
1447
1448         if (singleLine && getKeyListener() == null && ellipsize == ELLIPSIZE_NOT_SET) {
1449             ellipsize = ELLIPSIZE_END;
1450         }
1451
1452         switch (ellipsize) {
1453             case ELLIPSIZE_START:
1454                 setEllipsize(TextUtils.TruncateAt.START);
1455                 break;
1456             case ELLIPSIZE_MIDDLE:
1457                 setEllipsize(TextUtils.TruncateAt.MIDDLE);
1458                 break;
1459             case ELLIPSIZE_END:
1460                 setEllipsize(TextUtils.TruncateAt.END);
1461                 break;
1462             case ELLIPSIZE_MARQUEE:
1463                 if (ViewConfiguration.get(context).isFadingMarqueeEnabled()) {
1464                     setHorizontalFadingEdgeEnabled(true);
1465                     mMarqueeFadeMode = MARQUEE_FADE_NORMAL;
1466                 } else {
1467                     setHorizontalFadingEdgeEnabled(false);
1468                     mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS;
1469                 }
1470                 setEllipsize(TextUtils.TruncateAt.MARQUEE);
1471                 break;
1472         }
1473
1474         final boolean isPassword = password || passwordInputType || webPasswordInputType
1475                 || numberPasswordInputType;
1476         final boolean isMonospaceEnforced = isPassword || (mEditor != null
1477                 && (mEditor.mInputType
1478                 & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION))
1479                 == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD));
1480         if (isMonospaceEnforced) {
1481             attributes.mTypefaceIndex = MONOSPACE;
1482         }
1483
1484         applyTextAppearance(attributes);
1485
1486         if (isPassword) {
1487             setTransformationMethod(PasswordTransformationMethod.getInstance());
1488         }
1489
1490         if (maxlength >= 0) {
1491             setFilters(new InputFilter[] { new InputFilter.LengthFilter(maxlength) });
1492         } else {
1493             setFilters(NO_FILTERS);
1494         }
1495
1496         setText(text, bufferType);
1497         if (textIsSetFromXml) {
1498             mTextSetFromXmlOrResourceId = true;
1499         }
1500
1501         if (hint != null) setHint(hint);
1502
1503         /*
1504          * Views are not normally clickable unless specified to be.
1505          * However, TextViews that have input or movement methods *are*
1506          * clickable by default. By setting clickable here, we implicitly set focusable as well
1507          * if not overridden by the developer.
1508          */
1509         a = context.obtainStyledAttributes(
1510                 attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);
1511         boolean canInputOrMove = (mMovement != null || getKeyListener() != null);
1512         boolean clickable = canInputOrMove || isClickable();
1513         boolean longClickable = canInputOrMove || isLongClickable();
1514         int focusable = getFocusable();
1515
1516         n = a.getIndexCount();
1517         for (int i = 0; i < n; i++) {
1518             int attr = a.getIndex(i);
1519
1520             switch (attr) {
1521                 case com.android.internal.R.styleable.View_focusable:
1522                     TypedValue val = new TypedValue();
1523                     if (a.getValue(attr, val)) {
1524                         focusable = (val.type == TypedValue.TYPE_INT_BOOLEAN)
1525                                 ? (val.data == 0 ? NOT_FOCUSABLE : FOCUSABLE)
1526                                 : val.data;
1527                     }
1528                     break;
1529
1530                 case com.android.internal.R.styleable.View_clickable:
1531                     clickable = a.getBoolean(attr, clickable);
1532                     break;
1533
1534                 case com.android.internal.R.styleable.View_longClickable:
1535                     longClickable = a.getBoolean(attr, longClickable);
1536                     break;
1537             }
1538         }
1539         a.recycle();
1540
1541         // Some apps were relying on the undefined behavior of focusable winning over
1542         // focusableInTouchMode != focusable in TextViews if both were specified in XML (usually
1543         // when starting with EditText and setting only focusable=false). To keep those apps from
1544         // breaking, re-apply the focusable attribute here.
1545         if (focusable != getFocusable()) {
1546             setFocusable(focusable);
1547         }
1548         setClickable(clickable);
1549         setLongClickable(longClickable);
1550
1551         if (mEditor != null) mEditor.prepareCursorControllers();
1552
1553         // If not explicitly specified this view is important for accessibility.
1554         if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
1555             setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
1556         }
1557
1558         if (supportsAutoSizeText()) {
1559             if (mAutoSizeTextType == AUTO_SIZE_TEXT_TYPE_UNIFORM) {
1560                 // If uniform auto-size has been specified but preset values have not been set then
1561                 // replace the auto-size configuration values that have not been specified with the
1562                 // defaults.
1563                 if (!mHasPresetAutoSizeValues) {
1564                     final DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
1565
1566                     if (autoSizeMinTextSizeInPx == UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE) {
1567                         autoSizeMinTextSizeInPx = TypedValue.applyDimension(
1568                                 TypedValue.COMPLEX_UNIT_SP,
1569                                 DEFAULT_AUTO_SIZE_MIN_TEXT_SIZE_IN_SP,
1570                                 displayMetrics);
1571                     }
1572
1573                     if (autoSizeMaxTextSizeInPx == UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE) {
1574                         autoSizeMaxTextSizeInPx = TypedValue.applyDimension(
1575                                 TypedValue.COMPLEX_UNIT_SP,
1576                                 DEFAULT_AUTO_SIZE_MAX_TEXT_SIZE_IN_SP,
1577                                 displayMetrics);
1578                     }
1579
1580                     if (autoSizeStepGranularityInPx
1581                             == UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE) {
1582                         autoSizeStepGranularityInPx = DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX;
1583                     }
1584
1585                     validateAndSetAutoSizeTextTypeUniformConfiguration(autoSizeMinTextSizeInPx,
1586                             autoSizeMaxTextSizeInPx,
1587                             autoSizeStepGranularityInPx);
1588                 }
1589
1590                 setupAutoSizeText();
1591             }
1592         } else {
1593             mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_NONE;
1594         }
1595
1596         if (firstBaselineToTopHeight >= 0) {
1597             setFirstBaselineToTopHeight(firstBaselineToTopHeight);
1598         }
1599         if (lastBaselineToBottomHeight >= 0) {
1600             setLastBaselineToBottomHeight(lastBaselineToBottomHeight);
1601         }
1602         if (lineHeight >= 0) {
1603             setLineHeight(lineHeight);
1604         }
1605     }
1606
1607     /**
1608      * Specify whether this widget should automatically scale the text to try to perfectly fit
1609      * within the layout bounds by using the default auto-size configuration.
1610      *
1611      * @param autoSizeTextType the type of auto-size. Must be one of
1612      *        {@link TextView#AUTO_SIZE_TEXT_TYPE_NONE} or
1613      *        {@link TextView#AUTO_SIZE_TEXT_TYPE_UNIFORM}
1614      *
1615      * @throws IllegalArgumentException if <code>autoSizeTextType</code> is none of the types above.
1616      *
1617      * @attr ref android.R.styleable#TextView_autoSizeTextType
1618      *
1619      * @see #getAutoSizeTextType()
1620      */
1621     public void setAutoSizeTextTypeWithDefaults(@AutoSizeTextType int autoSizeTextType) {
1622         if (supportsAutoSizeText()) {
1623             switch (autoSizeTextType) {
1624                 case AUTO_SIZE_TEXT_TYPE_NONE:
1625                     clearAutoSizeConfiguration();
1626                     break;
1627                 case AUTO_SIZE_TEXT_TYPE_UNIFORM:
1628                     final DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
1629                     final float autoSizeMinTextSizeInPx = TypedValue.applyDimension(
1630                             TypedValue.COMPLEX_UNIT_SP,
1631                             DEFAULT_AUTO_SIZE_MIN_TEXT_SIZE_IN_SP,
1632                             displayMetrics);
1633                     final float autoSizeMaxTextSizeInPx = TypedValue.applyDimension(
1634                             TypedValue.COMPLEX_UNIT_SP,
1635                             DEFAULT_AUTO_SIZE_MAX_TEXT_SIZE_IN_SP,
1636                             displayMetrics);
1637
1638                     validateAndSetAutoSizeTextTypeUniformConfiguration(
1639                             autoSizeMinTextSizeInPx,
1640                             autoSizeMaxTextSizeInPx,
1641                             DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX);
1642                     if (setupAutoSizeText()) {
1643                         autoSizeText();
1644                         invalidate();
1645                     }
1646                     break;
1647                 default:
1648                     throw new IllegalArgumentException(
1649                             "Unknown auto-size text type: " + autoSizeTextType);
1650             }
1651         }
1652     }
1653
1654     /**
1655      * Specify whether this widget should automatically scale the text to try to perfectly fit
1656      * within the layout bounds. If all the configuration params are valid the type of auto-size is
1657      * set to {@link #AUTO_SIZE_TEXT_TYPE_UNIFORM}.
1658      *
1659      * @param autoSizeMinTextSize the minimum text size available for auto-size
1660      * @param autoSizeMaxTextSize the maximum text size available for auto-size
1661      * @param autoSizeStepGranularity the auto-size step granularity. It is used in conjunction with
1662      *                                the minimum and maximum text size in order to build the set of
1663      *                                text sizes the system uses to choose from when auto-sizing
1664      * @param unit the desired dimension unit for all sizes above. See {@link TypedValue} for the
1665      *             possible dimension units
1666      *
1667      * @throws IllegalArgumentException if any of the configuration params are invalid.
1668      *
1669      * @attr ref android.R.styleable#TextView_autoSizeTextType
1670      * @attr ref android.R.styleable#TextView_autoSizeMinTextSize
1671      * @attr ref android.R.styleable#TextView_autoSizeMaxTextSize
1672      * @attr ref android.R.styleable#TextView_autoSizeStepGranularity
1673      *
1674      * @see #setAutoSizeTextTypeWithDefaults(int)
1675      * @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
1676      * @see #getAutoSizeMinTextSize()
1677      * @see #getAutoSizeMaxTextSize()
1678      * @see #getAutoSizeStepGranularity()
1679      * @see #getAutoSizeTextAvailableSizes()
1680      */
1681     public void setAutoSizeTextTypeUniformWithConfiguration(int autoSizeMinTextSize,
1682             int autoSizeMaxTextSize, int autoSizeStepGranularity, int unit) {
1683         if (supportsAutoSizeText()) {
1684             final DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
1685             final float autoSizeMinTextSizeInPx = TypedValue.applyDimension(
1686                     unit, autoSizeMinTextSize, displayMetrics);
1687             final float autoSizeMaxTextSizeInPx = TypedValue.applyDimension(
1688                     unit, autoSizeMaxTextSize, displayMetrics);
1689             final float autoSizeStepGranularityInPx = TypedValue.applyDimension(
1690                     unit, autoSizeStepGranularity, displayMetrics);
1691
1692             validateAndSetAutoSizeTextTypeUniformConfiguration(autoSizeMinTextSizeInPx,
1693                     autoSizeMaxTextSizeInPx,
1694                     autoSizeStepGranularityInPx);
1695
1696             if (setupAutoSizeText()) {
1697                 autoSizeText();
1698                 invalidate();
1699             }
1700         }
1701     }
1702
1703     /**
1704      * Specify whether this widget should automatically scale the text to try to perfectly fit
1705      * within the layout bounds. If at least one value from the <code>presetSizes</code> is valid
1706      * then the type of auto-size is set to {@link #AUTO_SIZE_TEXT_TYPE_UNIFORM}.
1707      *
1708      * @param presetSizes an {@code int} array of sizes in pixels
1709      * @param unit the desired dimension unit for the preset sizes above. See {@link TypedValue} for
1710      *             the possible dimension units
1711      *
1712      * @throws IllegalArgumentException if all of the <code>presetSizes</code> are invalid.
1713      *
1714      * @attr ref android.R.styleable#TextView_autoSizeTextType
1715      * @attr ref android.R.styleable#TextView_autoSizePresetSizes
1716      *
1717      * @see #setAutoSizeTextTypeWithDefaults(int)
1718      * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
1719      * @see #getAutoSizeMinTextSize()
1720      * @see #getAutoSizeMaxTextSize()
1721      * @see #getAutoSizeTextAvailableSizes()
1722      */
1723     public void setAutoSizeTextTypeUniformWithPresetSizes(@NonNull int[] presetSizes, int unit) {
1724         if (supportsAutoSizeText()) {
1725             final int presetSizesLength = presetSizes.length;
1726             if (presetSizesLength > 0) {
1727                 int[] presetSizesInPx = new int[presetSizesLength];
1728
1729                 if (unit == TypedValue.COMPLEX_UNIT_PX) {
1730                     presetSizesInPx = Arrays.copyOf(presetSizes, presetSizesLength);
1731                 } else {
1732                     final DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
1733                     // Convert all to sizes to pixels.
1734                     for (int i = 0; i < presetSizesLength; i++) {
1735                         presetSizesInPx[i] = Math.round(TypedValue.applyDimension(unit,
1736                             presetSizes[i], displayMetrics));
1737                     }
1738                 }
1739
1740                 mAutoSizeTextSizesInPx = cleanupAutoSizePresetSizes(presetSizesInPx);
1741                 if (!setupAutoSizeUniformPresetSizesConfiguration()) {
1742                     throw new IllegalArgumentException("None of the preset sizes is valid: "
1743                             + Arrays.toString(presetSizes));
1744                 }
1745             } else {
1746                 mHasPresetAutoSizeValues = false;
1747             }
1748
1749             if (setupAutoSizeText()) {
1750                 autoSizeText();
1751                 invalidate();
1752             }
1753         }
1754     }
1755
1756     /**
1757      * Returns the type of auto-size set for this widget.
1758      *
1759      * @return an {@code int} corresponding to one of the auto-size types:
1760      *         {@link TextView#AUTO_SIZE_TEXT_TYPE_NONE} or
1761      *         {@link TextView#AUTO_SIZE_TEXT_TYPE_UNIFORM}
1762      *
1763      * @attr ref android.R.styleable#TextView_autoSizeTextType
1764      *
1765      * @see #setAutoSizeTextTypeWithDefaults(int)
1766      * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
1767      * @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
1768      */
1769     @AutoSizeTextType
1770     public int getAutoSizeTextType() {
1771         return mAutoSizeTextType;
1772     }
1773
1774     /**
1775      * @return the current auto-size step granularity in pixels.
1776      *
1777      * @attr ref android.R.styleable#TextView_autoSizeStepGranularity
1778      *
1779      * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
1780      */
1781     public int getAutoSizeStepGranularity() {
1782         return Math.round(mAutoSizeStepGranularityInPx);
1783     }
1784
1785     /**
1786      * @return the current auto-size minimum text size in pixels (the default is 12sp). Note that
1787      *         if auto-size has not been configured this function returns {@code -1}.
1788      *
1789      * @attr ref android.R.styleable#TextView_autoSizeMinTextSize
1790      *
1791      * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
1792      * @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
1793      */
1794     public int getAutoSizeMinTextSize() {
1795         return Math.round(mAutoSizeMinTextSizeInPx);
1796     }
1797
1798     /**
1799      * @return the current auto-size maximum text size in pixels (the default is 112sp). Note that
1800      *         if auto-size has not been configured this function returns {@code -1}.
1801      *
1802      * @attr ref android.R.styleable#TextView_autoSizeMaxTextSize
1803      *
1804      * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
1805      * @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
1806      */
1807     public int getAutoSizeMaxTextSize() {
1808         return Math.round(mAutoSizeMaxTextSizeInPx);
1809     }
1810
1811     /**
1812      * @return the current auto-size {@code int} sizes array (in pixels).
1813      *
1814      * @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
1815      * @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
1816      */
1817     public int[] getAutoSizeTextAvailableSizes() {
1818         return mAutoSizeTextSizesInPx;
1819     }
1820
1821     private void setupAutoSizeUniformPresetSizes(TypedArray textSizes) {
1822         final int textSizesLength = textSizes.length();
1823         final int[] parsedSizes = new int[textSizesLength];
1824
1825         if (textSizesLength > 0) {
1826             for (int i = 0; i < textSizesLength; i++) {
1827                 parsedSizes[i] = textSizes.getDimensionPixelSize(i, -1);
1828             }
1829             mAutoSizeTextSizesInPx = cleanupAutoSizePresetSizes(parsedSizes);
1830             setupAutoSizeUniformPresetSizesConfiguration();
1831         }
1832     }
1833
1834     private boolean setupAutoSizeUniformPresetSizesConfiguration() {
1835         final int sizesLength = mAutoSizeTextSizesInPx.length;
1836         mHasPresetAutoSizeValues = sizesLength > 0;
1837         if (mHasPresetAutoSizeValues) {
1838             mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_UNIFORM;
1839             mAutoSizeMinTextSizeInPx = mAutoSizeTextSizesInPx[0];
1840             mAutoSizeMaxTextSizeInPx = mAutoSizeTextSizesInPx[sizesLength - 1];
1841             mAutoSizeStepGranularityInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
1842         }
1843         return mHasPresetAutoSizeValues;
1844     }
1845
1846     /**
1847      * If all params are valid then save the auto-size configuration.
1848      *
1849      * @throws IllegalArgumentException if any of the params are invalid
1850      */
1851     private void validateAndSetAutoSizeTextTypeUniformConfiguration(float autoSizeMinTextSizeInPx,
1852             float autoSizeMaxTextSizeInPx, float autoSizeStepGranularityInPx) {
1853         // First validate.
1854         if (autoSizeMinTextSizeInPx <= 0) {
1855             throw new IllegalArgumentException("Minimum auto-size text size ("
1856                 + autoSizeMinTextSizeInPx  + "px) is less or equal to (0px)");
1857         }
1858
1859         if (autoSizeMaxTextSizeInPx <= autoSizeMinTextSizeInPx) {
1860             throw new IllegalArgumentException("Maximum auto-size text size ("
1861                 + autoSizeMaxTextSizeInPx + "px) is less or equal to minimum auto-size "
1862                 + "text size (" + autoSizeMinTextSizeInPx + "px)");
1863         }
1864
1865         if (autoSizeStepGranularityInPx <= 0) {
1866             throw new IllegalArgumentException("The auto-size step granularity ("
1867                 + autoSizeStepGranularityInPx + "px) is less or equal to (0px)");
1868         }
1869
1870         // All good, persist the configuration.
1871         mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_UNIFORM;
1872         mAutoSizeMinTextSizeInPx = autoSizeMinTextSizeInPx;
1873         mAutoSizeMaxTextSizeInPx = autoSizeMaxTextSizeInPx;
1874         mAutoSizeStepGranularityInPx = autoSizeStepGranularityInPx;
1875         mHasPresetAutoSizeValues = false;
1876     }
1877
1878     private void clearAutoSizeConfiguration() {
1879         mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_NONE;
1880         mAutoSizeMinTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
1881         mAutoSizeMaxTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
1882         mAutoSizeStepGranularityInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
1883         mAutoSizeTextSizesInPx = EmptyArray.INT;
1884         mNeedsAutoSizeText = false;
1885     }
1886
1887     // Returns distinct sorted positive values.
1888     private int[] cleanupAutoSizePresetSizes(int[] presetValues) {
1889         final int presetValuesLength = presetValues.length;
1890         if (presetValuesLength == 0) {
1891             return presetValues;
1892         }
1893         Arrays.sort(presetValues);
1894
1895         final IntArray uniqueValidSizes = new IntArray();
1896         for (int i = 0; i < presetValuesLength; i++) {
1897             final int currentPresetValue = presetValues[i];
1898
1899             if (currentPresetValue > 0
1900                     && uniqueValidSizes.binarySearch(currentPresetValue) < 0) {
1901                 uniqueValidSizes.add(currentPresetValue);
1902             }
1903         }
1904
1905         return presetValuesLength == uniqueValidSizes.size()
1906             ? presetValues
1907             : uniqueValidSizes.toArray();
1908     }
1909
1910     private boolean setupAutoSizeText() {
1911         if (supportsAutoSizeText() && mAutoSizeTextType == AUTO_SIZE_TEXT_TYPE_UNIFORM) {
1912             // Calculate the sizes set based on minimum size, maximum size and step size if we do
1913             // not have a predefined set of sizes or if the current sizes array is empty.
1914             if (!mHasPresetAutoSizeValues || mAutoSizeTextSizesInPx.length == 0) {
1915                 int autoSizeValuesLength = 1;
1916                 float currentSize = Math.round(mAutoSizeMinTextSizeInPx);
1917                 while (Math.round(currentSize + mAutoSizeStepGranularityInPx)
1918                         <= Math.round(mAutoSizeMaxTextSizeInPx)) {
1919                     autoSizeValuesLength++;
1920                     currentSize += mAutoSizeStepGranularityInPx;
1921                 }
1922
1923                 int[] autoSizeTextSizesInPx = new int[autoSizeValuesLength];
1924                 float sizeToAdd = mAutoSizeMinTextSizeInPx;
1925                 for (int i = 0; i < autoSizeValuesLength; i++) {
1926                     autoSizeTextSizesInPx[i] = Math.round(sizeToAdd);
1927                     sizeToAdd += mAutoSizeStepGranularityInPx;
1928                 }
1929                 mAutoSizeTextSizesInPx = cleanupAutoSizePresetSizes(autoSizeTextSizesInPx);
1930             }
1931
1932             mNeedsAutoSizeText = true;
1933         } else {
1934             mNeedsAutoSizeText = false;
1935         }
1936
1937         return mNeedsAutoSizeText;
1938     }
1939
1940     private int[] parseDimensionArray(TypedArray dimens) {
1941         if (dimens == null) {
1942             return null;
1943         }
1944         int[] result = new int[dimens.length()];
1945         for (int i = 0; i < result.length; i++) {
1946             result[i] = dimens.getDimensionPixelSize(i, 0);
1947         }
1948         return result;
1949     }
1950
1951     /**
1952      * @hide
1953      */
1954     @Override
1955     public void onActivityResult(int requestCode, int resultCode, Intent data) {
1956         if (requestCode == PROCESS_TEXT_REQUEST_CODE) {
1957             if (resultCode == Activity.RESULT_OK && data != null) {
1958                 CharSequence result = data.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEXT);
1959                 if (result != null) {
1960                     if (isTextEditable()) {
1961                         replaceSelectionWithText(result);
1962                         if (mEditor != null) {
1963                             mEditor.refreshTextActionMode();
1964                         }
1965                     } else {
1966                         if (result.length() > 0) {
1967                             Toast.makeText(getContext(), String.valueOf(result), Toast.LENGTH_LONG)
1968                                 .show();
1969                         }
1970                     }
1971                 }
1972             } else if (mText instanceof Spannable) {
1973                 // Reset the selection.
1974                 Selection.setSelection((Spannable) mText, getSelectionEnd());
1975             }
1976         }
1977     }
1978
1979     private void setTypefaceFromAttrs(Typeface fontTypeface, String familyName, int typefaceIndex,
1980             int styleIndex) {
1981         Typeface tf = fontTypeface;
1982         if (tf == null && familyName != null) {
1983             tf = Typeface.create(familyName, styleIndex);
1984         } else if (tf != null && tf.getStyle() != styleIndex) {
1985             tf = Typeface.create(tf, styleIndex);
1986         }
1987         if (tf != null) {
1988             setTypeface(tf);
1989             return;
1990         }
1991         switch (typefaceIndex) {
1992             case SANS:
1993                 tf = Typeface.SANS_SERIF;
1994                 break;
1995
1996             case SERIF:
1997                 tf = Typeface.SERIF;
1998                 break;
1999
2000             case MONOSPACE:
2001                 tf = Typeface.MONOSPACE;
2002                 break;
2003         }
2004
2005         setTypeface(tf, styleIndex);
2006     }
2007
2008     private void setRelativeDrawablesIfNeeded(Drawable start, Drawable end) {
2009         boolean hasRelativeDrawables = (start != null) || (end != null);
2010         if (hasRelativeDrawables) {
2011             Drawables dr = mDrawables;
2012             if (dr == null) {
2013                 mDrawables = dr = new Drawables(getContext());
2014             }
2015             mDrawables.mOverride = true;
2016             final Rect compoundRect = dr.mCompoundRect;
2017             int[] state = getDrawableState();
2018             if (start != null) {
2019                 start.setBounds(0, 0, start.getIntrinsicWidth(), start.getIntrinsicHeight());
2020                 start.setState(state);
2021                 start.copyBounds(compoundRect);
2022                 start.setCallback(this);
2023
2024                 dr.mDrawableStart = start;
2025                 dr.mDrawableSizeStart = compoundRect.width();
2026                 dr.mDrawableHeightStart = compoundRect.height();
2027             } else {
2028                 dr.mDrawableSizeStart = dr.mDrawableHeightStart = 0;
2029             }
2030             if (end != null) {
2031                 end.setBounds(0, 0, end.getIntrinsicWidth(), end.getIntrinsicHeight());
2032                 end.setState(state);
2033                 end.copyBounds(compoundRect);
2034                 end.setCallback(this);
2035
2036                 dr.mDrawableEnd = end;
2037                 dr.mDrawableSizeEnd = compoundRect.width();
2038                 dr.mDrawableHeightEnd = compoundRect.height();
2039             } else {
2040                 dr.mDrawableSizeEnd = dr.mDrawableHeightEnd = 0;
2041             }
2042             resetResolvedDrawables();
2043             resolveDrawables();
2044             applyCompoundDrawableTint();
2045         }
2046     }
2047
2048     @android.view.RemotableViewMethod
2049     @Override
2050     public void setEnabled(boolean enabled) {
2051         if (enabled == isEnabled()) {
2052             return;
2053         }
2054
2055         if (!enabled) {
2056             // Hide the soft input if the currently active TextView is disabled
2057             InputMethodManager imm = InputMethodManager.peekInstance();
2058             if (imm != null && imm.isActive(this)) {
2059                 imm.hideSoftInputFromWindow(getWindowToken(), 0);
2060             }
2061         }
2062
2063         super.setEnabled(enabled);
2064
2065         if (enabled) {
2066             // Make sure IME is updated with current editor info.
2067             InputMethodManager imm = InputMethodManager.peekInstance();
2068             if (imm != null) imm.restartInput(this);
2069         }
2070
2071         // Will change text color
2072         if (mEditor != null) {
2073             mEditor.invalidateTextDisplayList();
2074             mEditor.prepareCursorControllers();
2075
2076             // start or stop the cursor blinking as appropriate
2077             mEditor.makeBlink();
2078         }
2079     }
2080
2081     /**
2082      * Sets the typeface and style in which the text should be displayed,
2083      * and turns on the fake bold and italic bits in the Paint if the
2084      * Typeface that you provided does not have all the bits in the
2085      * style that you specified.
2086      *
2087      * @attr ref android.R.styleable#TextView_typeface
2088      * @attr ref android.R.styleable#TextView_textStyle
2089      */
2090     public void setTypeface(Typeface tf, int style) {
2091         if (style > 0) {
2092             if (tf == null) {
2093                 tf = Typeface.defaultFromStyle(style);
2094             } else {
2095                 tf = Typeface.create(tf, style);
2096             }
2097
2098             setTypeface(tf);
2099             // now compute what (if any) algorithmic styling is needed
2100             int typefaceStyle = tf != null ? tf.getStyle() : 0;
2101             int need = style & ~typefaceStyle;
2102             mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
2103             mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
2104         } else {
2105             mTextPaint.setFakeBoldText(false);
2106             mTextPaint.setTextSkewX(0);
2107             setTypeface(tf);
2108         }
2109     }
2110
2111     /**
2112      * Subclasses override this to specify that they have a KeyListener
2113      * by default even if not specifically called for in the XML options.
2114      */
2115     protected boolean getDefaultEditable() {
2116         return false;
2117     }
2118
2119     /**
2120      * Subclasses override this to specify a default movement method.
2121      */
2122     protected MovementMethod getDefaultMovementMethod() {
2123         return null;
2124     }
2125
2126     /**
2127      * Return the text that TextView is displaying. If {@link #setText(CharSequence)} was called
2128      * with an argument of {@link android.widget.TextView.BufferType#SPANNABLE BufferType.SPANNABLE}
2129      * or {@link android.widget.TextView.BufferType#EDITABLE BufferType.EDITABLE}, you can cast
2130      * the return value from this method to Spannable or Editable, respectively.
2131      *
2132      * <p>The content of the return value should not be modified. If you want a modifiable one, you
2133      * should make your own copy first.</p>
2134      *
2135      * @return The text displayed by the text view.
2136      * @attr ref android.R.styleable#TextView_text
2137      */
2138     @ViewDebug.CapturedViewProperty
2139     public CharSequence getText() {
2140         return mText;
2141     }
2142
2143     /**
2144      * Returns the length, in characters, of the text managed by this TextView
2145      * @return The length of the text managed by the TextView in characters.
2146      */
2147     public int length() {
2148         return mText.length();
2149     }
2150
2151     /**
2152      * Return the text that TextView is displaying as an Editable object. If the text is not
2153      * editable, null is returned.
2154      *
2155      * @see #getText
2156      */
2157     public Editable getEditableText() {
2158         return (mText instanceof Editable) ? (Editable) mText : null;
2159     }
2160
2161     /**
2162      * Gets the vertical distance between lines of text, in pixels.
2163      * Note that markup within the text can cause individual lines
2164      * to be taller or shorter than this height, and the layout may
2165      * contain additional first-or last-line padding.
2166      * @return The height of one standard line in pixels.
2167      */
2168     public int getLineHeight() {
2169         return FastMath.round(mTextPaint.getFontMetricsInt(null) * mSpacingMult + mSpacingAdd);
2170     }
2171
2172     /**
2173      * Gets the {@link android.text.Layout} that is currently being used to display the text.
2174      * This value can be null if the text or width has recently changed.
2175      * @return The Layout that is currently being used to display the text.
2176      */
2177     public final Layout getLayout() {
2178         return mLayout;
2179     }
2180
2181     /**
2182      * @return the {@link android.text.Layout} that is currently being used to
2183      * display the hint text. This can be null.
2184      */
2185     final Layout getHintLayout() {
2186         return mHintLayout;
2187     }
2188
2189     /**
2190      * Retrieve the {@link android.content.UndoManager} that is currently associated
2191      * with this TextView.  By default there is no associated UndoManager, so null
2192      * is returned.  One can be associated with the TextView through
2193      * {@link #setUndoManager(android.content.UndoManager, String)}
2194      *
2195      * @hide
2196      */
2197     public final UndoManager getUndoManager() {
2198         // TODO: Consider supporting a global undo manager.
2199         throw new UnsupportedOperationException("not implemented");
2200     }
2201
2202
2203     /**
2204      * @hide
2205      */
2206     @VisibleForTesting
2207     public final Editor getEditorForTesting() {
2208         return mEditor;
2209     }
2210
2211     /**
2212      * Associate an {@link android.content.UndoManager} with this TextView.  Once
2213      * done, all edit operations on the TextView will result in appropriate
2214      * {@link android.content.UndoOperation} objects pushed on the given UndoManager's
2215      * stack.
2216      *
2217      * @param undoManager The {@link android.content.UndoManager} to associate with
2218      * this TextView, or null to clear any existing association.
2219      * @param tag String tag identifying this particular TextView owner in the
2220      * UndoManager.  This is used to keep the correct association with the
2221      * {@link android.content.UndoOwner} of any operations inside of the UndoManager.
2222      *
2223      * @hide
2224      */
2225     public final void setUndoManager(UndoManager undoManager, String tag) {
2226         // TODO: Consider supporting a global undo manager. An implementation will need to:
2227         // * createEditorIfNeeded()
2228         // * Promote to BufferType.EDITABLE if needed.
2229         // * Update the UndoManager and UndoOwner.
2230         // Likewise it will need to be able to restore the default UndoManager.
2231         throw new UnsupportedOperationException("not implemented");
2232     }
2233
2234     /**
2235      * Gets the current {@link KeyListener} for the TextView.
2236      * This will frequently be null for non-EditText TextViews.
2237      * @return the current key listener for this TextView.
2238      *
2239      * @attr ref android.R.styleable#TextView_numeric
2240      * @attr ref android.R.styleable#TextView_digits
2241      * @attr ref android.R.styleable#TextView_phoneNumber
2242      * @attr ref android.R.styleable#TextView_inputMethod
2243      * @attr ref android.R.styleable#TextView_capitalize
2244      * @attr ref android.R.styleable#TextView_autoText
2245      */
2246     public final KeyListener getKeyListener() {
2247         return mEditor == null ? null : mEditor.mKeyListener;
2248     }
2249
2250     /**
2251      * Sets the key listener to be used with this TextView.  This can be null
2252      * to disallow user input.  Note that this method has significant and
2253      * subtle interactions with soft keyboards and other input method:
2254      * see {@link KeyListener#getInputType() KeyListener.getContentType()}
2255      * for important details.  Calling this method will replace the current
2256      * content type of the text view with the content type returned by the
2257      * key listener.
2258      * <p>
2259      * Be warned that if you want a TextView with a key listener or movement
2260      * method not to be focusable, or if you want a TextView without a
2261      * key listener or movement method to be focusable, you must call
2262      * {@link #setFocusable} again after calling this to get the focusability
2263      * back the way you want it.
2264      *
2265      * @attr ref android.R.styleable#TextView_numeric
2266      * @attr ref android.R.styleable#TextView_digits
2267      * @attr ref android.R.styleable#TextView_phoneNumber
2268      * @attr ref android.R.styleable#TextView_inputMethod
2269      * @attr ref android.R.styleable#TextView_capitalize
2270      * @attr ref android.R.styleable#TextView_autoText
2271      */
2272     public void setKeyListener(KeyListener input) {
2273         mListenerChanged = true;
2274         setKeyListenerOnly(input);
2275         fixFocusableAndClickableSettings();
2276
2277         if (input != null) {
2278             createEditorIfNeeded();
2279             setInputTypeFromEditor();
2280         } else {
2281             if (mEditor != null) mEditor.mInputType = EditorInfo.TYPE_NULL;
2282         }
2283
2284         InputMethodManager imm = InputMethodManager.peekInstance();
2285         if (imm != null) imm.restartInput(this);
2286     }
2287
2288     private void setInputTypeFromEditor() {
2289         try {
2290             mEditor.mInputType = mEditor.mKeyListener.getInputType();
2291         } catch (IncompatibleClassChangeError e) {
2292             mEditor.mInputType = EditorInfo.TYPE_CLASS_TEXT;
2293         }
2294         // Change inputType, without affecting transformation.
2295         // No need to applySingleLine since mSingleLine is unchanged.
2296         setInputTypeSingleLine(mSingleLine);
2297     }
2298
2299     private void setKeyListenerOnly(KeyListener input) {
2300         if (mEditor == null && input == null) return; // null is the default value
2301
2302         createEditorIfNeeded();
2303         if (mEditor.mKeyListener != input) {
2304             mEditor.mKeyListener = input;
2305             if (input != null && !(mText instanceof Editable)) {
2306                 setText(mText);
2307             }
2308
2309             setFilters((Editable) mText, mFilters);
2310         }
2311     }
2312
2313     /**
2314      * Gets the {@link android.text.method.MovementMethod} being used for this TextView,
2315      * which provides positioning, scrolling, and text selection functionality.
2316      * This will frequently be null for non-EditText TextViews.
2317      * @return the movement method being used for this TextView.
2318      * @see android.text.method.MovementMethod
2319      */
2320     public final MovementMethod getMovementMethod() {
2321         return mMovement;
2322     }
2323
2324     /**
2325      * Sets the {@link android.text.method.MovementMethod} for handling arrow key movement
2326      * for this TextView. This can be null to disallow using the arrow keys to move the
2327      * cursor or scroll the view.
2328      * <p>
2329      * Be warned that if you want a TextView with a key listener or movement
2330      * method not to be focusable, or if you want a TextView without a
2331      * key listener or movement method to be focusable, you must call
2332      * {@link #setFocusable} again after calling this to get the focusability
2333      * back the way you want it.
2334      */
2335     public final void setMovementMethod(MovementMethod movement) {
2336         if (mMovement != movement) {
2337             mMovement = movement;
2338
2339             if (movement != null && !(mText instanceof Spannable)) {
2340                 setText(mText);
2341             }
2342
2343             fixFocusableAndClickableSettings();
2344
2345             // SelectionModifierCursorController depends on textCanBeSelected, which depends on
2346             // mMovement
2347             if (mEditor != null) mEditor.prepareCursorControllers();
2348         }
2349     }
2350
2351     private void fixFocusableAndClickableSettings() {
2352         if (mMovement != null || (mEditor != null && mEditor.mKeyListener != null)) {
2353             setFocusable(FOCUSABLE);
2354             setClickable(true);
2355             setLongClickable(true);
2356         } else {
2357             setFocusable(FOCUSABLE_AUTO);
2358             setClickable(false);
2359             setLongClickable(false);
2360         }
2361     }
2362
2363     /**
2364      * Gets the current {@link android.text.method.TransformationMethod} for the TextView.
2365      * This is frequently null, except for single-line and password fields.
2366      * @return the current transformation method for this TextView.
2367      *
2368      * @attr ref android.R.styleable#TextView_password
2369      * @attr ref android.R.styleable#TextView_singleLine
2370      */
2371     public final TransformationMethod getTransformationMethod() {
2372         return mTransformation;
2373     }
2374
2375     /**
2376      * Sets the transformation that is applied to the text that this
2377      * TextView is displaying.
2378      *
2379      * @attr ref android.R.styleable#TextView_password
2380      * @attr ref android.R.styleable#TextView_singleLine
2381      */
2382     public final void setTransformationMethod(TransformationMethod method) {
2383         if (method == mTransformation) {
2384             // Avoid the setText() below if the transformation is
2385             // the same.
2386             return;
2387         }
2388         if (mTransformation != null) {
2389             if (mText instanceof Spannable) {
2390                 ((Spannable) mText).removeSpan(mTransformation);
2391             }
2392         }
2393
2394         mTransformation = method;
2395
2396         if (method instanceof TransformationMethod2) {
2397             TransformationMethod2 method2 = (TransformationMethod2) method;
2398             mAllowTransformationLengthChange = !isTextSelectable() && !(mText instanceof Editable);
2399             method2.setLengthChangesAllowed(mAllowTransformationLengthChange);
2400         } else {
2401             mAllowTransformationLengthChange = false;
2402         }
2403
2404         setText(mText);
2405
2406         if (hasPasswordTransformationMethod()) {
2407             notifyViewAccessibilityStateChangedIfNeeded(
2408                     AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
2409         }
2410
2411         // PasswordTransformationMethod always have LTR text direction heuristics returned by
2412         // getTextDirectionHeuristic, needs reset
2413         mTextDir = getTextDirectionHeuristic();
2414     }
2415
2416     /**
2417      * Returns the top padding of the view, plus space for the top
2418      * Drawable if any.
2419      */
2420     public int getCompoundPaddingTop() {
2421         final Drawables dr = mDrawables;
2422         if (dr == null || dr.mShowing[Drawables.TOP] == null) {
2423             return mPaddingTop;
2424         } else {
2425             return mPaddingTop + dr.mDrawablePadding + dr.mDrawableSizeTop;
2426         }
2427     }
2428
2429     /**
2430      * Returns the bottom padding of the view, plus space for the bottom
2431      * Drawable if any.
2432      */
2433     public int getCompoundPaddingBottom() {
2434         final Drawables dr = mDrawables;
2435         if (dr == null || dr.mShowing[Drawables.BOTTOM] == null) {
2436             return mPaddingBottom;
2437         } else {
2438             return mPaddingBottom + dr.mDrawablePadding + dr.mDrawableSizeBottom;
2439         }
2440     }
2441
2442     /**
2443      * Returns the left padding of the view, plus space for the left
2444      * Drawable if any.
2445      */
2446     public int getCompoundPaddingLeft() {
2447         final Drawables dr = mDrawables;
2448         if (dr == null || dr.mShowing[Drawables.LEFT] == null) {
2449             return mPaddingLeft;
2450         } else {
2451             return mPaddingLeft + dr.mDrawablePadding + dr.mDrawableSizeLeft;
2452         }
2453     }
2454
2455     /**
2456      * Returns the right padding of the view, plus space for the right
2457      * Drawable if any.
2458      */
2459     public int getCompoundPaddingRight() {
2460         final Drawables dr = mDrawables;
2461         if (dr == null || dr.mShowing[Drawables.RIGHT] == null) {
2462             return mPaddingRight;
2463         } else {
2464             return mPaddingRight + dr.mDrawablePadding + dr.mDrawableSizeRight;
2465         }
2466     }
2467
2468     /**
2469      * Returns the start padding of the view, plus space for the start
2470      * Drawable if any.
2471      */
2472     public int getCompoundPaddingStart() {
2473         resolveDrawables();
2474         switch(getLayoutDirection()) {
2475             default:
2476             case LAYOUT_DIRECTION_LTR:
2477                 return getCompoundPaddingLeft();
2478             case LAYOUT_DIRECTION_RTL:
2479                 return getCompoundPaddingRight();
2480         }
2481     }
2482
2483     /**
2484      * Returns the end padding of the view, plus space for the end
2485      * Drawable if any.
2486      */
2487     public int getCompoundPaddingEnd() {
2488         resolveDrawables();
2489         switch(getLayoutDirection()) {
2490             default:
2491             case LAYOUT_DIRECTION_LTR:
2492                 return getCompoundPaddingRight();
2493             case LAYOUT_DIRECTION_RTL:
2494                 return getCompoundPaddingLeft();
2495         }
2496     }
2497
2498     /**
2499      * Returns the extended top padding of the view, including both the
2500      * top Drawable if any and any extra space to keep more than maxLines
2501      * of text from showing.  It is only valid to call this after measuring.
2502      */
2503     public int getExtendedPaddingTop() {
2504         if (mMaxMode != LINES) {
2505             return getCompoundPaddingTop();
2506         }
2507
2508         if (mLayout == null) {
2509             assumeLayout();
2510         }
2511
2512         if (mLayout.getLineCount() <= mMaximum) {
2513             return getCompoundPaddingTop();
2514         }
2515
2516         int top = getCompoundPaddingTop();
2517         int bottom = getCompoundPaddingBottom();
2518         int viewht = getHeight() - top - bottom;
2519         int layoutht = mLayout.getLineTop(mMaximum);
2520
2521         if (layoutht >= viewht) {
2522             return top;
2523         }
2524
2525         final int gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
2526         if (gravity == Gravity.TOP) {
2527             return top;
2528         } else if (gravity == Gravity.BOTTOM) {
2529             return top + viewht - layoutht;
2530         } else { // (gravity == Gravity.CENTER_VERTICAL)
2531             return top + (viewht - layoutht) / 2;
2532         }
2533     }
2534
2535     /**
2536      * Returns the extended bottom padding of the view, including both the
2537      * bottom Drawable if any and any extra space to keep more than maxLines
2538      * of text from showing.  It is only valid to call this after measuring.
2539      */
2540     public int getExtendedPaddingBottom() {
2541         if (mMaxMode != LINES) {
2542             return getCompoundPaddingBottom();
2543         }
2544
2545         if (mLayout == null) {
2546             assumeLayout();
2547         }
2548
2549         if (mLayout.getLineCount() <= mMaximum) {
2550             return getCompoundPaddingBottom();
2551         }
2552
2553         int top = getCompoundPaddingTop();
2554         int bottom = getCompoundPaddingBottom();
2555         int viewht = getHeight() - top - bottom;
2556         int layoutht = mLayout.getLineTop(mMaximum);
2557
2558         if (layoutht >= viewht) {
2559             return bottom;
2560         }
2561
2562         final int gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
2563         if (gravity == Gravity.TOP) {
2564             return bottom + viewht - layoutht;
2565         } else if (gravity == Gravity.BOTTOM) {
2566             return bottom;
2567         } else { // (gravity == Gravity.CENTER_VERTICAL)
2568             return bottom + (viewht - layoutht) / 2;
2569         }
2570     }
2571
2572     /**
2573      * Returns the total left padding of the view, including the left
2574      * Drawable if any.
2575      */
2576     public int getTotalPaddingLeft() {
2577         return getCompoundPaddingLeft();
2578     }
2579
2580     /**
2581      * Returns the total right padding of the view, including the right
2582      * Drawable if any.
2583      */
2584     public int getTotalPaddingRight() {
2585         return getCompoundPaddingRight();
2586     }
2587
2588     /**
2589      * Returns the total start padding of the view, including the start
2590      * Drawable if any.
2591      */
2592     public int getTotalPaddingStart() {
2593         return getCompoundPaddingStart();
2594     }
2595
2596     /**
2597      * Returns the total end padding of the view, including the end
2598      * Drawable if any.
2599      */
2600     public int getTotalPaddingEnd() {
2601         return getCompoundPaddingEnd();
2602     }
2603
2604     /**
2605      * Returns the total top padding of the view, including the top
2606      * Drawable if any, the extra space to keep more than maxLines
2607      * from showing, and the vertical offset for gravity, if any.
2608      */
2609     public int getTotalPaddingTop() {
2610         return getExtendedPaddingTop() + getVerticalOffset(true);
2611     }
2612
2613     /**
2614      * Returns the total bottom padding of the view, including the bottom
2615      * Drawable if any, the extra space to keep more than maxLines
2616      * from showing, and the vertical offset for gravity, if any.
2617      */
2618     public int getTotalPaddingBottom() {
2619         return getExtendedPaddingBottom() + getBottomVerticalOffset(true);
2620     }
2621
2622     /**
2623      * Sets the Drawables (if any) to appear to the left of, above, to the
2624      * right of, and below the text. Use {@code null} if you do not want a
2625      * Drawable there. The Drawables must already have had
2626      * {@link Drawable#setBounds} called.
2627      * <p>
2628      * Calling this method will overwrite any Drawables previously set using
2629      * {@link #setCompoundDrawablesRelative} or related methods.
2630      *
2631      * @attr ref android.R.styleable#TextView_drawableLeft
2632      * @attr ref android.R.styleable#TextView_drawableTop
2633      * @attr ref android.R.styleable#TextView_drawableRight
2634      * @attr ref android.R.styleable#TextView_drawableBottom
2635      */
2636     public void setCompoundDrawables(@Nullable Drawable left, @Nullable Drawable top,
2637             @Nullable Drawable right, @Nullable Drawable bottom) {
2638         Drawables dr = mDrawables;
2639
2640         // We're switching to absolute, discard relative.
2641         if (dr != null) {
2642             if (dr.mDrawableStart != null) dr.mDrawableStart.setCallback(null);
2643             dr.mDrawableStart = null;
2644             if (dr.mDrawableEnd != null) dr.mDrawableEnd.setCallback(null);
2645             dr.mDrawableEnd = null;
2646             dr.mDrawableSizeStart = dr.mDrawableHeightStart = 0;
2647             dr.mDrawableSizeEnd = dr.mDrawableHeightEnd = 0;
2648         }
2649
2650         final boolean drawables = left != null || top != null || right != null || bottom != null;
2651         if (!drawables) {
2652             // Clearing drawables...  can we free the data structure?
2653             if (dr != null) {
2654                 if (!dr.hasMetadata()) {
2655                     mDrawables = null;
2656                 } else {
2657                     // We need to retain the last set padding, so just clear
2658                     // out all of the fields in the existing structure.
2659                     for (int i = dr.mShowing.length - 1; i >= 0; i--) {
2660                         if (dr.mShowing[i] != null) {
2661                             dr.mShowing[i].setCallback(null);
2662                         }
2663                         dr.mShowing[i] = null;
2664                     }
2665                     dr.mDrawableSizeLeft = dr.mDrawableHeightLeft = 0;
2666                     dr.mDrawableSizeRight = dr.mDrawableHeightRight = 0;
2667                     dr.mDrawableSizeTop = dr.mDrawableWidthTop = 0;
2668                     dr.mDrawableSizeBottom = dr.mDrawableWidthBottom = 0;
2669                 }
2670             }
2671         } else {
2672             if (dr == null) {
2673                 mDrawables = dr = new Drawables(getContext());
2674             }
2675
2676             mDrawables.mOverride = false;
2677
2678             if (dr.mShowing[Drawables.LEFT] != left && dr.mShowing[Drawables.LEFT] != null) {
2679                 dr.mShowing[Drawables.LEFT].setCallback(null);
2680             }
2681             dr.mShowing[Drawables.LEFT] = left;
2682
2683             if (dr.mShowing[Drawables.TOP] != top && dr.mShowing[Drawables.TOP] != null) {
2684                 dr.mShowing[Drawables.TOP].setCallback(null);
2685             }
2686             dr.mShowing[Drawables.TOP] = top;
2687
2688             if (dr.mShowing[Drawables.RIGHT] != right && dr.mShowing[Drawables.RIGHT] != null) {
2689                 dr.mShowing[Drawables.RIGHT].setCallback(null);
2690             }
2691             dr.mShowing[Drawables.RIGHT] = right;
2692
2693             if (dr.mShowing[Drawables.BOTTOM] != bottom && dr.mShowing[Drawables.BOTTOM] != null) {
2694                 dr.mShowing[Drawables.BOTTOM].setCallback(null);
2695             }
2696             dr.mShowing[Drawables.BOTTOM] = bottom;
2697
2698             final Rect compoundRect = dr.mCompoundRect;
2699             int[] state;
2700
2701             state = getDrawableState();
2702
2703             if (left != null) {
2704                 left.setState(state);
2705                 left.copyBounds(compoundRect);
2706                 left.setCallback(this);
2707                 dr.mDrawableSizeLeft = compoundRect.width();
2708                 dr.mDrawableHeightLeft = compoundRect.height();
2709             } else {
2710                 dr.mDrawableSizeLeft = dr.mDrawableHeightLeft = 0;
2711             }
2712
2713             if (right != null) {
2714                 right.setState(state);
2715                 right.copyBounds(compoundRect);
2716                 right.setCallback(this);
2717                 dr.mDrawableSizeRight = compoundRect.width();
2718                 dr.mDrawableHeightRight = compoundRect.height();
2719             } else {
2720                 dr.mDrawableSizeRight = dr.mDrawableHeightRight = 0;
2721             }
2722
2723             if (top != null) {
2724                 top.setState(state);
2725                 top.copyBounds(compoundRect);
2726                 top.setCallback(this);
2727                 dr.mDrawableSizeTop = compoundRect.height();
2728                 dr.mDrawableWidthTop = compoundRect.width();
2729             } else {
2730                 dr.mDrawableSizeTop = dr.mDrawableWidthTop = 0;
2731             }
2732
2733             if (bottom != null) {
2734                 bottom.setState(state);
2735                 bottom.copyBounds(compoundRect);
2736                 bottom.setCallback(this);
2737                 dr.mDrawableSizeBottom = compoundRect.height();
2738                 dr.mDrawableWidthBottom = compoundRect.width();
2739             } else {
2740                 dr.mDrawableSizeBottom = dr.mDrawableWidthBottom = 0;
2741             }
2742         }
2743
2744         // Save initial left/right drawables
2745         if (dr != null) {
2746             dr.mDrawableLeftInitial = left;
2747             dr.mDrawableRightInitial = right;
2748         }
2749
2750         resetResolvedDrawables();
2751         resolveDrawables();
2752         applyCompoundDrawableTint();
2753         invalidate();
2754         requestLayout();
2755     }
2756
2757     /**
2758      * Sets the Drawables (if any) to appear to the left of, above, to the
2759      * right of, and below the text. Use 0 if you do not want a Drawable there.
2760      * The Drawables' bounds will be set to their intrinsic bounds.
2761      * <p>
2762      * Calling this method will overwrite any Drawables previously set using
2763      * {@link #setCompoundDrawablesRelative} or related methods.
2764      *
2765      * @param left Resource identifier of the left Drawable.
2766      * @param top Resource identifier of the top Drawable.
2767      * @param right Resource identifier of the right Drawable.
2768      * @param bottom Resource identifier of the bottom Drawable.
2769      *
2770      * @attr ref android.R.styleable#TextView_drawableLeft
2771      * @attr ref android.R.styleable#TextView_drawableTop
2772      * @attr ref android.R.styleable#TextView_drawableRight
2773      * @attr ref android.R.styleable#TextView_drawableBottom
2774      */
2775     @android.view.RemotableViewMethod
2776     public void setCompoundDrawablesWithIntrinsicBounds(@DrawableRes int left,
2777             @DrawableRes int top, @DrawableRes int right, @DrawableRes int bottom) {
2778         final Context context = getContext();
2779         setCompoundDrawablesWithIntrinsicBounds(left != 0 ? context.getDrawable(left) : null,
2780                 top != 0 ? context.getDrawable(top) : null,
2781                 right != 0 ? context.getDrawable(right) : null,
2782                 bottom != 0 ? context.getDrawable(bottom) : null);
2783     }
2784
2785     /**
2786      * Sets the Drawables (if any) to appear to the left of, above, to the
2787      * right of, and below the text. Use {@code null} if you do not want a
2788      * Drawable there. The Drawables' bounds will be set to their intrinsic
2789      * bounds.
2790      * <p>
2791      * Calling this method will overwrite any Drawables previously set using
2792      * {@link #setCompoundDrawablesRelative} or related methods.
2793      *
2794      * @attr ref android.R.styleable#TextView_drawableLeft
2795      * @attr ref android.R.styleable#TextView_drawableTop
2796      * @attr ref android.R.styleable#TextView_drawableRight
2797      * @attr ref android.R.styleable#TextView_drawableBottom
2798      */
2799     @android.view.RemotableViewMethod
2800     public void setCompoundDrawablesWithIntrinsicBounds(@Nullable Drawable left,
2801             @Nullable Drawable top, @Nullable Drawable right, @Nullable Drawable bottom) {
2802
2803         if (left != null) {
2804             left.setBounds(0, 0, left.getIntrinsicWidth(), left.getIntrinsicHeight());
2805         }
2806         if (right != null) {
2807             right.setBounds(0, 0, right.getIntrinsicWidth(), right.getIntrinsicHeight());
2808         }
2809         if (top != null) {
2810             top.setBounds(0, 0, top.getIntrinsicWidth(), top.getIntrinsicHeight());
2811         }
2812         if (bottom != null) {
2813             bottom.setBounds(0, 0, bottom.getIntrinsicWidth(), bottom.getIntrinsicHeight());
2814         }
2815         setCompoundDrawables(left, top, right, bottom);
2816     }
2817
2818     /**
2819      * Sets the Drawables (if any) to appear to the start of, above, to the end
2820      * of, and below the text. Use {@code null} if you do not want a Drawable
2821      * there. The Drawables must already have had {@link Drawable#setBounds}
2822      * called.
2823      * <p>
2824      * Calling this method will overwrite any Drawables previously set using
2825      * {@link #setCompoundDrawables} or related methods.
2826      *
2827      * @attr ref android.R.styleable#TextView_drawableStart
2828      * @attr ref android.R.styleable#TextView_drawableTop
2829      * @attr ref android.R.styleable#TextView_drawableEnd
2830      * @attr ref android.R.styleable#TextView_drawableBottom
2831      */
2832     @android.view.RemotableViewMethod
2833     public void setCompoundDrawablesRelative(@Nullable Drawable start, @Nullable Drawable top,
2834             @Nullable Drawable end, @Nullable Drawable bottom) {
2835         Drawables dr = mDrawables;
2836
2837         // We're switching to relative, discard absolute.
2838         if (dr != null) {
2839             if (dr.mShowing[Drawables.LEFT] != null) {
2840                 dr.mShowing[Drawables.LEFT].setCallback(null);
2841             }
2842             dr.mShowing[Drawables.LEFT] = dr.mDrawableLeftInitial = null;
2843             if (dr.mShowing[Drawables.RIGHT] != null) {
2844                 dr.mShowing[Drawables.RIGHT].setCallback(null);
2845             }
2846             dr.mShowing[Drawables.RIGHT] = dr.mDrawableRightInitial = null;
2847             dr.mDrawableSizeLeft = dr.mDrawableHeightLeft = 0;
2848             dr.mDrawableSizeRight = dr.mDrawableHeightRight = 0;
2849         }
2850
2851         final boolean drawables = start != null || top != null
2852                 || end != null || bottom != null;
2853
2854         if (!drawables) {
2855             // Clearing drawables...  can we free the data structure?
2856             if (dr != null) {
2857                 if (!dr.hasMetadata()) {
2858                     mDrawables = null;
2859                 } else {
2860                     // We need to retain the last set padding, so just clear
2861                     // out all of the fields in the existing structure.
2862                     if (dr.mDrawableStart != null) dr.mDrawableStart.setCallback(null);
2863                     dr.mDrawableStart = null;
2864                     if (dr.mShowing[Drawables.TOP] != null) {
2865                         dr.mShowing[Drawables.TOP].setCallback(null);
2866                     }
2867                     dr.mShowing[Drawables.TOP] = null;
2868                     if (dr.mDrawableEnd != null) {
2869                         dr.mDrawableEnd.setCallback(null);
2870                     }
2871                     dr.mDrawableEnd = null;
2872                     if (dr.mShowing[Drawables.BOTTOM] != null) {
2873                         dr.mShowing[Drawables.BOTTOM].setCallback(null);
2874                     }
2875                     dr.mShowing[Drawables.BOTTOM] = null;
2876                     dr.mDrawableSizeStart = dr.mDrawableHeightStart = 0;
2877                     dr.mDrawableSizeEnd = dr.mDrawableHeightEnd = 0;
2878                     dr.mDrawableSizeTop = dr.mDrawableWidthTop = 0;
2879                     dr.mDrawableSizeBottom = dr.mDrawableWidthBottom = 0;
2880                 }
2881             }
2882         } else {
2883             if (dr == null) {
2884                 mDrawables = dr = new Drawables(getContext());
2885             }
2886
2887             mDrawables.mOverride = true;
2888
2889             if (dr.mDrawableStart != start && dr.mDrawableStart != null) {
2890                 dr.mDrawableStart.setCallback(null);
2891             }
2892             dr.mDrawableStart = start;
2893
2894             if (dr.mShowing[Drawables.TOP] != top && dr.mShowing[Drawables.TOP] != null) {
2895                 dr.mShowing[Drawables.TOP].setCallback(null);
2896             }
2897             dr.mShowing[Drawables.TOP] = top;
2898
2899             if (dr.mDrawableEnd != end && dr.mDrawableEnd != null) {
2900                 dr.mDrawableEnd.setCallback(null);
2901             }
2902             dr.mDrawableEnd = end;
2903
2904             if (dr.mShowing[Drawables.BOTTOM] != bottom && dr.mShowing[Drawables.BOTTOM] != null) {
2905                 dr.mShowing[Drawables.BOTTOM].setCallback(null);
2906             }
2907             dr.mShowing[Drawables.BOTTOM] = bottom;
2908
2909             final Rect compoundRect = dr.mCompoundRect;
2910             int[] state;
2911
2912             state = getDrawableState();
2913
2914             if (start != null) {
2915                 start.setState(state);
2916                 start.copyBounds(compoundRect);
2917                 start.setCallback(this);
2918                 dr.mDrawableSizeStart = compoundRect.width();
2919                 dr.mDrawableHeightStart = compoundRect.height();
2920             } else {
2921                 dr.mDrawableSizeStart = dr.mDrawableHeightStart = 0;
2922             }
2923
2924             if (end != null) {
2925                 end.setState(state);
2926                 end.copyBounds(compoundRect);
2927                 end.setCallback(this);
2928                 dr.mDrawableSizeEnd = compoundRect.width();
2929                 dr.mDrawableHeightEnd = compoundRect.height();
2930             } else {
2931                 dr.mDrawableSizeEnd = dr.mDrawableHeightEnd = 0;
2932             }
2933
2934             if (top != null) {
2935                 top.setState(state);
2936                 top.copyBounds(compoundRect);
2937                 top.setCallback(this);
2938                 dr.mDrawableSizeTop = compoundRect.height();
2939                 dr.mDrawableWidthTop = compoundRect.width();
2940             } else {
2941                 dr.mDrawableSizeTop = dr.mDrawableWidthTop = 0;
2942             }
2943
2944             if (bottom != null) {
2945                 bottom.setState(state);
2946                 bottom.copyBounds(compoundRect);
2947                 bottom.setCallback(this);
2948                 dr.mDrawableSizeBottom = compoundRect.height();
2949                 dr.mDrawableWidthBottom = compoundRect.width();
2950             } else {
2951                 dr.mDrawableSizeBottom = dr.mDrawableWidthBottom = 0;
2952             }
2953         }
2954
2955         resetResolvedDrawables();
2956         resolveDrawables();
2957         invalidate();
2958         requestLayout();
2959     }
2960
2961     /**
2962      * Sets the Drawables (if any) to appear to the start of, above, to the end
2963      * of, and below the text. Use 0 if you do not want a Drawable there. The
2964      * Drawables' bounds will be set to their intrinsic bounds.
2965      * <p>
2966      * Calling this method will overwrite any Drawables previously set using
2967      * {@link #setCompoundDrawables} or related methods.
2968      *
2969      * @param start Resource identifier of the start Drawable.
2970      * @param top Resource identifier of the top Drawable.
2971      * @param end Resource identifier of the end Drawable.
2972      * @param bottom Resource identifier of the bottom Drawable.
2973      *
2974      * @attr ref android.R.styleable#TextView_drawableStart
2975      * @attr ref android.R.styleable#TextView_drawableTop
2976      * @attr ref android.R.styleable#TextView_drawableEnd
2977      * @attr ref android.R.styleable#TextView_drawableBottom
2978      */
2979     @android.view.RemotableViewMethod
2980     public void setCompoundDrawablesRelativeWithIntrinsicBounds(@DrawableRes int start,
2981             @DrawableRes int top, @DrawableRes int end, @DrawableRes int bottom) {
2982         final Context context = getContext();
2983         setCompoundDrawablesRelativeWithIntrinsicBounds(
2984                 start != 0 ? context.getDrawable(start) : null,
2985                 top != 0 ? context.getDrawable(top) : null,
2986                 end != 0 ? context.getDrawable(end) : null,
2987                 bottom != 0 ? context.getDrawable(bottom) : null);
2988     }
2989
2990     /**
2991      * Sets the Drawables (if any) to appear to the start of, above, to the end
2992      * of, and below the text. Use {@code null} if you do not want a Drawable
2993      * there. The Drawables' bounds will be set to their intrinsic bounds.
2994      * <p>
2995      * Calling this method will overwrite any Drawables previously set using
2996      * {@link #setCompoundDrawables} or related methods.
2997      *
2998      * @attr ref android.R.styleable#TextView_drawableStart
2999      * @attr ref android.R.styleable#TextView_drawableTop
3000      * @attr ref android.R.styleable#TextView_drawableEnd
3001      * @attr ref android.R.styleable#TextView_drawableBottom
3002      */
3003     @android.view.RemotableViewMethod
3004     public void setCompoundDrawablesRelativeWithIntrinsicBounds(@Nullable Drawable start,
3005             @Nullable Drawable top, @Nullable Drawable end, @Nullable Drawable bottom) {
3006
3007         if (start != null) {
3008             start.setBounds(0, 0, start.getIntrinsicWidth(), start.getIntrinsicHeight());
3009         }
3010         if (end != null) {
3011             end.setBounds(0, 0, end.getIntrinsicWidth(), end.getIntrinsicHeight());
3012         }
3013         if (top != null) {
3014             top.setBounds(0, 0, top.getIntrinsicWidth(), top.getIntrinsicHeight());
3015         }
3016         if (bottom != null) {
3017             bottom.setBounds(0, 0, bottom.getIntrinsicWidth(), bottom.getIntrinsicHeight());
3018         }
3019         setCompoundDrawablesRelative(start, top, end, bottom);
3020     }
3021
3022     /**
3023      * Returns drawables for the left, top, right, and bottom borders.
3024      *
3025      * @attr ref android.R.styleable#TextView_drawableLeft
3026      * @attr ref android.R.styleable#TextView_drawableTop
3027      * @attr ref android.R.styleable#TextView_drawableRight
3028      * @attr ref android.R.styleable#TextView_drawableBottom
3029      */
3030     @NonNull
3031     public Drawable[] getCompoundDrawables() {
3032         final Drawables dr = mDrawables;
3033         if (dr != null) {
3034             return dr.mShowing.clone();
3035         } else {
3036             return new Drawable[] { null, null, null, null };
3037         }
3038     }
3039
3040     /**
3041      * Returns drawables for the start, top, end, and bottom borders.
3042      *
3043      * @attr ref android.R.styleable#TextView_drawableStart
3044      * @attr ref android.R.styleable#TextView_drawableTop
3045      * @attr ref android.R.styleable#TextView_drawableEnd
3046      * @attr ref android.R.styleable#TextView_drawableBottom
3047      */
3048     @NonNull
3049     public Drawable[] getCompoundDrawablesRelative() {
3050         final Drawables dr = mDrawables;
3051         if (dr != null) {
3052             return new Drawable[] {
3053                 dr.mDrawableStart, dr.mShowing[Drawables.TOP],
3054                 dr.mDrawableEnd, dr.mShowing[Drawables.BOTTOM]
3055             };
3056         } else {
3057             return new Drawable[] { null, null, null, null };
3058         }
3059     }
3060
3061     /**
3062      * Sets the size of the padding between the compound drawables and
3063      * the text.
3064      *
3065      * @attr ref android.R.styleable#TextView_drawablePadding
3066      */
3067     @android.view.RemotableViewMethod
3068     public void setCompoundDrawablePadding(int pad) {
3069         Drawables dr = mDrawables;
3070         if (pad == 0) {
3071             if (dr != null) {
3072                 dr.mDrawablePadding = pad;
3073             }
3074         } else {
3075             if (dr == null) {
3076                 mDrawables = dr = new Drawables(getContext());
3077             }
3078             dr.mDrawablePadding = pad;
3079         }
3080
3081         invalidate();
3082         requestLayout();
3083     }
3084
3085     /**
3086      * Returns the padding between the compound drawables and the text.
3087      *
3088      * @attr ref android.R.styleable#TextView_drawablePadding
3089      */
3090     public int getCompoundDrawablePadding() {
3091         final Drawables dr = mDrawables;
3092         return dr != null ? dr.mDrawablePadding : 0;
3093     }
3094
3095     /**
3096      * Applies a tint to the compound drawables. Does not modify the
3097      * current tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
3098      * <p>
3099      * Subsequent calls to
3100      * {@link #setCompoundDrawables(Drawable, Drawable, Drawable, Drawable)}
3101      * and related methods will automatically mutate the drawables and apply
3102      * the specified tint and tint mode using
3103      * {@link Drawable#setTintList(ColorStateList)}.
3104      *
3105      * @param tint the tint to apply, may be {@code null} to clear tint
3106      *
3107      * @attr ref android.R.styleable#TextView_drawableTint
3108      * @see #getCompoundDrawableTintList()
3109      * @see Drawable#setTintList(ColorStateList)
3110      */
3111     public void setCompoundDrawableTintList(@Nullable ColorStateList tint) {
3112         if (mDrawables == null) {
3113             mDrawables = new Drawables(getContext());
3114         }
3115         mDrawables.mTintList = tint;
3116         mDrawables.mHasTint = true;
3117
3118         applyCompoundDrawableTint();
3119     }
3120
3121     /**
3122      * @return the tint applied to the compound drawables
3123      * @attr ref android.R.styleable#TextView_drawableTint
3124      * @see #setCompoundDrawableTintList(ColorStateList)
3125      */
3126     public ColorStateList getCompoundDrawableTintList() {
3127         return mDrawables != null ? mDrawables.mTintList : null;
3128     }
3129
3130     /**
3131      * Specifies the blending mode used to apply the tint specified by
3132      * {@link #setCompoundDrawableTintList(ColorStateList)} to the compound
3133      * drawables. The default mode is {@link PorterDuff.Mode#SRC_IN}.
3134      *
3135      * @param tintMode the blending mode used to apply the tint, may be
3136      *                 {@code null} to clear tint
3137      * @attr ref android.R.styleable#TextView_drawableTintMode
3138      * @see #setCompoundDrawableTintList(ColorStateList)
3139      * @see Drawable#setTintMode(PorterDuff.Mode)
3140      */
3141     public void setCompoundDrawableTintMode(@Nullable PorterDuff.Mode tintMode) {
3142         if (mDrawables == null) {
3143             mDrawables = new Drawables(getContext());
3144         }
3145         mDrawables.mTintMode = tintMode;
3146         mDrawables.mHasTintMode = true;
3147
3148         applyCompoundDrawableTint();
3149     }
3150
3151     /**
3152      * Returns the blending mode used to apply the tint to the compound
3153      * drawables, if specified.
3154      *
3155      * @return the blending mode used to apply the tint to the compound
3156      *         drawables
3157      * @attr ref android.R.styleable#TextView_drawableTintMode
3158      * @see #setCompoundDrawableTintMode(PorterDuff.Mode)
3159      */
3160     public PorterDuff.Mode getCompoundDrawableTintMode() {
3161         return mDrawables != null ? mDrawables.mTintMode : null;
3162     }
3163
3164     private void applyCompoundDrawableTint() {
3165         if (mDrawables == null) {
3166             return;
3167         }
3168
3169         if (mDrawables.mHasTint || mDrawables.mHasTintMode) {
3170             final ColorStateList tintList = mDrawables.mTintList;
3171             final PorterDuff.Mode tintMode = mDrawables.mTintMode;
3172             final boolean hasTint = mDrawables.mHasTint;
3173             final boolean hasTintMode = mDrawables.mHasTintMode;
3174             final int[] state = getDrawableState();
3175
3176             for (Drawable dr : mDrawables.mShowing) {
3177                 if (dr == null) {
3178                     continue;
3179                 }
3180
3181                 if (dr == mDrawables.mDrawableError) {
3182                     // From a developer's perspective, the error drawable isn't
3183                     // a compound drawable. Don't apply the generic compound
3184                     // drawable tint to it.
3185                     continue;
3186                 }
3187
3188                 dr.mutate();
3189
3190                 if (hasTint) {
3191                     dr.setTintList(tintList);
3192                 }
3193
3194                 if (hasTintMode) {
3195                     dr.setTintMode(tintMode);
3196                 }
3197
3198                 // The drawable (or one of its children) may not have been
3199                 // stateful before applying the tint, so let's try again.
3200                 if (dr.isStateful()) {
3201                     dr.setState(state);
3202                 }
3203             }
3204         }
3205     }
3206
3207     /**
3208      * @inheritDoc
3209      *
3210      * @see #setFirstBaselineToTopHeight(int)
3211      * @see #setLastBaselineToBottomHeight(int)
3212      */
3213     @Override
3214     public void setPadding(int left, int top, int right, int bottom) {
3215         if (left != mPaddingLeft
3216                 || right != mPaddingRight
3217                 || top != mPaddingTop
3218                 ||  bottom != mPaddingBottom) {
3219             nullLayouts();
3220         }
3221
3222         // the super call will requestLayout()
3223         super.setPadding(left, top, right, bottom);
3224         invalidate();
3225     }
3226
3227     /**
3228      * @inheritDoc
3229      *
3230      * @see #setFirstBaselineToTopHeight(int)
3231      * @see #setLastBaselineToBottomHeight(int)
3232      */
3233     @Override
3234     public void setPaddingRelative(int start, int top, int end, int bottom) {
3235         if (start != getPaddingStart()
3236                 || end != getPaddingEnd()
3237                 || top != mPaddingTop
3238                 || bottom != mPaddingBottom) {
3239             nullLayouts();
3240         }
3241
3242         // the super call will requestLayout()
3243         super.setPaddingRelative(start, top, end, bottom);
3244         invalidate();
3245     }
3246
3247     /**
3248      * Updates the top padding of the TextView so that {@code firstBaselineToTopHeight} is
3249      * equal to the distance between the firt text baseline and the top of this TextView.
3250      * <strong>Note</strong> that if {@code FontMetrics.top} or {@code FontMetrics.ascent} was
3251      * already greater than {@code firstBaselineToTopHeight}, the top padding is not updated.
3252      *
3253      * @param firstBaselineToTopHeight distance between first baseline to top of the container
3254      *      in pixels
3255      *
3256      * @see #getFirstBaselineToTopHeight()
3257      * @see #setPadding(int, int, int, int)
3258      * @see #setPaddingRelative(int, int, int, int)
3259      *
3260      * @attr ref android.R.styleable#TextView_firstBaselineToTopHeight
3261      */
3262     public void setFirstBaselineToTopHeight(@Px @IntRange(from = 0) int firstBaselineToTopHeight) {
3263         Preconditions.checkArgumentNonnegative(firstBaselineToTopHeight);
3264
3265         final FontMetricsInt fontMetrics = getPaint().getFontMetricsInt();
3266         final int fontMetricsTop;
3267         if (getIncludeFontPadding()) {
3268             fontMetricsTop = fontMetrics.top;
3269         } else {
3270             fontMetricsTop = fontMetrics.ascent;
3271         }
3272
3273         // TODO: Decide if we want to ignore density ratio (i.e. when the user changes font size
3274         // in settings). At the moment, we don't.
3275
3276         if (firstBaselineToTopHeight > Math.abs(fontMetricsTop)) {
3277             final int paddingTop = firstBaselineToTopHeight - (-fontMetricsTop);
3278             setPadding(getPaddingLeft(), paddingTop, getPaddingRight(), getPaddingBottom());
3279         }
3280     }
3281
3282     /**
3283      * Updates the bottom padding of the TextView so that {@code lastBaselineToBottomHeight} is
3284      * equal to the distance between the last text baseline and the bottom of this TextView.
3285      * <strong>Note</strong> that if {@code FontMetrics.bottom} or {@code FontMetrics.descent} was
3286      * already greater than {@code lastBaselineToBottomHeight}, the bottom padding is not updated.
3287      *
3288      * @param lastBaselineToBottomHeight distance between last baseline to bottom of the container
3289      *      in pixels
3290      *
3291      * @see #getLastBaselineToBottomHeight()
3292      * @see #setPadding(int, int, int, int)
3293      * @see #setPaddingRelative(int, int, int, int)
3294      *
3295      * @attr ref android.R.styleable#TextView_lastBaselineToBottomHeight
3296      */
3297     public void setLastBaselineToBottomHeight(
3298             @Px @IntRange(from = 0) int lastBaselineToBottomHeight) {
3299         Preconditions.checkArgumentNonnegative(lastBaselineToBottomHeight);
3300
3301         final FontMetricsInt fontMetrics = getPaint().getFontMetricsInt();
3302         final int fontMetricsBottom;
3303         if (getIncludeFontPadding()) {
3304             fontMetricsBottom = fontMetrics.bottom;
3305         } else {
3306             fontMetricsBottom = fontMetrics.descent;
3307         }
3308
3309         // TODO: Decide if we want to ignore density ratio (i.e. when the user changes font size
3310         // in settings). At the moment, we don't.
3311
3312         if (lastBaselineToBottomHeight > Math.abs(fontMetricsBottom)) {
3313             final int paddingBottom = lastBaselineToBottomHeight - fontMetricsBottom;
3314             setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), paddingBottom);
3315         }
3316     }
3317
3318     /**
3319      * Returns the distance between the first text baseline and the top of this TextView.
3320      *
3321      * @see #setFirstBaselineToTopHeight(int)
3322      * @attr ref android.R.styleable#TextView_firstBaselineToTopHeight
3323      */
3324     public int getFirstBaselineToTopHeight() {
3325         return getPaddingTop() - getPaint().getFontMetricsInt().top;
3326     }
3327
3328     /**
3329      * Returns the distance between the last text baseline and the bottom of this TextView.
3330      *
3331      * @see #setLastBaselineToBottomHeight(int)
3332      * @attr ref android.R.styleable#TextView_lastBaselineToBottomHeight
3333      */
3334     public int getLastBaselineToBottomHeight() {
3335         return getPaddingBottom() + getPaint().getFontMetricsInt().bottom;
3336     }
3337
3338     /**
3339      * Gets the autolink mask of the text.  See {@link
3340      * android.text.util.Linkify#ALL Linkify.ALL} and peers for
3341      * possible values.
3342      *
3343      * @attr ref android.R.styleable#TextView_autoLink
3344      */
3345     public final int getAutoLinkMask() {
3346         return mAutoLinkMask;
3347     }
3348
3349     /**
3350      * Sets the text appearance from the specified style resource.
3351      * <p>
3352      * Use a framework-defined {@code TextAppearance} style like
3353      * {@link android.R.style#TextAppearance_Material_Body1 @android:style/TextAppearance.Material.Body1}
3354      * or see {@link android.R.styleable#TextAppearance TextAppearance} for the
3355      * set of attributes that can be used in a custom style.
3356      *
3357      * @param resId the resource identifier of the style to apply
3358      * @attr ref android.R.styleable#TextView_textAppearance
3359      */
3360     @SuppressWarnings("deprecation")
3361     public void setTextAppearance(@StyleRes int resId) {
3362         setTextAppearance(mContext, resId);
3363     }
3364
3365     /**
3366      * Sets the text color, size, style, hint color, and highlight color
3367      * from the specified TextAppearance resource.
3368      *
3369      * @deprecated Use {@link #setTextAppearance(int)} instead.
3370      */
3371     @Deprecated
3372     public void setTextAppearance(Context context, @StyleRes int resId) {
3373         final TypedArray ta = context.obtainStyledAttributes(resId, R.styleable.TextAppearance);
3374         final TextAppearanceAttributes attributes = new TextAppearanceAttributes();
3375         readTextAppearance(context, ta, attributes, false /* styleArray */);
3376         ta.recycle();
3377         applyTextAppearance(attributes);
3378     }
3379
3380     /**
3381      * Set of attributes that can be defined in a Text Appearance. This is used to simplify the code
3382      * that reads these attributes in the constructor and in {@link #setTextAppearance}.
3383      */
3384     private static class TextAppearanceAttributes {
3385         int mTextColorHighlight = 0;
3386         ColorStateList mTextColor = null;
3387         ColorStateList mTextColorHint = null;
3388         ColorStateList mTextColorLink = null;
3389         int mTextSize = 0;
3390         String mFontFamily = null;
3391         Typeface mFontTypeface = null;
3392         boolean mFontFamilyExplicit = false;
3393         int mTypefaceIndex = -1;
3394         int mStyleIndex = -1;
3395         boolean mAllCaps = false;
3396         int mShadowColor = 0;
3397         float mShadowDx = 0, mShadowDy = 0, mShadowRadius = 0;
3398         boolean mHasElegant = false;
3399         boolean mElegant = false;
3400         boolean mHasFallbackLineSpacing = false;
3401         boolean mFallbackLineSpacing = false;
3402         boolean mHasLetterSpacing = false;
3403         float mLetterSpacing = 0;
3404         String mFontFeatureSettings = null;
3405
3406         @Override
3407         public String toString() {
3408             return "TextAppearanceAttributes {\n"
3409                     + "    mTextColorHighlight:" + mTextColorHighlight + "\n"
3410                     + "    mTextColor:" + mTextColor + "\n"
3411                     + "    mTextColorHint:" + mTextColorHint + "\n"
3412                     + "    mTextColorLink:" + mTextColorLink + "\n"
3413                     + "    mTextSize:" + mTextSize + "\n"
3414                     + "    mFontFamily:" + mFontFamily + "\n"
3415                     + "    mFontTypeface:" + mFontTypeface + "\n"
3416                     + "    mFontFamilyExplicit:" + mFontFamilyExplicit + "\n"
3417                     + "    mTypefaceIndex:" + mTypefaceIndex + "\n"
3418                     + "    mStyleIndex:" + mStyleIndex + "\n"
3419                     + "    mAllCaps:" + mAllCaps + "\n"
3420                     + "    mShadowColor:" + mShadowColor + "\n"
3421                     + "    mShadowDx:" + mShadowDx + "\n"
3422                     + "    mShadowDy:" + mShadowDy + "\n"
3423                     + "    mShadowRadius:" + mShadowRadius + "\n"
3424                     + "    mHasElegant:" + mHasElegant + "\n"
3425                     + "    mElegant:" + mElegant + "\n"
3426                     + "    mHasFallbackLineSpacing:" + mHasFallbackLineSpacing + "\n"
3427                     + "    mFallbackLineSpacing:" + mFallbackLineSpacing + "\n"
3428                     + "    mHasLetterSpacing:" + mHasLetterSpacing + "\n"
3429                     + "    mLetterSpacing:" + mLetterSpacing + "\n"
3430                     + "    mFontFeatureSettings:" + mFontFeatureSettings + "\n"
3431                     + "}";
3432         }
3433     }
3434
3435     // Maps styleable attributes that exist both in TextView style and TextAppearance.
3436     private static final SparseIntArray sAppearanceValues = new SparseIntArray();
3437     static {
3438         sAppearanceValues.put(com.android.internal.R.styleable.TextView_textColorHighlight,
3439                 com.android.internal.R.styleable.TextAppearance_textColorHighlight);
3440         sAppearanceValues.put(com.android.internal.R.styleable.TextView_textColor,
3441                 com.android.internal.R.styleable.TextAppearance_textColor);
3442         sAppearanceValues.put(com.android.internal.R.styleable.TextView_textColorHint,
3443                 com.android.internal.R.styleable.TextAppearance_textColorHint);
3444         sAppearanceValues.put(com.android.internal.R.styleable.TextView_textColorLink,
3445                 com.android.internal.R.styleable.TextAppearance_textColorLink);
3446         sAppearanceValues.put(com.android.internal.R.styleable.TextView_textSize,
3447                 com.android.internal.R.styleable.TextAppearance_textSize);
3448         sAppearanceValues.put(com.android.internal.R.styleable.TextView_typeface,
3449                 com.android.internal.R.styleable.TextAppearance_typeface);
3450         sAppearanceValues.put(com.android.internal.R.styleable.TextView_fontFamily,
3451                 com.android.internal.R.styleable.TextAppearance_fontFamily);
3452         sAppearanceValues.put(com.android.internal.R.styleable.TextView_textStyle,
3453                 com.android.internal.R.styleable.TextAppearance_textStyle);
3454         sAppearanceValues.put(com.android.internal.R.styleable.TextView_textAllCaps,
3455                 com.android.internal.R.styleable.TextAppearance_textAllCaps);
3456         sAppearanceValues.put(com.android.internal.R.styleable.TextView_shadowColor,
3457                 com.android.internal.R.styleable.TextAppearance_shadowColor);
3458         sAppearanceValues.put(com.android.internal.R.styleable.TextView_shadowDx,
3459                 com.android.internal.R.styleable.TextAppearance_shadowDx);
3460         sAppearanceValues.put(com.android.internal.R.styleable.TextView_shadowDy,
3461                 com.android.internal.R.styleable.TextAppearance_shadowDy);
3462         sAppearanceValues.put(com.android.internal.R.styleable.TextView_shadowRadius,
3463                 com.android.internal.R.styleable.TextAppearance_shadowRadius);
3464         sAppearanceValues.put(com.android.internal.R.styleable.TextView_elegantTextHeight,
3465                 com.android.internal.R.styleable.TextAppearance_elegantTextHeight);
3466         sAppearanceValues.put(com.android.internal.R.styleable.TextView_fallbackLineSpacing,
3467                 com.android.internal.R.styleable.TextAppearance_fallbackLineSpacing);
3468         sAppearanceValues.put(com.android.internal.R.styleable.TextView_letterSpacing,
3469                 com.android.internal.R.styleable.TextAppearance_letterSpacing);
3470         sAppearanceValues.put(com.android.internal.R.styleable.TextView_fontFeatureSettings,
3471                 com.android.internal.R.styleable.TextAppearance_fontFeatureSettings);
3472     }
3473
3474     /**
3475      * Read the Text Appearance attributes from a given TypedArray and set its values to the given
3476      * set. If the TypedArray contains a value that was already set in the given attributes, that
3477      * will be overriden.
3478      *
3479      * @param context The Context to be used
3480      * @param appearance The TypedArray to read properties from
3481      * @param attributes the TextAppearanceAttributes to fill in
3482      * @param styleArray Whether the given TypedArray is a style or a TextAppearance. This defines
3483      *                   what attribute indexes will be used to read the properties.
3484      */
3485     private void readTextAppearance(Context context, TypedArray appearance,
3486             TextAppearanceAttributes attributes, boolean styleArray) {
3487         final int n = appearance.getIndexCount();
3488         for (int i = 0; i < n; i++) {
3489             final int attr = appearance.getIndex(i);
3490             int index = attr;
3491             // Translate style array index ids to TextAppearance ids.
3492             if (styleArray) {
3493                 index = sAppearanceValues.get(attr, -1);
3494                 if (index == -1) {
3495                     // This value is not part of a Text Appearance and should be ignored.
3496                     continue;
3497                 }
3498             }
3499             switch (index) {
3500                 case com.android.internal.R.styleable.TextAppearance_textColorHighlight:
3501                     attributes.mTextColorHighlight =
3502                             appearance.getColor(attr, attributes.mTextColorHighlight);
3503                     break;
3504                 case com.android.internal.R.styleable.TextAppearance_textColor:
3505                     attributes.mTextColor = appearance.getColorStateList(attr);
3506                     break;
3507                 case com.android.internal.R.styleable.TextAppearance_textColorHint:
3508                     attributes.mTextColorHint = appearance.getColorStateList(attr);
3509                     break;
3510                 case com.android.internal.R.styleable.TextAppearance_textColorLink:
3511                     attributes.mTextColorLink = appearance.getColorStateList(attr);
3512                     break;
3513                 case com.android.internal.R.styleable.TextAppearance_textSize:
3514                     attributes.mTextSize =
3515                             appearance.getDimensionPixelSize(attr, attributes.mTextSize);
3516                     break;
3517                 case com.android.internal.R.styleable.TextAppearance_typeface:
3518                     attributes.mTypefaceIndex = appearance.getInt(attr, attributes.mTypefaceIndex);
3519                     if (attributes.mTypefaceIndex != -1 && !attributes.mFontFamilyExplicit) {
3520                         attributes.mFontFamily = null;
3521                     }
3522                     break;
3523                 case com.android.internal.R.styleable.TextAppearance_fontFamily:
3524                     if (!context.isRestricted() && context.canLoadUnsafeResources()) {
3525                         try {
3526                             attributes.mFontTypeface = appearance.getFont(attr);
3527                         } catch (UnsupportedOperationException | Resources.NotFoundException e) {
3528                             // Expected if it is not a font resource.
3529                         }
3530                     }
3531                     if (attributes.mFontTypeface == null) {
3532                         attributes.mFontFamily = appearance.getString(attr);
3533                     }
3534                     attributes.mFontFamilyExplicit = true;
3535                     break;
3536                 case com.android.internal.R.styleable.TextAppearance_textStyle:
3537                     attributes.mStyleIndex = appearance.getInt(attr, attributes.mStyleIndex);
3538                     break;
3539                 case com.android.internal.R.styleable.TextAppearance_textAllCaps:
3540                     attributes.mAllCaps = appearance.getBoolean(attr, attributes.mAllCaps);
3541                     break;
3542                 case com.android.internal.R.styleable.TextAppearance_shadowColor:
3543                     attributes.mShadowColor = appearance.getInt(attr, attributes.mShadowColor);
3544                     break;
3545                 case com.android.internal.R.styleable.TextAppearance_shadowDx:
3546                     attributes.mShadowDx = appearance.getFloat(attr, attributes.mShadowDx);
3547                     break;
3548                 case com.android.internal.R.styleable.TextAppearance_shadowDy:
3549                     attributes.mShadowDy = appearance.getFloat(attr, attributes.mShadowDy);
3550                     break;
3551                 case com.android.internal.R.styleable.TextAppearance_shadowRadius:
3552                     attributes.mShadowRadius = appearance.getFloat(attr, attributes.mShadowRadius);
3553                     break;
3554                 case com.android.internal.R.styleable.TextAppearance_elegantTextHeight:
3555                     attributes.mHasElegant = true;
3556                     attributes.mElegant = appearance.getBoolean(attr, attributes.mElegant);
3557                     break;
3558                 case com.android.internal.R.styleable.TextAppearance_fallbackLineSpacing:
3559                     attributes.mHasFallbackLineSpacing = true;
3560                     attributes.mFallbackLineSpacing = appearance.getBoolean(attr,
3561                             attributes.mFallbackLineSpacing);
3562                     break;
3563                 case com.android.internal.R.styleable.TextAppearance_letterSpacing:
3564                     attributes.mHasLetterSpacing = true;
3565                     attributes.mLetterSpacing =
3566                             appearance.getFloat(attr, attributes.mLetterSpacing);
3567                     break;
3568                 case com.android.internal.R.styleable.TextAppearance_fontFeatureSettings:
3569                     attributes.mFontFeatureSettings = appearance.getString(attr);
3570                     break;
3571                 default:
3572             }
3573         }
3574     }
3575
3576     private void applyTextAppearance(TextAppearanceAttributes attributes) {
3577         if (attributes.mTextColor != null) {
3578             setTextColor(attributes.mTextColor);
3579         }
3580
3581         if (attributes.mTextColorHint != null) {
3582             setHintTextColor(attributes.mTextColorHint);
3583         }
3584
3585         if (attributes.mTextColorLink != null) {
3586             setLinkTextColor(attributes.mTextColorLink);
3587         }
3588
3589         if (attributes.mTextColorHighlight != 0) {
3590             setHighlightColor(attributes.mTextColorHighlight);
3591         }
3592
3593         if (attributes.mTextSize != 0) {
3594             setRawTextSize(attributes.mTextSize, true /* shouldRequestLayout */);
3595         }
3596
3597         if (attributes.mTypefaceIndex != -1 && !attributes.mFontFamilyExplicit) {
3598             attributes.mFontFamily = null;
3599         }
3600         setTypefaceFromAttrs(attributes.mFontTypeface, attributes.mFontFamily,
3601                 attributes.mTypefaceIndex, attributes.mStyleIndex);
3602
3603         if (attributes.mShadowColor != 0) {
3604             setShadowLayer(attributes.mShadowRadius, attributes.mShadowDx, attributes.mShadowDy,
3605                     attributes.mShadowColor);
3606         }
3607
3608         if (attributes.mAllCaps) {
3609             setTransformationMethod(new AllCapsTransformationMethod(getContext()));
3610         }
3611
3612         if (attributes.mHasElegant) {
3613             setElegantTextHeight(attributes.mElegant);
3614         }
3615
3616         if (attributes.mHasFallbackLineSpacing) {
3617             setFallbackLineSpacing(attributes.mFallbackLineSpacing);
3618         }
3619
3620         if (attributes.mHasLetterSpacing) {
3621             setLetterSpacing(attributes.mLetterSpacing);
3622         }
3623
3624         if (attributes.mFontFeatureSettings != null) {
3625             setFontFeatureSettings(attributes.mFontFeatureSettings);
3626         }
3627     }
3628
3629     /**
3630      * Get the default primary {@link Locale} of the text in this TextView. This will always be
3631      * the first member of {@link #getTextLocales()}.
3632      * @return the default primary {@link Locale} of the text in this TextView.
3633      */
3634     @NonNull
3635     public Locale getTextLocale() {
3636         return mTextPaint.getTextLocale();
3637     }
3638
3639     /**
3640      * Get the default {@link LocaleList} of the text in this TextView.
3641      * @return the default {@link LocaleList} of the text in this TextView.
3642      */
3643     @NonNull @Size(min = 1)
3644     public LocaleList getTextLocales() {
3645         return mTextPaint.getTextLocales();
3646     }
3647
3648     private void changeListenerLocaleTo(@Nullable Locale locale) {
3649         if (mListenerChanged) {
3650             // If a listener has been explicitly set, don't change it. We may break something.
3651             return;
3652         }
3653         // The following null check is not absolutely necessary since all calling points of
3654         // changeListenerLocaleTo() guarantee a non-null mEditor at the moment. But this is left
3655         // here in case others would want to call this method in the future.
3656         if (mEditor != null) {
3657             KeyListener listener = mEditor.mKeyListener;
3658             if (listener instanceof DigitsKeyListener) {
3659                 listener = DigitsKeyListener.getInstance(locale, (DigitsKeyListener) listener);
3660             } else if (listener instanceof DateKeyListener) {
3661                 listener = DateKeyListener.getInstance(locale);
3662             } else if (listener instanceof TimeKeyListener) {
3663                 listener = TimeKeyListener.getInstance(locale);
3664             } else if (listener instanceof DateTimeKeyListener) {
3665                 listener = DateTimeKeyListener.getInstance(locale);
3666             } else {
3667                 return;
3668             }
3669             final boolean wasPasswordType = isPasswordInputType(mEditor.mInputType);
3670             setKeyListenerOnly(listener);
3671             setInputTypeFromEditor();
3672             if (wasPasswordType) {
3673                 final int newInputClass = mEditor.mInputType & EditorInfo.TYPE_MASK_CLASS;
3674                 if (newInputClass == EditorInfo.TYPE_CLASS_TEXT) {
3675                     mEditor.mInputType |= EditorInfo.TYPE_TEXT_VARIATION_PASSWORD;
3676                 } else if (newInputClass == EditorInfo.TYPE_CLASS_NUMBER) {
3677                     mEditor.mInputType |= EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD;
3678                 }
3679             }
3680         }
3681     }
3682
3683     /**
3684      * Set the default {@link Locale} of the text in this TextView to a one-member
3685      * {@link LocaleList} containing just the given Locale.
3686      *
3687      * @param locale the {@link Locale} for drawing text, must not be null.
3688      *
3689      * @see #setTextLocales
3690      */
3691     public void setTextLocale(@NonNull Locale locale) {
3692         mLocalesChanged = true;
3693         mTextPaint.setTextLocale(locale);
3694         if (mLayout != null) {
3695             nullLayouts();
3696             requestLayout();
3697             invalidate();
3698         }
3699     }
3700
3701     /**
3702      * Set the default {@link LocaleList} of the text in this TextView to the given value.
3703      *
3704      * This value is used to choose appropriate typefaces for ambiguous characters (typically used
3705      * for CJK locales to disambiguate Hanzi/Kanji/Hanja characters). It also affects
3706      * other aspects of text display, including line breaking.
3707      *
3708      * @param locales the {@link LocaleList} for drawing text, must not be null or empty.
3709      *
3710      * @see Paint#setTextLocales
3711      */
3712     public void setTextLocales(@NonNull @Size(min = 1) LocaleList locales) {
3713         mLocalesChanged = true;
3714         mTextPaint.setTextLocales(locales);
3715         if (mLayout != null) {
3716             nullLayouts();
3717             requestLayout();
3718             invalidate();
3719         }
3720     }
3721
3722     @Override
3723     protected void onConfigurationChanged(Configuration newConfig) {
3724         super.onConfigurationChanged(newConfig);
3725         if (!mLocalesChanged) {
3726             mTextPaint.setTextLocales(LocaleList.getDefault());
3727             if (mLayout != null) {
3728                 nullLayouts();
3729                 requestLayout();
3730                 invalidate();
3731             }
3732         }
3733     }
3734
3735     /**
3736      * @return the size (in pixels) of the default text size in this TextView.
3737      */
3738     @ViewDebug.ExportedProperty(category = "text")
3739     public float getTextSize() {
3740         return mTextPaint.getTextSize();
3741     }
3742
3743     /**
3744      * @return the size (in scaled pixels) of the default text size in this TextView.
3745      * @hide
3746      */
3747     @ViewDebug.ExportedProperty(category = "text")
3748     public float getScaledTextSize() {
3749         return mTextPaint.getTextSize() / mTextPaint.density;
3750     }
3751
3752     /** @hide */
3753     @ViewDebug.ExportedProperty(category = "text", mapping = {
3754             @ViewDebug.IntToString(from = Typeface.NORMAL, to = "NORMAL"),
3755             @ViewDebug.IntToString(from = Typeface.BOLD, to = "BOLD"),
3756             @ViewDebug.IntToString(from = Typeface.ITALIC, to = "ITALIC"),
3757             @ViewDebug.IntToString(from = Typeface.BOLD_ITALIC, to = "BOLD_ITALIC")
3758     })
3759     public int getTypefaceStyle() {
3760         Typeface typeface = mTextPaint.getTypeface();
3761         return typeface != null ? typeface.getStyle() : Typeface.NORMAL;
3762     }
3763
3764     /**
3765      * Set the default text size to the given value, interpreted as "scaled
3766      * pixel" units.  This size is adjusted based on the current density and
3767      * user font size preference.
3768      *
3769      * <p>Note: if this TextView has the auto-size feature enabled than this function is no-op.
3770      *
3771      * @param size The scaled pixel size.
3772      *
3773      * @attr ref android.R.styleable#TextView_textSize
3774      */
3775     @android.view.RemotableViewMethod
3776     public void setTextSize(float size) {
3777         setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
3778     }
3779
3780     /**
3781      * Set the default text size to a given unit and value. See {@link
3782      * TypedValue} for the possible dimension units.
3783      *
3784      * <p>Note: if this TextView has the auto-size feature enabled than this function is no-op.
3785      *
3786      * @param unit The desired dimension unit.
3787      * @param size The desired size in the given units.
3788      *
3789      * @attr ref android.R.styleable#TextView_textSize
3790      */
3791     public void setTextSize(int unit, float size) {
3792         if (!isAutoSizeEnabled()) {
3793             setTextSizeInternal(unit, size, true /* shouldRequestLayout */);
3794         }
3795     }
3796
3797     private void setTextSizeInternal(int unit, float size, boolean shouldRequestLayout) {
3798         Context c = getContext();
3799         Resources r;
3800
3801         if (c == null) {
3802             r = Resources.getSystem();
3803         } else {
3804             r = c.getResources();
3805         }
3806
3807         setRawTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()),
3808                 shouldRequestLayout);
3809     }
3810
3811     private void setRawTextSize(float size, boolean shouldRequestLayout) {
3812         if (size != mTextPaint.getTextSize()) {
3813             mTextPaint.setTextSize(size);
3814
3815             if (shouldRequestLayout && mLayout != null) {
3816                 // Do not auto-size right after setting the text size.
3817                 mNeedsAutoSizeText = false;
3818                 nullLayouts();
3819                 requestLayout();
3820                 invalidate();
3821             }
3822         }
3823     }
3824
3825     /**
3826      * Gets the extent by which text should be stretched horizontally.
3827      * This will usually be 1.0.
3828      * @return The horizontal scale factor.
3829      */
3830     public float getTextScaleX() {
3831         return mTextPaint.getTextScaleX();
3832     }
3833
3834     /**
3835      * Sets the horizontal scale factor for text. The default value
3836      * is 1.0. Values greater than 1.0 stretch the text wider.
3837      * Values less than 1.0 make the text narrower. By default, this value is 1.0.
3838      * @param size The horizontal scale factor.
3839      * @attr ref android.R.styleable#TextView_textScaleX
3840      */
3841     @android.view.RemotableViewMethod
3842     public void setTextScaleX(float size) {
3843         if (size != mTextPaint.getTextScaleX()) {
3844             mUserSetTextScaleX = true;
3845             mTextPaint.setTextScaleX(size);
3846
3847             if (mLayout != null) {
3848                 nullLayouts();
3849                 requestLayout();
3850                 invalidate();
3851             }
3852         }
3853     }
3854
3855     /**
3856      * Sets the typeface and style in which the text should be displayed.
3857      * Note that not all Typeface families actually have bold and italic
3858      * variants, so you may need to use
3859      * {@link #setTypeface(Typeface, int)} to get the appearance
3860      * that you actually want.
3861      *
3862      * @see #getTypeface()
3863      *
3864      * @attr ref android.R.styleable#TextView_fontFamily
3865      * @attr ref android.R.styleable#TextView_typeface
3866      * @attr ref android.R.styleable#TextView_textStyle
3867      */
3868     public void setTypeface(Typeface tf) {
3869         if (mTextPaint.getTypeface() != tf) {
3870             mTextPaint.setTypeface(tf);
3871
3872             if (mLayout != null) {
3873                 nullLayouts();
3874                 requestLayout();
3875                 invalidate();
3876             }
3877         }
3878     }
3879
3880     /**
3881      * Gets the current {@link Typeface} that is used to style the text.
3882      * @return The current Typeface.
3883      *
3884      * @see #setTypeface(Typeface)
3885      *
3886      * @attr ref android.R.styleable#TextView_fontFamily
3887      * @attr ref android.R.styleable#TextView_typeface
3888      * @attr ref android.R.styleable#TextView_textStyle
3889      */
3890     public Typeface getTypeface() {
3891         return mTextPaint.getTypeface();
3892     }
3893
3894     /**
3895      * Set the TextView's elegant height metrics flag. This setting selects font
3896      * variants that have not been compacted to fit Latin-based vertical
3897      * metrics, and also increases top and bottom bounds to provide more space.
3898      *
3899      * @param elegant set the paint's elegant metrics flag.
3900      *
3901      * @see #isElegantTextHeight()
3902      * @see Paint#isElegantTextHeight()
3903      *
3904      * @attr ref android.R.styleable#TextView_elegantTextHeight
3905      */
3906     public void setElegantTextHeight(boolean elegant) {
3907         if (elegant != mTextPaint.isElegantTextHeight()) {
3908             mTextPaint.setElegantTextHeight(elegant);
3909             if (mLayout != null) {
3910                 nullLayouts();
3911                 requestLayout();
3912                 invalidate();
3913             }
3914         }
3915     }
3916
3917     /**
3918      * Set whether to respect the ascent and descent of the fallback fonts that are used in
3919      * displaying the text (which is needed to avoid text from consecutive lines running into
3920      * each other). If set, fallback fonts that end up getting used can increase the ascent
3921      * and descent of the lines that they are used on.
3922      * <p/>
3923      * It is required to be true if text could be in languages like Burmese or Tibetan where text
3924      * is typically much taller or deeper than Latin text.
3925      *
3926      * @param enabled whether to expand linespacing based on fallback fonts, {@code true} by default
3927      *
3928      * @see StaticLayout.Builder#setUseLineSpacingFromFallbacks(boolean)
3929      *
3930      * @attr ref android.R.styleable#TextView_fallbackLineSpacing
3931      */
3932     public void setFallbackLineSpacing(boolean enabled) {
3933         if (mUseFallbackLineSpacing != enabled) {
3934             mUseFallbackLineSpacing = enabled;
3935             if (mLayout != null) {
3936                 nullLayouts();
3937                 requestLayout();
3938                 invalidate();
3939             }
3940         }
3941     }
3942
3943     /**
3944      * @return whether fallback line spacing is enabled, {@code true} by default
3945      *
3946      * @see #setFallbackLineSpacing(boolean)
3947      *
3948      * @attr ref android.R.styleable#TextView_fallbackLineSpacing
3949      */
3950     public boolean isFallbackLineSpacing() {
3951         return mUseFallbackLineSpacing;
3952     }
3953
3954     /**
3955      * Get the value of the TextView's elegant height metrics flag. This setting selects font
3956      * variants that have not been compacted to fit Latin-based vertical
3957      * metrics, and also increases top and bottom bounds to provide more space.
3958      * @return {@code true} if the elegant height metrics flag is set.
3959      *
3960      * @see #setElegantTextHeight(boolean)
3961      * @see Paint#setElegantTextHeight(boolean)
3962      */
3963     public boolean isElegantTextHeight() {
3964         return mTextPaint.isElegantTextHeight();
3965     }
3966
3967     /**
3968      * Gets the text letter-space value, which determines the spacing between characters.
3969      * The value returned is in ems. Normally, this value is 0.0.
3970      * @return The text letter-space value in ems.
3971      *
3972      * @see #setLetterSpacing(float)
3973      * @see Paint#setLetterSpacing
3974      */
3975     public float getLetterSpacing() {
3976         return mTextPaint.getLetterSpacing();
3977     }
3978
3979     /**
3980      * Sets text letter-spacing in em units.  Typical values
3981      * for slight expansion will be around 0.05.  Negative values tighten text.
3982      *
3983      * @see #getLetterSpacing()
3984      * @see Paint#getLetterSpacing
3985      *
3986      * @param letterSpacing A text letter-space value in ems.
3987      * @attr ref android.R.styleable#TextView_letterSpacing
3988      */
3989     @android.view.RemotableViewMethod
3990     public void setLetterSpacing(float letterSpacing) {
3991         if (letterSpacing != mTextPaint.getLetterSpacing()) {
3992             mTextPaint.setLetterSpacing(letterSpacing);
3993
3994             if (mLayout != null) {
3995                 nullLayouts();
3996                 requestLayout();
3997                 invalidate();
3998             }
3999         }
4000     }
4001
4002     /**
4003      * Returns the font feature settings. The format is the same as the CSS
4004      * font-feature-settings attribute:
4005      * <a href="https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop">
4006      *     https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop</a>
4007      *
4008      * @return the currently set font feature settings.  Default is null.
4009      *
4010      * @see #setFontFeatureSettings(String)
4011      * @see Paint#setFontFeatureSettings(String) Paint.setFontFeatureSettings(String)
4012      */
4013     @Nullable
4014     public String getFontFeatureSettings() {
4015         return mTextPaint.getFontFeatureSettings();
4016     }
4017
4018     /**
4019      * Returns the font variation settings.
4020      *
4021      * @return the currently set font variation settings.  Returns null if no variation is
4022      * specified.
4023      *
4024      * @see #setFontVariationSettings(String)
4025      * @see Paint#setFontVariationSettings(String) Paint.setFontVariationSettings(String)
4026      */
4027     @Nullable
4028     public String getFontVariationSettings() {
4029         return mTextPaint.getFontVariationSettings();
4030     }
4031
4032     /**
4033      * Sets the break strategy for breaking paragraphs into lines. The default value for
4034      * TextView is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}, and the default value for
4035      * EditText is {@link Layout#BREAK_STRATEGY_SIMPLE}, the latter to avoid the
4036      * text "dancing" when being edited.
4037      *
4038      * @attr ref android.R.styleable#TextView_breakStrategy
4039      * @see #getBreakStrategy()
4040      */
4041     public void setBreakStrategy(@Layout.BreakStrategy int breakStrategy) {
4042         mBreakStrategy = breakStrategy;
4043         if (mLayout != null) {
4044             nullLayouts();
4045             requestLayout();
4046             invalidate();
4047         }
4048     }
4049
4050     /**
4051      * Gets the current strategy for breaking paragraphs into lines.
4052      * @return the current strategy for breaking paragraphs into lines.
4053      *
4054      * @attr ref android.R.styleable#TextView_breakStrategy
4055      * @see #setBreakStrategy(int)
4056      */
4057     @Layout.BreakStrategy
4058     public int getBreakStrategy() {
4059         return mBreakStrategy;
4060     }
4061
4062     /**
4063      * Sets the frequency of automatic hyphenation to use when determining word breaks.
4064      * The default value for both TextView and {@link EditText} is
4065      * {@link Layout#HYPHENATION_FREQUENCY_NORMAL}.
4066      * Note that the default hyphenation frequency value is set from the theme.
4067      *
4068      * @param hyphenationFrequency The hyphenation frequency to use.
4069      * @attr ref android.R.styleable#TextView_hyphenationFrequency
4070      * @see #getHyphenationFrequency()
4071      */
4072     public void setHyphenationFrequency(@Layout.HyphenationFrequency int hyphenationFrequency) {
4073         mHyphenationFrequency = hyphenationFrequency;
4074         if (mLayout != null) {
4075             nullLayouts();
4076             requestLayout();
4077             invalidate();
4078         }
4079     }
4080
4081     /**
4082      * Gets the current frequency of automatic hyphenation to be used when determining word breaks.
4083      * @return the current frequency of automatic hyphenation to be used when determining word
4084      * breaks.
4085      *
4086      * @attr ref android.R.styleable#TextView_hyphenationFrequency
4087      * @see #setHyphenationFrequency(int)
4088      */
4089     @Layout.HyphenationFrequency
4090     public int getHyphenationFrequency() {
4091         return mHyphenationFrequency;
4092     }
4093
4094     /**
4095      * Gets the parameters for text layout precomputation, for use with {@link PrecomputedText}.
4096      *
4097      * @return a current {@link PrecomputedText.Params}
4098      * @see PrecomputedText
4099      */
4100     public @NonNull PrecomputedText.Params getTextMetricsParams() {
4101         return new PrecomputedText.Params(new TextPaint(mTextPaint), getTextDirectionHeuristic(),
4102                 mBreakStrategy, mHyphenationFrequency);
4103     }
4104
4105     /**
4106      * Apply the text layout parameter.
4107      *
4108      * Update the TextView parameters to be compatible with {@link PrecomputedText.Params}.
4109      * @see PrecomputedText
4110      */
4111     public void setTextMetricsParams(@NonNull PrecomputedText.Params params) {
4112         mTextPaint.set(params.getTextPaint());
4113         mTextDir = params.getTextDirection();
4114         mBreakStrategy = params.getBreakStrategy();
4115         mHyphenationFrequency = params.getHyphenationFrequency();
4116         if (mLayout != null) {
4117             nullLayouts();
4118             requestLayout();
4119             invalidate();
4120         }
4121     }
4122
4123     /**
4124      * Set justification mode. The default value is {@link Layout#JUSTIFICATION_MODE_NONE}. If the
4125      * last line is too short for justification, the last line will be displayed with the
4126      * alignment set by {@link android.view.View#setTextAlignment}.
4127      *
4128      * @see #getJustificationMode()
4129      */
4130     @Layout.JustificationMode
4131     public void setJustificationMode(@Layout.JustificationMode int justificationMode) {
4132         mJustificationMode = justificationMode;
4133         if (mLayout != null) {
4134             nullLayouts();
4135             requestLayout();
4136             invalidate();
4137         }
4138     }
4139
4140     /**
4141      * @return true if currently paragraph justification mode.
4142      *
4143      * @see #setJustificationMode(int)
4144      */
4145     public @Layout.JustificationMode int getJustificationMode() {
4146         return mJustificationMode;
4147     }
4148
4149     /**
4150      * Sets font feature settings. The format is the same as the CSS
4151      * font-feature-settings attribute:
4152      * <a href="https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop">
4153      *     https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop</a>
4154      *
4155      * @param fontFeatureSettings font feature settings represented as CSS compatible string
4156      *
4157      * @see #getFontFeatureSettings()
4158      * @see Paint#getFontFeatureSettings() Paint.getFontFeatureSettings()
4159      *
4160      * @attr ref android.R.styleable#TextView_fontFeatureSettings
4161      */
4162     @android.view.RemotableViewMethod
4163     public void setFontFeatureSettings(@Nullable String fontFeatureSettings) {
4164         if (fontFeatureSettings != mTextPaint.getFontFeatureSettings()) {
4165             mTextPaint.setFontFeatureSettings(fontFeatureSettings);
4166
4167             if (mLayout != null) {
4168                 nullLayouts();
4169                 requestLayout();
4170                 invalidate();
4171             }
4172         }
4173     }
4174
4175
4176     /**
4177      * Sets TrueType or OpenType font variation settings. The settings string is constructed from
4178      * multiple pairs of axis tag and style values. The axis tag must contain four ASCII characters
4179      * and must be wrapped with single quotes (U+0027) or double quotes (U+0022). Axis strings that
4180      * are longer or shorter than four characters, or contain characters outside of U+0020..U+007E
4181      * are invalid. If a specified axis name is not defined in the font, the settings will be
4182      * ignored.
4183      *
4184      * <p>
4185      * Examples,
4186      * <ul>
4187      * <li>Set font width to 150.
4188      * <pre>
4189      * <code>
4190      *   TextView textView = (TextView) findViewById(R.id.textView);
4191      *   textView.setFontVariationSettings("'wdth' 150");
4192      * </code>
4193      * </pre>
4194      * </li>
4195      *
4196      * <li>Set the font slant to 20 degrees and ask for italic style.
4197      * <pre>
4198      * <code>
4199      *   TextView textView = (TextView) findViewById(R.id.textView);
4200      *   textView.setFontVariationSettings("'slnt' 20, 'ital' 1");
4201      * </code>
4202      * </pre>
4203      * </p>
4204      * </li>
4205      * </ul>
4206      *
4207      * @param fontVariationSettings font variation settings. You can pass null or empty string as
4208      *                              no variation settings.
4209      * @return true if the given settings is effective to at least one font file underlying this
4210      *         TextView. This function also returns true for empty settings string. Otherwise
4211      *         returns false.
4212      *
4213      * @throws IllegalArgumentException If given string is not a valid font variation settings
4214      *                                  format.
4215      *
4216      * @see #getFontVariationSettings()
4217      * @see FontVariationAxis
4218      */
4219     public boolean setFontVariationSettings(@Nullable String fontVariationSettings) {
4220         final String existingSettings = mTextPaint.getFontVariationSettings();
4221         if (fontVariationSettings == existingSettings
4222                 || (fontVariationSettings != null
4223                         && fontVariationSettings.equals(existingSettings))) {
4224             return true;
4225         }
4226         boolean effective = mTextPaint.setFontVariationSettings(fontVariationSettings);
4227
4228         if (effective && mLayout != null) {
4229             nullLayouts();
4230             requestLayout();
4231             invalidate();
4232         }
4233         return effective;
4234     }
4235
4236     /**
4237      * Sets the text color for all the states (normal, selected,
4238      * focused) to be this color.
4239      *
4240      * @param color A color value in the form 0xAARRGGBB.
4241      * Do not pass a resource ID. To get a color value from a resource ID, call
4242      * {@link android.support.v4.content.ContextCompat#getColor(Context, int) getColor}.
4243      *
4244      * @see #setTextColor(ColorStateList)
4245      * @see #getTextColors()
4246      *
4247      * @attr ref android.R.styleable#TextView_textColor
4248      */
4249     @android.view.RemotableViewMethod
4250     public void setTextColor(@ColorInt int color) {
4251         mTextColor = ColorStateList.valueOf(color);
4252         updateTextColors();
4253     }
4254
4255     /**
4256      * Sets the text color.
4257      *
4258      * @see #setTextColor(int)
4259      * @see #getTextColors()
4260      * @see #setHintTextColor(ColorStateList)
4261      * @see #setLinkTextColor(ColorStateList)
4262      *
4263      * @attr ref android.R.styleable#TextView_textColor
4264      */
4265     @android.view.RemotableViewMethod
4266     public void setTextColor(ColorStateList colors) {
4267         if (colors == null) {
4268             throw new NullPointerException();
4269         }
4270
4271         mTextColor = colors;
4272         updateTextColors();
4273     }
4274
4275     /**
4276      * Gets the text colors for the different states (normal, selected, focused) of the TextView.
4277      *
4278      * @see #setTextColor(ColorStateList)
4279      * @see #setTextColor(int)
4280      *
4281      * @attr ref android.R.styleable#TextView_textColor
4282      */
4283     public final ColorStateList getTextColors() {
4284         return mTextColor;
4285     }
4286
4287     /**
4288      * Return the current color selected for normal text.
4289      *
4290      * @return Returns the current text color.
4291      */
4292     @ColorInt
4293     public final int getCurrentTextColor() {
4294         return mCurTextColor;
4295     }
4296
4297     /**
4298      * Sets the color used to display the selection highlight.
4299      *
4300      * @attr ref android.R.styleable#TextView_textColorHighlight
4301      */
4302     @android.view.RemotableViewMethod
4303     public void setHighlightColor(@ColorInt int color) {
4304         if (mHighlightColor != color) {
4305             mHighlightColor = color;
4306             invalidate();
4307         }
4308     }
4309
4310     /**
4311      * @return the color used to display the selection highlight
4312      *
4313      * @see #setHighlightColor(int)
4314      *
4315      * @attr ref android.R.styleable#TextView_textColorHighlight
4316      */
4317     @ColorInt
4318     public int getHighlightColor() {
4319         return mHighlightColor;
4320     }
4321
4322     /**
4323      * Sets whether the soft input method will be made visible when this
4324      * TextView gets focused. The default is true.
4325      */
4326     @android.view.RemotableViewMethod
4327     public final void setShowSoftInputOnFocus(boolean show) {
4328         createEditorIfNeeded();
4329         mEditor.mShowSoftInputOnFocus = show;
4330     }
4331
4332     /**
4333      * Returns whether the soft input method will be made visible when this
4334      * TextView gets focused. The default is true.
4335      */
4336     public final boolean getShowSoftInputOnFocus() {
4337         // When there is no Editor, return default true value
4338         return mEditor == null || mEditor.mShowSoftInputOnFocus;
4339     }
4340
4341     /**
4342      * Gives the text a shadow of the specified blur radius and color, the specified
4343      * distance from its drawn position.
4344      * <p>
4345      * The text shadow produced does not interact with the properties on view
4346      * that are responsible for real time shadows,
4347      * {@link View#getElevation() elevation} and
4348      * {@link View#getTranslationZ() translationZ}.
4349      *
4350      * @see Paint#setShadowLayer(float, float, float, int)
4351      *
4352      * @attr ref android.R.styleable#TextView_shadowColor
4353      * @attr ref android.R.styleable#TextView_shadowDx
4354      * @attr ref android.R.styleable#TextView_shadowDy
4355      * @attr ref android.R.styleable#TextView_shadowRadius
4356      */
4357     public void setShadowLayer(float radius, float dx, float dy, int color) {
4358         mTextPaint.setShadowLayer(radius, dx, dy, color);
4359
4360         mShadowRadius = radius;
4361         mShadowDx = dx;
4362         mShadowDy = dy;
4363         mShadowColor = color;
4364
4365         // Will change text clip region
4366         if (mEditor != null) {
4367             mEditor.invalidateTextDisplayList();
4368             mEditor.invalidateHandlesAndActionMode();
4369         }
4370         invalidate();
4371     }
4372
4373     /**
4374      * Gets the radius of the shadow layer.
4375      *
4376      * @return the radius of the shadow layer. If 0, the shadow layer is not visible
4377      *
4378      * @see #setShadowLayer(float, float, float, int)
4379      *
4380      * @attr ref android.R.styleable#TextView_shadowRadius
4381      */
4382     public float getShadowRadius() {
4383         return mShadowRadius;
4384     }
4385
4386     /**
4387      * @return the horizontal offset of the shadow layer
4388      *
4389      * @see #setShadowLayer(float, float, float, int)
4390      *
4391      * @attr ref android.R.styleable#TextView_shadowDx
4392      */
4393     public float getShadowDx() {
4394         return mShadowDx;
4395     }
4396
4397     /**
4398      * Gets the vertical offset of the shadow layer.
4399      * @return The vertical offset of the shadow layer.
4400      *
4401      * @see #setShadowLayer(float, float, float, int)
4402      *
4403      * @attr ref android.R.styleable#TextView_shadowDy
4404      */
4405     public float getShadowDy() {
4406         return mShadowDy;
4407     }
4408
4409     /**
4410      * Gets the color of the shadow layer.
4411      * @return the color of the shadow layer
4412      *
4413      * @see #setShadowLayer(float, float, float, int)
4414      *
4415      * @attr ref android.R.styleable#TextView_shadowColor
4416      */
4417     @ColorInt
4418     public int getShadowColor() {
4419         return mShadowColor;
4420     }
4421
4422     /**
4423      * Gets the {@link TextPaint} used for the text.
4424      * Use this only to consult the Paint's properties and not to change them.
4425      * @return The base paint used for the text.
4426      */
4427     public TextPaint getPaint() {
4428         return mTextPaint;
4429     }
4430
4431     /**
4432      * Sets the autolink mask of the text.  See {@link
4433      * android.text.util.Linkify#ALL Linkify.ALL} and peers for
4434      * possible values.
4435      *
4436      * @attr ref android.R.styleable#TextView_autoLink
4437      */
4438     @android.view.RemotableViewMethod
4439     public final void setAutoLinkMask(int mask) {
4440         mAutoLinkMask = mask;
4441     }
4442
4443     /**
4444      * Sets whether the movement method will automatically be set to
4445      * {@link LinkMovementMethod} if {@link #setAutoLinkMask} has been
4446      * set to nonzero and links are detected in {@link #setText}.
4447      * The default is true.
4448      *
4449      * @attr ref android.R.styleable#TextView_linksClickable
4450      */
4451     @android.view.RemotableViewMethod
4452     public final void setLinksClickable(boolean whether) {
4453         mLinksClickable = whether;
4454     }
4455
4456     /**
4457      * Returns whether the movement method will automatically be set to
4458      * {@link LinkMovementMethod} if {@link #setAutoLinkMask} has been
4459      * set to nonzero and links are detected in {@link #setText}.
4460      * The default is true.
4461      *
4462      * @attr ref android.R.styleable#TextView_linksClickable
4463      */
4464     public final boolean getLinksClickable() {
4465         return mLinksClickable;
4466     }
4467
4468     /**
4469      * Returns the list of {@link android.text.style.URLSpan URLSpans} attached to the text
4470      * (by {@link Linkify} or otherwise) if any.  You can call
4471      * {@link URLSpan#getURL} on them to find where they link to
4472      * or use {@link Spanned#getSpanStart} and {@link Spanned#getSpanEnd}
4473      * to find the region of the text they are attached to.
4474      */
4475     public URLSpan[] getUrls() {
4476         if (mText instanceof Spanned) {
4477             return ((Spanned) mText).getSpans(0, mText.length(), URLSpan.class);
4478         } else {
4479             return new URLSpan[0];
4480         }
4481     }
4482
4483     /**
4484      * Sets the color of the hint text for all the states (disabled, focussed, selected...) of this
4485      * TextView.
4486      *
4487      * @see #setHintTextColor(ColorStateList)
4488      * @see #getHintTextColors()
4489      * @see #setTextColor(int)
4490      *
4491      * @attr ref android.R.styleable#TextView_textColorHint
4492      */
4493     @android.view.RemotableViewMethod
4494     public final void setHintTextColor(@ColorInt int color) {
4495         mHintTextColor = ColorStateList.valueOf(color);
4496         updateTextColors();
4497     }
4498
4499     /**
4500      * Sets the color of the hint text.
4501      *
4502      * @see #getHintTextColors()
4503      * @see #setHintTextColor(int)
4504      * @see #setTextColor(ColorStateList)
4505      * @see #setLinkTextColor(ColorStateList)
4506      *
4507      * @attr ref android.R.styleable#TextView_textColorHint
4508      */
4509     public final void setHintTextColor(ColorStateList colors) {
4510         mHintTextColor = colors;
4511         updateTextColors();
4512     }
4513
4514     /**
4515      * @return the color of the hint text, for the different states of this TextView.
4516      *
4517      * @see #setHintTextColor(ColorStateList)
4518      * @see #setHintTextColor(int)
4519      * @see #setTextColor(ColorStateList)
4520      * @see #setLinkTextColor(ColorStateList)
4521      *
4522      * @attr ref android.R.styleable#TextView_textColorHint
4523      */
4524     public final ColorStateList getHintTextColors() {
4525         return mHintTextColor;
4526     }
4527
4528     /**
4529      * <p>Return the current color selected to paint the hint text.</p>
4530      *
4531      * @return Returns the current hint text color.
4532      */
4533     @ColorInt
4534     public final int getCurrentHintTextColor() {
4535         return mHintTextColor != null ? mCurHintTextColor : mCurTextColor;
4536     }
4537
4538     /**
4539      * Sets the color of links in the text.
4540      *
4541      * @see #setLinkTextColor(ColorStateList)
4542      * @see #getLinkTextColors()
4543      *
4544      * @attr ref android.R.styleable#TextView_textColorLink
4545      */
4546     @android.view.RemotableViewMethod
4547     public final void setLinkTextColor(@ColorInt int color) {
4548         mLinkTextColor = ColorStateList.valueOf(color);
4549         updateTextColors();
4550     }
4551
4552     /**
4553      * Sets the color of links in the text.
4554      *
4555      * @see #setLinkTextColor(int)
4556      * @see #getLinkTextColors()
4557      * @see #setTextColor(ColorStateList)
4558      * @see #setHintTextColor(ColorStateList)
4559      *
4560      * @attr ref android.R.styleable#TextView_textColorLink
4561      */
4562     public final void setLinkTextColor(ColorStateList colors) {
4563         mLinkTextColor = colors;
4564         updateTextColors();
4565     }
4566
4567     /**
4568      * @return the list of colors used to paint the links in the text, for the different states of
4569      * this TextView
4570      *
4571      * @see #setLinkTextColor(ColorStateList)
4572      * @see #setLinkTextColor(int)
4573      *
4574      * @attr ref android.R.styleable#TextView_textColorLink
4575      */
4576     public final ColorStateList getLinkTextColors() {
4577         return mLinkTextColor;
4578     }
4579
4580     /**
4581      * Sets the horizontal alignment of the text and the
4582      * vertical gravity that will be used when there is extra space
4583      * in the TextView beyond what is required for the text itself.
4584      *
4585      * @see android.view.Gravity
4586      * @attr ref android.R.styleable#TextView_gravity
4587      */
4588     public void setGravity(int gravity) {
4589         if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
4590             gravity |= Gravity.START;
4591         }
4592         if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
4593             gravity |= Gravity.TOP;
4594         }
4595
4596         boolean newLayout = false;
4597
4598         if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK)
4599                 != (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK)) {
4600             newLayout = true;
4601         }
4602
4603         if (gravity != mGravity) {
4604             invalidate();
4605         }
4606
4607         mGravity = gravity;
4608
4609         if (mLayout != null && newLayout) {
4610             // XXX this is heavy-handed because no actual content changes.
4611             int want = mLayout.getWidth();
4612             int hintWant = mHintLayout == null ? 0 : mHintLayout.getWidth();
4613
4614             makeNewLayout(want, hintWant, UNKNOWN_BORING, UNKNOWN_BORING,
4615                     mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(), true);
4616         }
4617     }
4618
4619     /**
4620      * Returns the horizontal and vertical alignment of this TextView.
4621      *
4622      * @see android.view.Gravity
4623      * @attr ref android.R.styleable#TextView_gravity
4624      */
4625     public int getGravity() {
4626         return mGravity;
4627     }
4628
4629     /**
4630      * Gets the flags on the Paint being used to display the text.
4631      * @return The flags on the Paint being used to display the text.
4632      * @see Paint#getFlags
4633      */
4634     public int getPaintFlags() {
4635         return mTextPaint.getFlags();
4636     }
4637
4638     /**
4639      * Sets flags on the Paint being used to display the text and
4640      * reflows the text if they are different from the old flags.
4641      * @see Paint#setFlags
4642      */
4643     @android.view.RemotableViewMethod
4644     public void setPaintFlags(int flags) {
4645         if (mTextPaint.getFlags() != flags) {
4646             mTextPaint.setFlags(flags);
4647
4648             if (mLayout != null) {
4649                 nullLayouts();
4650                 requestLayout();
4651                 invalidate();
4652             }
4653         }
4654     }
4655
4656     /**
4657      * Sets whether the text should be allowed to be wider than the
4658      * View is.  If false, it will be wrapped to the width of the View.
4659      *
4660      * @attr ref android.R.styleable#TextView_scrollHorizontally
4661      */
4662     public void setHorizontallyScrolling(boolean whether) {
4663         if (mHorizontallyScrolling != whether) {
4664             mHorizontallyScrolling = whether;
4665
4666             if (mLayout != null) {
4667                 nullLayouts();
4668                 requestLayout();
4669                 invalidate();
4670             }
4671         }
4672     }
4673
4674     /**
4675      * Returns whether the text is allowed to be wider than the View is.
4676      * If false, the text will be wrapped to the width of the View.
4677      *
4678      * @attr ref android.R.styleable#TextView_scrollHorizontally
4679      * @hide
4680      */
4681     public boolean getHorizontallyScrolling() {
4682         return mHorizontallyScrolling;
4683     }
4684
4685     /**
4686      * Sets the height of the TextView to be at least {@code minLines} tall.
4687      * <p>
4688      * This value is used for height calculation if LayoutParams does not force TextView to have an
4689      * exact height. Setting this value overrides other previous minimum height configurations such
4690      * as {@link #setMinHeight(int)} or {@link #setHeight(int)}. {@link #setSingleLine()} will set
4691      * this value to 1.
4692      *
4693      * @param minLines the minimum height of TextView in terms of number of lines
4694      *
4695      * @see #getMinLines()
4696      * @see #setLines(int)
4697      *
4698      * @attr ref android.R.styleable#TextView_minLines
4699      */
4700     @android.view.RemotableViewMethod
4701     public void setMinLines(int minLines) {
4702         mMinimum = minLines;
4703         mMinMode = LINES;
4704
4705         requestLayout();
4706         invalidate();
4707     }
4708
4709     /**
4710      * Returns the minimum height of TextView in terms of number of lines or -1 if the minimum
4711      * height was set using {@link #setMinHeight(int)} or {@link #setHeight(int)}.
4712      *
4713      * @return the minimum height of TextView in terms of number of lines or -1 if the minimum
4714      *         height is not defined in lines
4715      *
4716      * @see #setMinLines(int)
4717      * @see #setLines(int)
4718      *
4719      * @attr ref android.R.styleable#TextView_minLines
4720      */
4721     public int getMinLines() {
4722         return mMinMode == LINES ? mMinimum : -1;
4723     }
4724
4725     /**
4726      * Sets the height of the TextView to be at least {@code minPixels} tall.
4727      * <p>
4728      * This value is used for height calculation if LayoutParams does not force TextView to have an
4729      * exact height. Setting this value overrides previous minimum height configurations such as
4730      * {@link #setMinLines(int)} or {@link #setLines(int)}.
4731      * <p>
4732      * The value given here is different than {@link #setMinimumHeight(int)}. Between
4733      * {@code minHeight} and the value set in {@link #setMinimumHeight(int)}, the greater one is
4734      * used to decide the final height.
4735      *
4736      * @param minPixels the minimum height of TextView in terms of pixels
4737      *
4738      * @see #getMinHeight()
4739      * @see #setHeight(int)
4740      *
4741      * @attr ref android.R.styleable#TextView_minHeight
4742      */
4743     @android.view.RemotableViewMethod
4744     public void setMinHeight(int minPixels) {
4745         mMinimum = minPixels;
4746         mMinMode = PIXELS;
4747
4748         requestLayout();
4749         invalidate();
4750     }
4751
4752     /**
4753      * Returns the minimum height of TextView in terms of pixels or -1 if the minimum height was
4754      * set using {@link #setMinLines(int)} or {@link #setLines(int)}.
4755      *
4756      * @return the minimum height of TextView in terms of pixels or -1 if the minimum height is not
4757      *         defined in pixels
4758      *
4759      * @see #setMinHeight(int)
4760      * @see #setHeight(int)
4761      *
4762      * @attr ref android.R.styleable#TextView_minHeight
4763      */
4764     public int getMinHeight() {
4765         return mMinMode == PIXELS ? mMinimum : -1;
4766     }
4767
4768     /**
4769      * Sets the height of the TextView to be at most {@code maxLines} tall.
4770      * <p>
4771      * This value is used for height calculation if LayoutParams does not force TextView to have an
4772      * exact height. Setting this value overrides previous maximum height configurations such as
4773      * {@link #setMaxHeight(int)} or {@link #setLines(int)}.
4774      *
4775      * @param maxLines the maximum height of TextView in terms of number of lines
4776      *
4777      * @see #getMaxLines()
4778      * @see #setLines(int)
4779      *
4780      * @attr ref android.R.styleable#TextView_maxLines
4781      */
4782     @android.view.RemotableViewMethod
4783     public void setMaxLines(int maxLines) {
4784         mMaximum = maxLines;
4785         mMaxMode = LINES;
4786
4787         requestLayout();
4788         invalidate();
4789     }
4790
4791     /**
4792      * Returns the maximum height of TextView in terms of number of lines or -1 if the
4793      * maximum height was set using {@link #setMaxHeight(int)} or {@link #setHeight(int)}.
4794      *
4795      * @return the maximum height of TextView in terms of number of lines. -1 if the maximum height
4796      *         is not defined in lines.
4797      *
4798      * @see #setMaxLines(int)
4799      * @see #setLines(int)
4800      *
4801      * @attr ref android.R.styleable#TextView_maxLines
4802      */
4803     public int getMaxLines() {
4804         return mMaxMode == LINES ? mMaximum : -1;
4805     }
4806
4807     /**
4808      * Sets the height of the TextView to be at most {@code maxPixels} tall.
4809      * <p>
4810      * This value is used for height calculation if LayoutParams does not force TextView to have an
4811      * exact height. Setting this value overrides previous maximum height configurations such as
4812      * {@link #setMaxLines(int)} or {@link #setLines(int)}.
4813      *
4814      * @param maxPixels the maximum height of TextView in terms of pixels
4815      *
4816      * @see #getMaxHeight()
4817      * @see #setHeight(int)
4818      *
4819      * @attr ref android.R.styleable#TextView_maxHeight
4820      */
4821     @android.view.RemotableViewMethod
4822     public void setMaxHeight(int maxPixels) {
4823         mMaximum = maxPixels;
4824         mMaxMode = PIXELS;
4825
4826         requestLayout();
4827         invalidate();
4828     }
4829
4830     /**
4831      * Returns the maximum height of TextView in terms of pixels or -1 if the maximum height was
4832      * set using {@link #setMaxLines(int)} or {@link #setLines(int)}.
4833      *
4834      * @return the maximum height of TextView in terms of pixels or -1 if the maximum height
4835      *         is not defined in pixels
4836      *
4837      * @see #setMaxHeight(int)
4838      * @see #setHeight(int)
4839      *
4840      * @attr ref android.R.styleable#TextView_maxHeight
4841      */
4842     public int getMaxHeight() {
4843         return mMaxMode == PIXELS ? mMaximum : -1;
4844     }
4845
4846     /**
4847      * Sets the height of the TextView to be exactly {@code lines} tall.
4848      * <p>
4849      * This value is used for height calculation if LayoutParams does not force TextView to have an
4850      * exact height. Setting this value overrides previous minimum/maximum height configurations
4851      * such as {@link #setMinLines(int)} or {@link #setMaxLines(int)}. {@link #setSingleLine()} will
4852      * set this value to 1.
4853      *
4854      * @param lines the exact height of the TextView in terms of lines
4855      *
4856      * @see #setHeight(int)
4857      *
4858      * @attr ref android.R.styleable#TextView_lines
4859      */
4860     @android.view.RemotableViewMethod
4861     public void setLines(int lines) {
4862         mMaximum = mMinimum = lines;
4863         mMaxMode = mMinMode = LINES;
4864
4865         requestLayout();
4866         invalidate();
4867     }
4868
4869     /**
4870      * Sets the height of the TextView to be exactly <code>pixels</code> tall.
4871      * <p>
4872      * This value is used for height calculation if LayoutParams does not force TextView to have an
4873      * exact height. Setting this value overrides previous minimum/maximum height configurations
4874      * such as {@link #setMinHeight(int)} or {@link #setMaxHeight(int)}.
4875      *
4876      * @param pixels the exact height of the TextView in terms of pixels
4877      *
4878      * @see #setLines(int)
4879      *
4880      * @attr ref android.R.styleable#TextView_height
4881      */
4882     @android.view.RemotableViewMethod
4883     public void setHeight(int pixels) {
4884         mMaximum = mMinimum = pixels;
4885         mMaxMode = mMinMode = PIXELS;
4886
4887         requestLayout();
4888         invalidate();
4889     }
4890
4891     /**
4892      * Sets the width of the TextView to be at least {@code minEms} wide.
4893      * <p>
4894      * This value is used for width calculation if LayoutParams does not force TextView to have an
4895      * exact width. Setting this value overrides previous minimum width configurations such as
4896      * {@link #setMinWidth(int)} or {@link #setWidth(int)}.
4897      *
4898      * @param minEms the minimum width of TextView in terms of ems
4899      *
4900      * @see #getMinEms()
4901      * @see #setEms(int)
4902      *
4903      * @attr ref android.R.styleable#TextView_minEms
4904      */
4905     @android.view.RemotableViewMethod
4906     public void setMinEms(int minEms) {
4907         mMinWidth = minEms;
4908         mMinWidthMode = EMS;
4909
4910         requestLayout();
4911         invalidate();
4912     }
4913
4914     /**
4915      * Returns the minimum width of TextView in terms of ems or -1 if the minimum width was set
4916      * using {@link #setMinWidth(int)} or {@link #setWidth(int)}.
4917      *
4918      * @return the minimum width of TextView in terms of ems. -1 if the minimum width is not
4919      *         defined in ems
4920      *
4921      * @see #setMinEms(int)
4922      * @see #setEms(int)
4923      *
4924      * @attr ref android.R.styleable#TextView_minEms
4925      */
4926     public int getMinEms() {
4927         return mMinWidthMode == EMS ? mMinWidth : -1;
4928     }
4929
4930     /**
4931      * Sets the width of the TextView to be at least {@code minPixels} wide.
4932      * <p>
4933      * This value is used for width calculation if LayoutParams does not force TextView to have an
4934      * exact width. Setting this value overrides previous minimum width configurations such as
4935      * {@link #setMinEms(int)} or {@link #setEms(int)}.
4936      * <p>
4937      * The value given here is different than {@link #setMinimumWidth(int)}. Between
4938      * {@code minWidth} and the value set in {@link #setMinimumWidth(int)}, the greater one is used
4939      * to decide the final width.
4940      *
4941      * @param minPixels the minimum width of TextView in terms of pixels
4942      *
4943      * @see #getMinWidth()
4944      * @see #setWidth(int)
4945      *
4946      * @attr ref android.R.styleable#TextView_minWidth
4947      */
4948     @android.view.RemotableViewMethod
4949     public void setMinWidth(int minPixels) {
4950         mMinWidth = minPixels;
4951         mMinWidthMode = PIXELS;
4952
4953         requestLayout();
4954         invalidate();
4955     }
4956
4957     /**
4958      * Returns the minimum width of TextView in terms of pixels or -1 if the minimum width was set
4959      * using {@link #setMinEms(int)} or {@link #setEms(int)}.
4960      *
4961      * @return the minimum width of TextView in terms of pixels or -1 if the minimum width is not
4962      *         defined in pixels
4963      *
4964      * @see #setMinWidth(int)
4965      * @see #setWidth(int)
4966      *
4967      * @attr ref android.R.styleable#TextView_minWidth
4968      */
4969     public int getMinWidth() {
4970         return mMinWidthMode == PIXELS ? mMinWidth : -1;
4971     }
4972
4973     /**
4974      * Sets the width of the TextView to be at most {@code maxEms} wide.
4975      * <p>
4976      * This value is used for width calculation if LayoutParams does not force TextView to have an
4977      * exact width. Setting this value overrides previous maximum width configurations such as
4978      * {@link #setMaxWidth(int)} or {@link #setWidth(int)}.
4979      *
4980      * @param maxEms the maximum width of TextView in terms of ems
4981      *
4982      * @see #getMaxEms()
4983      * @see #setEms(int)
4984      *
4985      * @attr ref android.R.styleable#TextView_maxEms
4986      */
4987     @android.view.RemotableViewMethod
4988     public void setMaxEms(int maxEms) {
4989         mMaxWidth = maxEms;
4990         mMaxWidthMode = EMS;
4991
4992         requestLayout();
4993         invalidate();
4994     }
4995
4996     /**
4997      * Returns the maximum width of TextView in terms of ems or -1 if the maximum width was set
4998      * using {@link #setMaxWidth(int)} or {@link #setWidth(int)}.
4999      *
5000      * @return the maximum width of TextView in terms of ems or -1 if the maximum width is not
5001      *         defined in ems
5002      *
5003      * @see #setMaxEms(int)
5004      * @see #setEms(int)
5005      *
5006      * @attr ref android.R.styleable#TextView_maxEms
5007      */
5008     public int getMaxEms() {
5009         return mMaxWidthMode == EMS ? mMaxWidth : -1;
5010     }
5011
5012     /**
5013      * Sets the width of the TextView to be at most {@code maxPixels} wide.
5014      * <p>
5015      * This value is used for width calculation if LayoutParams does not force TextView to have an
5016      * exact width. Setting this value overrides previous maximum width configurations such as
5017      * {@link #setMaxEms(int)} or {@link #setEms(int)}.
5018      *
5019      * @param maxPixels the maximum width of TextView in terms of pixels
5020      *
5021      * @see #getMaxWidth()
5022      * @see #setWidth(int)
5023      *
5024      * @attr ref android.R.styleable#TextView_maxWidth
5025      */
5026     @android.view.RemotableViewMethod
5027     public void setMaxWidth(int maxPixels) {
5028         mMaxWidth = maxPixels;
5029         mMaxWidthMode = PIXELS;
5030
5031         requestLayout();
5032         invalidate();
5033     }
5034
5035     /**
5036      * Returns the maximum width of TextView in terms of pixels or -1 if the maximum width was set
5037      * using {@link #setMaxEms(int)} or {@link #setEms(int)}.
5038      *
5039      * @return the maximum width of TextView in terms of pixels. -1 if the maximum width is not
5040      *         defined in pixels
5041      *
5042      * @see #setMaxWidth(int)
5043      * @see #setWidth(int)
5044      *
5045      * @attr ref android.R.styleable#TextView_maxWidth
5046      */
5047     public int getMaxWidth() {
5048         return mMaxWidthMode == PIXELS ? mMaxWidth : -1;
5049     }
5050
5051     /**
5052      * Sets the width of the TextView to be exactly {@code ems} wide.
5053      *
5054      * This value is used for width calculation if LayoutParams does not force TextView to have an
5055      * exact width. Setting this value overrides previous minimum/maximum configurations such as
5056      * {@link #setMinEms(int)} or {@link #setMaxEms(int)}.
5057      *
5058      * @param ems the exact width of the TextView in terms of ems
5059      *
5060      * @see #setWidth(int)
5061      *
5062      * @attr ref android.R.styleable#TextView_ems
5063      */
5064     @android.view.RemotableViewMethod
5065     public void setEms(int ems) {
5066         mMaxWidth = mMinWidth = ems;
5067         mMaxWidthMode = mMinWidthMode = EMS;
5068
5069         requestLayout();
5070         invalidate();
5071     }
5072
5073     /**
5074      * Sets the width of the TextView to be exactly {@code pixels} wide.
5075      * <p>
5076      * This value is used for width calculation if LayoutParams does not force TextView to have an
5077      * exact width. Setting this value overrides previous minimum/maximum width configurations
5078      * such as {@link #setMinWidth(int)} or {@link #setMaxWidth(int)}.
5079      *
5080      * @param pixels the exact width of the TextView in terms of pixels
5081      *
5082      * @see #setEms(int)
5083      *
5084      * @attr ref android.R.styleable#TextView_width
5085      */
5086     @android.view.RemotableViewMethod
5087     public void setWidth(int pixels) {
5088         mMaxWidth = mMinWidth = pixels;
5089         mMaxWidthMode = mMinWidthMode = PIXELS;
5090
5091         requestLayout();
5092         invalidate();
5093     }
5094
5095     /**
5096      * Sets line spacing for this TextView.  Each line other than the last line will have its height
5097      * multiplied by {@code mult} and have {@code add} added to it.
5098      *
5099      * @param add The value in pixels that should be added to each line other than the last line.
5100      *            This will be applied after the multiplier
5101      * @param mult The value by which each line height other than the last line will be multiplied
5102      *             by
5103      *
5104      * @attr ref android.R.styleable#TextView_lineSpacingExtra
5105      * @attr ref android.R.styleable#TextView_lineSpacingMultiplier
5106      */
5107     public void setLineSpacing(float add, float mult) {
5108         if (mSpacingAdd != add || mSpacingMult != mult) {
5109             mSpacingAdd = add;
5110             mSpacingMult = mult;
5111
5112             if (mLayout != null) {
5113                 nullLayouts();
5114                 requestLayout();
5115                 invalidate();
5116             }
5117         }
5118     }
5119
5120     /**
5121      * Gets the line spacing multiplier
5122      *
5123      * @return the value by which each line's height is multiplied to get its actual height.
5124      *
5125      * @see #setLineSpacing(float, float)
5126      * @see #getLineSpacingExtra()
5127      *
5128      * @attr ref android.R.styleable#TextView_lineSpacingMultiplier
5129      */
5130     public float getLineSpacingMultiplier() {
5131         return mSpacingMult;
5132     }
5133
5134     /**
5135      * Gets the line spacing extra space
5136      *
5137      * @return the extra space that is added to the height of each lines of this TextView.
5138      *
5139      * @see #setLineSpacing(float, float)
5140      * @see #getLineSpacingMultiplier()
5141      *
5142      * @attr ref android.R.styleable#TextView_lineSpacingExtra
5143      */
5144     public float getLineSpacingExtra() {
5145         return mSpacingAdd;
5146     }
5147
5148     /**
5149      * Sets an explicit line height for this TextView. This is equivalent to the vertical distance
5150      * between subsequent baselines in the TextView.
5151      *
5152      * @param lineHeight the line height in pixels
5153      *
5154      * @see #setLineSpacing(float, float)
5155      * @see #getLineSpacing()
5156      *
5157      * @attr ref android.R.styleable#TextView_lineHeight
5158      */
5159     public void setLineHeight(@Px @IntRange(from = 0) int lineHeight) {
5160         Preconditions.checkArgumentNonnegative(lineHeight);
5161
5162         final int fontHeight = getPaint().getFontMetricsInt(null);
5163         // Make sure we don't setLineSpacing if it's not needed to avoid unnecessary redraw.
5164         if (lineHeight != fontHeight) {
5165             // Set lineSpacingExtra by the difference of lineSpacing with lineHeight
5166             setLineSpacing(lineHeight - fontHeight, 1f);
5167         }
5168     }
5169
5170     /**
5171      * Gets whether this view is a heading for accessibility purposes.
5172      *
5173      * @return {@code true} if the view is a heading, {@code false} otherwise.
5174      *
5175      * @attr ref android.R.styleable#TextView_accessibilityHeading
5176      */
5177     public boolean isAccessibilityHeading() {
5178         return mIsAccessibilityHeading;
5179     }
5180
5181     /**
5182      * Set if view is a heading for a section of content for accessibility purposes.
5183      *
5184      * @param isHeading {@code true} if the view is a heading, {@code false} otherwise.
5185      *
5186      * @attr ref android.R.styleable#TextView_accessibilityHeading
5187      */
5188     public void setAccessibilityHeading(boolean isHeading) {
5189         if (isHeading != mIsAccessibilityHeading) {
5190             mIsAccessibilityHeading = isHeading;
5191             notifyViewAccessibilityStateChangedIfNeeded(
5192                     AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
5193         }
5194     }
5195
5196     /**
5197      * Convenience method to append the specified text to the TextView's
5198      * display buffer, upgrading it to {@link android.widget.TextView.BufferType#EDITABLE}
5199      * if it was not already editable.
5200      *
5201      * @param text text to be appended to the already displayed text
5202      */
5203     public final void append(CharSequence text) {
5204         append(text, 0, text.length());
5205     }
5206
5207     /**
5208      * Convenience method to append the specified text slice to the TextView's
5209      * display buffer, upgrading it to {@link android.widget.TextView.BufferType#EDITABLE}
5210      * if it was not already editable.
5211      *
5212      * @param text text to be appended to the already displayed text
5213      * @param start the index of the first character in the {@code text}
5214      * @param end the index of the character following the last character in the {@code text}
5215      *
5216      * @see Appendable#append(CharSequence, int, int)
5217      */
5218     public void append(CharSequence text, int start, int end) {
5219         if (!(mText instanceof Editable)) {
5220             setText(mText, BufferType.EDITABLE);
5221         }
5222
5223         ((Editable) mText).append(text, start, end);
5224
5225         if (mAutoLinkMask != 0) {
5226             boolean linksWereAdded = Linkify.addLinks((Spannable) mText, mAutoLinkMask);
5227             // Do not change the movement method for text that support text selection as it
5228             // would prevent an arbitrary cursor displacement.
5229             if (linksWereAdded && mLinksClickable && !textCanBeSelected()) {
5230                 setMovementMethod(LinkMovementMethod.getInstance());
5231             }
5232         }
5233     }
5234
5235     private void updateTextColors() {
5236         boolean inval = false;
5237         final int[] drawableState = getDrawableState();
5238         int color = mTextColor.getColorForState(drawableState, 0);
5239         if (color != mCurTextColor) {
5240             mCurTextColor = color;
5241             inval = true;
5242         }
5243         if (mLinkTextColor != null) {
5244             color = mLinkTextColor.getColorForState(drawableState, 0);
5245             if (color != mTextPaint.linkColor) {
5246                 mTextPaint.linkColor = color;
5247                 inval = true;
5248             }
5249         }
5250         if (mHintTextColor != null) {
5251             color = mHintTextColor.getColorForState(drawableState, 0);
5252             if (color != mCurHintTextColor) {
5253                 mCurHintTextColor = color;
5254                 if (mText.length() == 0) {
5255                     inval = true;
5256                 }
5257             }
5258         }
5259         if (inval) {
5260             // Text needs to be redrawn with the new color
5261             if (mEditor != null) mEditor.invalidateTextDisplayList();
5262             invalidate();
5263         }
5264     }
5265
5266     @Override
5267     protected void drawableStateChanged() {
5268         super.drawableStateChanged();
5269
5270         if (mTextColor != null && mTextColor.isStateful()
5271                 || (mHintTextColor != null && mHintTextColor.isStateful())
5272                 || (mLinkTextColor != null && mLinkTextColor.isStateful())) {
5273             updateTextColors();
5274         }
5275
5276         if (mDrawables != null) {
5277             final int[] state = getDrawableState();
5278             for (Drawable dr : mDrawables.mShowing) {
5279                 if (dr != null && dr.isStateful() && dr.setState(state)) {
5280                     invalidateDrawable(dr);
5281                 }
5282             }
5283         }
5284     }
5285
5286     @Override
5287     public void drawableHotspotChanged(float x, float y) {
5288         super.drawableHotspotChanged(x, y);
5289
5290         if (mDrawables != null) {
5291             for (Drawable dr : mDrawables.mShowing) {
5292                 if (dr != null) {
5293                     dr.setHotspot(x, y);
5294                 }
5295             }
5296         }
5297     }
5298
5299     @Override
5300     public Parcelable onSaveInstanceState() {
5301         Parcelable superState = super.onSaveInstanceState();
5302
5303         // Save state if we are forced to
5304         final boolean freezesText = getFreezesText();
5305         boolean hasSelection = false;
5306         int start = -1;
5307         int end = -1;
5308
5309         if (mText != null) {
5310             start = getSelectionStart();
5311             end = getSelectionEnd();
5312             if (start >= 0 || end >= 0) {
5313                 // Or save state if there is a selection
5314                 hasSelection = true;
5315             }
5316         }
5317
5318         if (freezesText || hasSelection) {
5319             SavedState ss = new SavedState(superState);
5320
5321             if (freezesText) {
5322                 if (mText instanceof Spanned) {
5323                     final Spannable sp = new SpannableStringBuilder(mText);
5324
5325                     if (mEditor != null) {
5326                         removeMisspelledSpans(sp);
5327                         sp.removeSpan(mEditor.mSuggestionRangeSpan);
5328                     }
5329
5330                     ss.text = sp;
5331                 } else {
5332                     ss.text = mText.toString();
5333                 }
5334             }
5335
5336             if (hasSelection) {
5337                 // XXX Should also save the current scroll position!
5338                 ss.selStart = start;
5339                 ss.selEnd = end;
5340             }
5341
5342             if (isFocused() && start >= 0 && end >= 0) {
5343                 ss.frozenWithFocus = true;
5344             }
5345
5346             ss.error = getError();
5347
5348             if (mEditor != null) {
5349                 ss.editorState = mEditor.saveInstanceState();
5350             }
5351             return ss;
5352         }
5353
5354         return superState;
5355     }
5356
5357     void removeMisspelledSpans(Spannable spannable) {
5358         SuggestionSpan[] suggestionSpans = spannable.getSpans(0, spannable.length(),
5359                 SuggestionSpan.class);
5360         for (int i = 0; i < suggestionSpans.length; i++) {
5361             int flags = suggestionSpans[i].getFlags();
5362             if ((flags & SuggestionSpan.FLAG_EASY_CORRECT) != 0
5363                     && (flags & SuggestionSpan.FLAG_MISSPELLED) != 0) {
5364                 spannable.removeSpan(suggestionSpans[i]);
5365             }
5366         }
5367     }
5368
5369     @Override
5370     public void onRestoreInstanceState(Parcelable state) {
5371         if (!(state instanceof SavedState)) {
5372             super.onRestoreInstanceState(state);
5373             return;
5374         }
5375
5376         SavedState ss = (SavedState) state;
5377         super.onRestoreInstanceState(ss.getSuperState());
5378
5379         // XXX restore buffer type too, as well as lots of other stuff
5380         if (ss.text != null) {
5381             setText(ss.text);
5382         }
5383
5384         if (ss.selStart >= 0 && ss.selEnd >= 0) {
5385             if (mText instanceof Spannable) {
5386                 int len = mText.length();
5387
5388                 if (ss.selStart > len || ss.selEnd > len) {
5389                     String restored = "";
5390
5391                     if (ss.text != null) {
5392                         restored = "(restored) ";
5393                     }
5394
5395                     Log.e(LOG_TAG, "Saved cursor position " + ss.selStart + "/" + ss.selEnd
5396                             + " out of range for " + restored + "text " + mText);
5397                 } else {
5398                     Selection.setSelection((Spannable) mText, ss.selStart, ss.selEnd);
5399
5400                     if (ss.frozenWithFocus) {
5401                         createEditorIfNeeded();
5402                         mEditor.mFrozenWithFocus = true;
5403                     }
5404                 }
5405             }
5406         }
5407
5408         if (ss.error != null) {
5409             final CharSequence error = ss.error;
5410             // Display the error later, after the first layout pass
5411             post(new Runnable() {
5412                 public void run() {
5413                     if (mEditor == null || !mEditor.mErrorWasChanged) {
5414                         setError(error);
5415                     }
5416                 }
5417             });
5418         }
5419
5420         if (ss.editorState != null) {
5421             createEditorIfNeeded();
5422             mEditor.restoreInstanceState(ss.editorState);
5423         }
5424     }
5425
5426     /**
5427      * Control whether this text view saves its entire text contents when
5428      * freezing to an icicle, in addition to dynamic state such as cursor
5429      * position.  By default this is false, not saving the text.  Set to true
5430      * if the text in the text view is not being saved somewhere else in
5431      * persistent storage (such as in a content provider) so that if the
5432      * view is later thawed the user will not lose their data. For
5433      * {@link android.widget.EditText} it is always enabled, regardless of
5434      * the value of the attribute.
5435      *
5436      * @param freezesText Controls whether a frozen icicle should include the
5437      * entire text data: true to include it, false to not.
5438      *
5439      * @attr ref android.R.styleable#TextView_freezesText
5440      */
5441     @android.view.RemotableViewMethod
5442     public void setFreezesText(boolean freezesText) {
5443         mFreezesText = freezesText;
5444     }
5445
5446     /**
5447      * Return whether this text view is including its entire text contents
5448      * in frozen icicles. For {@link android.widget.EditText} it always returns true.
5449      *
5450      * @return Returns true if text is included, false if it isn't.
5451      *
5452      * @see #setFreezesText
5453      */
5454     public boolean getFreezesText() {
5455         return mFreezesText;
5456     }
5457
5458     ///////////////////////////////////////////////////////////////////////////
5459
5460     /**
5461      * Sets the Factory used to create new {@link Editable Editables}.
5462      *
5463      * @param factory {@link android.text.Editable.Factory Editable.Factory} to be used
5464      *
5465      * @see android.text.Editable.Factory
5466      * @see android.widget.TextView.BufferType#EDITABLE
5467      */
5468     public final void setEditableFactory(Editable.Factory factory) {
5469         mEditableFactory = factory;
5470         setText(mText);
5471     }
5472
5473     /**
5474      * Sets the Factory used to create new {@link Spannable Spannables}.
5475      *
5476      * @param factory {@link android.text.Spannable.Factory Spannable.Factory} to be used
5477      *
5478      * @see android.text.Spannable.Factory
5479      * @see android.widget.TextView.BufferType#SPANNABLE
5480      */
5481     public final void setSpannableFactory(Spannable.Factory factory) {
5482         mSpannableFactory = factory;
5483         setText(mText);
5484     }
5485
5486     /**
5487      * Sets the text to be displayed. TextView <em>does not</em> accept
5488      * HTML-like formatting, which you can do with text strings in XML resource files.
5489      * To style your strings, attach android.text.style.* objects to a
5490      * {@link android.text.SpannableString}, or see the
5491      * <a href="{@docRoot}guide/topics/resources/available-resources.html#stringresources">
5492      * Available Resource Types</a> documentation for an example of setting
5493      * formatted text in the XML resource file.
5494      * <p/>
5495      * When required, TextView will use {@link android.text.Spannable.Factory} to create final or
5496      * intermediate {@link Spannable Spannables}. Likewise it will use
5497      * {@link android.text.Editable.Factory} to create final or intermediate
5498      * {@link Editable Editables}.
5499      *
5500      * @param text text to be displayed
5501      *
5502      * @attr ref android.R.styleable#TextView_text
5503      */
5504     @android.view.RemotableViewMethod
5505     public final void setText(CharSequence text) {
5506         setText(text, mBufferType);
5507     }
5508
5509     /**
5510      * Sets the text to be displayed but retains the cursor position. Same as
5511      * {@link #setText(CharSequence)} except that the cursor position (if any) is retained in the
5512      * new text.
5513      * <p/>
5514      * When required, TextView will use {@link android.text.Spannable.Factory} to create final or
5515      * intermediate {@link Spannable Spannables}. Likewise it will use
5516      * {@link android.text.Editable.Factory} to create final or intermediate
5517      * {@link Editable Editables}.
5518      *
5519      * @param text text to be displayed
5520      *
5521      * @see #setText(CharSequence)
5522      */
5523     @android.view.RemotableViewMethod
5524     public final void setTextKeepState(CharSequence text) {
5525         setTextKeepState(text, mBufferType);
5526     }
5527
5528     /**
5529      * Sets the text to be displayed and the {@link android.widget.TextView.BufferType}.
5530      * <p/>
5531      * When required, TextView will use {@link android.text.Spannable.Factory} to create final or
5532      * intermediate {@link Spannable Spannables}. Likewise it will use
5533      * {@link android.text.Editable.Factory} to create final or intermediate
5534      * {@link Editable Editables}.
5535      *
5536      * @param text text to be displayed
5537      * @param type a {@link android.widget.TextView.BufferType} which defines whether the text is
5538      *              stored as a static text, styleable/spannable text, or editable text
5539      *
5540      * @see #setText(CharSequence)
5541      * @see android.widget.TextView.BufferType
5542      * @see #setSpannableFactory(Spannable.Factory)
5543      * @see #setEditableFactory(Editable.Factory)
5544      *
5545      * @attr ref android.R.styleable#TextView_text
5546      * @attr ref android.R.styleable#TextView_bufferType
5547      */
5548     public void setText(CharSequence text, BufferType type) {
5549         setText(text, type, true, 0);
5550
5551         if (mCharWrapper != null) {
5552             mCharWrapper.mChars = null;
5553         }
5554     }
5555
5556     private void setText(CharSequence text, BufferType type,
5557                          boolean notifyBefore, int oldlen) {
5558         mTextSetFromXmlOrResourceId = false;
5559         if (text == null) {
5560             text = "";
5561         }
5562
5563         // If suggestions are not enabled, remove the suggestion spans from the text
5564         if (!isSuggestionsEnabled()) {
5565             text = removeSuggestionSpans(text);
5566         }
5567
5568         if (!mUserSetTextScaleX) mTextPaint.setTextScaleX(1.0f);
5569
5570         if (text instanceof Spanned
5571                 && ((Spanned) text).getSpanStart(TextUtils.TruncateAt.MARQUEE) >= 0) {
5572             if (ViewConfiguration.get(mContext).isFadingMarqueeEnabled()) {
5573                 setHorizontalFadingEdgeEnabled(true);
5574                 mMarqueeFadeMode = MARQUEE_FADE_NORMAL;
5575             } else {
5576                 setHorizontalFadingEdgeEnabled(false);
5577                 mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS;
5578             }
5579             setEllipsize(TextUtils.TruncateAt.MARQUEE);
5580         }
5581
5582         int n = mFilters.length;
5583         for (int i = 0; i < n; i++) {
5584             CharSequence out = mFilters[i].filter(text, 0, text.length(), EMPTY_SPANNED, 0, 0);
5585             if (out != null) {
5586                 text = out;
5587             }
5588         }
5589
5590         if (notifyBefore) {
5591             if (mText != null) {
5592                 oldlen = mText.length();
5593                 sendBeforeTextChanged(mText, 0, oldlen, text.length());
5594             } else {
5595                 sendBeforeTextChanged("", 0, 0, text.length());
5596             }
5597         }
5598
5599         boolean needEditableForNotification = false;
5600
5601         if (mListeners != null && mListeners.size() != 0) {
5602             needEditableForNotification = true;
5603         }
5604
5605         if (type == BufferType.EDITABLE || getKeyListener() != null
5606                 || needEditableForNotification) {
5607             createEditorIfNeeded();
5608             mEditor.forgetUndoRedo();
5609             Editable t = mEditableFactory.newEditable(text);
5610             text = t;
5611             setFilters(t, mFilters);
5612             InputMethodManager imm = InputMethodManager.peekInstance();
5613             if (imm != null) imm.restartInput(this);
5614         } else if (type == BufferType.SPANNABLE || mMovement != null) {
5615             text = mSpannableFactory.newSpannable(text);
5616         } else if (!(text instanceof PrecomputedText || text instanceof CharWrapper)) {
5617             text = TextUtils.stringOrSpannedString(text);
5618         }
5619
5620         if (mAutoLinkMask != 0) {
5621             Spannable s2;
5622
5623             if (type == BufferType.EDITABLE || text instanceof Spannable) {
5624                 s2 = (Spannable) text;
5625             } else {
5626                 s2 = mSpannableFactory.newSpannable(text);
5627             }
5628
5629             if (Linkify.addLinks(s2, mAutoLinkMask)) {
5630                 text = s2;
5631                 type = (type == BufferType.EDITABLE) ? BufferType.EDITABLE : BufferType.SPANNABLE;
5632
5633                 /*
5634                  * We must go ahead and set the text before changing the
5635                  * movement method, because setMovementMethod() may call
5636                  * setText() again to try to upgrade the buffer type.
5637                  */
5638                 mText = text;
5639
5640                 // Do not change the movement method for text that support text selection as it
5641                 // would prevent an arbitrary cursor displacement.
5642                 if (mLinksClickable && !textCanBeSelected()) {
5643                     setMovementMethod(LinkMovementMethod.getInstance());
5644                 }
5645             }
5646         }
5647
5648         mBufferType = type;
5649         mText = text;
5650
5651         if (mTransformation == null) {
5652             mTransformed = text;
5653         } else {
5654             mTransformed = mTransformation.getTransformation(text, this);
5655         }
5656
5657         final int textLength = text.length();
5658
5659         if (text instanceof Spannable && !mAllowTransformationLengthChange) {
5660             Spannable sp = (Spannable) text;
5661
5662             // Remove any ChangeWatchers that might have come from other TextViews.
5663             final ChangeWatcher[] watchers = sp.getSpans(0, sp.length(), ChangeWatcher.class);
5664             final int count = watchers.length;
5665             for (int i = 0; i < count; i++) {
5666                 sp.removeSpan(watchers[i]);
5667             }
5668
5669             if (mChangeWatcher == null) mChangeWatcher = new ChangeWatcher();
5670
5671             sp.setSpan(mChangeWatcher, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE
5672                     | (CHANGE_WATCHER_PRIORITY << Spanned.SPAN_PRIORITY_SHIFT));
5673
5674             if (mEditor != null) mEditor.addSpanWatchers(sp);
5675
5676             if (mTransformation != null) {
5677                 sp.setSpan(mTransformation, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
5678             }
5679
5680             if (mMovement != null) {
5681                 mMovement.initialize(this, (Spannable) text);
5682
5683                 /*
5684                  * Initializing the movement method will have set the
5685                  * selection, so reset mSelectionMoved to keep that from
5686                  * interfering with the normal on-focus selection-setting.
5687                  */
5688                 if (mEditor != null) mEditor.mSelectionMoved = false;
5689             }
5690         }
5691
5692         if (mLayout != null) {
5693             checkForRelayout();
5694         }
5695
5696         sendOnTextChanged(text, 0, oldlen, textLength);
5697         onTextChanged(text, 0, oldlen, textLength);
5698
5699         notifyViewAccessibilityStateChangedIfNeeded(AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
5700
5701         if (needEditableForNotification) {
5702             sendAfterTextChanged((Editable) text);
5703         } else {
5704             notifyAutoFillManagerAfterTextChangedIfNeeded();
5705         }
5706
5707         // SelectionModifierCursorController depends on textCanBeSelected, which depends on text
5708         if (mEditor != null) mEditor.prepareCursorControllers();
5709     }
5710
5711     /**
5712      * Sets the TextView to display the specified slice of the specified
5713      * char array. You must promise that you will not change the contents
5714      * of the array except for right before another call to setText(),
5715      * since the TextView has no way to know that the text
5716      * has changed and that it needs to invalidate and re-layout.
5717      *
5718      * @param text char array to be displayed
5719      * @param start start index in the char array
5720      * @param len length of char count after {@code start}
5721      */
5722     public final void setText(char[] text, int start, int len) {
5723         int oldlen = 0;
5724
5725         if (start < 0 || len < 0 || start + len > text.length) {
5726             throw new IndexOutOfBoundsException(start + ", " + len);
5727         }
5728
5729         /*
5730          * We must do the before-notification here ourselves because if
5731          * the old text is a CharWrapper we destroy it before calling
5732          * into the normal path.
5733          */
5734         if (mText != null) {
5735             oldlen = mText.length();
5736             sendBeforeTextChanged(mText, 0, oldlen, len);
5737         } else {
5738             sendBeforeTextChanged("", 0, 0, len);
5739         }
5740
5741         if (mCharWrapper == null) {
5742             mCharWrapper = new CharWrapper(text, start, len);
5743         } else {
5744             mCharWrapper.set(text, start, len);
5745         }
5746
5747         setText(mCharWrapper, mBufferType, false, oldlen);
5748     }
5749
5750     /**
5751      * Sets the text to be displayed and the {@link android.widget.TextView.BufferType} but retains
5752      * the cursor position. Same as
5753      * {@link #setText(CharSequence, android.widget.TextView.BufferType)} except that the cursor
5754      * position (if any) is retained in the new text.
5755      * <p/>
5756      * When required, TextView will use {@link android.text.Spannable.Factory} to create final or
5757      * intermediate {@link Spannable Spannables}. Likewise it will use
5758      * {@link android.text.Editable.Factory} to create final or intermediate
5759      * {@link Editable Editables}.
5760      *
5761      * @param text text to be displayed
5762      * @param type a {@link android.widget.TextView.BufferType} which defines whether the text is
5763      *              stored as a static text, styleable/spannable text, or editable text
5764      *
5765      * @see #setText(CharSequence, android.widget.TextView.BufferType)
5766      */
5767     public final void setTextKeepState(CharSequence text, BufferType type) {
5768         int start = getSelectionStart();
5769         int end = getSelectionEnd();
5770         int len = text.length();
5771
5772         setText(text, type);
5773
5774         if (start >= 0 || end >= 0) {
5775             if (mText instanceof Spannable) {
5776                 Selection.setSelection((Spannable) mText,
5777                                        Math.max(0, Math.min(start, len)),
5778                                        Math.max(0, Math.min(end, len)));
5779             }
5780         }
5781     }
5782
5783     /**
5784      * Sets the text to be displayed using a string resource identifier.
5785      *
5786      * @param resid the resource identifier of the string resource to be displayed
5787      *
5788      * @see #setText(CharSequence)
5789      *
5790      * @attr ref android.R.styleable#TextView_text
5791      */
5792     @android.view.RemotableViewMethod
5793     public final void setText(@StringRes int resid) {
5794         setText(getContext().getResources().getText(resid));
5795         mTextSetFromXmlOrResourceId = true;
5796         mTextId = resid;
5797     }
5798
5799     /**
5800      * Sets the text to be displayed using a string resource identifier and the
5801      * {@link android.widget.TextView.BufferType}.
5802      * <p/>
5803      * When required, TextView will use {@link android.text.Spannable.Factory} to create final or
5804      * intermediate {@link Spannable Spannables}. Likewise it will use
5805      * {@link android.text.Editable.Factory} to create final or intermediate
5806      * {@link Editable Editables}.
5807      *
5808      * @param resid the resource identifier of the string resource to be displayed
5809      * @param type a {@link android.widget.TextView.BufferType} which defines whether the text is
5810      *              stored as a static text, styleable/spannable text, or editable text
5811      *
5812      * @see #setText(int)
5813      * @see #setText(CharSequence)
5814      * @see android.widget.TextView.BufferType
5815      * @see #setSpannableFactory(Spannable.Factory)
5816      * @see #setEditableFactory(Editable.Factory)
5817      *
5818      * @attr ref android.R.styleable#TextView_text
5819      * @attr ref android.R.styleable#TextView_bufferType
5820      */
5821     public final void setText(@StringRes int resid, BufferType type) {
5822         setText(getContext().getResources().getText(resid), type);
5823         mTextSetFromXmlOrResourceId = true;
5824         mTextId = resid;
5825     }
5826
5827     /**
5828      * Sets the text to be displayed when the text of the TextView is empty.
5829      * Null means to use the normal empty text. The hint does not currently
5830      * participate in determining the size of the view.
5831      *
5832      * @attr ref android.R.styleable#TextView_hint
5833      */
5834     @android.view.RemotableViewMethod
5835     public final void setHint(CharSequence hint) {
5836         setHintInternal(hint);
5837
5838         if (mEditor != null && isInputMethodTarget()) {
5839             mEditor.reportExtractedText();
5840         }
5841     }
5842
5843     private void setHintInternal(CharSequence hint) {
5844         mHint = TextUtils.stringOrSpannedString(hint);
5845
5846         if (mLayout != null) {
5847             checkForRelayout();
5848         }
5849
5850         if (mText.length() == 0) {
5851             invalidate();
5852         }
5853
5854         // Invalidate display list if hint is currently used
5855         if (mEditor != null && mText.length() == 0 && mHint != null) {
5856             mEditor.invalidateTextDisplayList();
5857         }
5858     }
5859
5860     /**
5861      * Sets the text to be displayed when the text of the TextView is empty,
5862      * from a resource.
5863      *
5864      * @attr ref android.R.styleable#TextView_hint
5865      */
5866     @android.view.RemotableViewMethod
5867     public final void setHint(@StringRes int resid) {
5868         setHint(getContext().getResources().getText(resid));
5869     }
5870
5871     /**
5872      * Returns the hint that is displayed when the text of the TextView
5873      * is empty.
5874      *
5875      * @attr ref android.R.styleable#TextView_hint
5876      */
5877     @ViewDebug.CapturedViewProperty
5878     public CharSequence getHint() {
5879         return mHint;
5880     }
5881
5882     boolean isSingleLine() {
5883         return mSingleLine;
5884     }
5885
5886     private static boolean isMultilineInputType(int type) {
5887         return (type & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE))
5888                 == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE);
5889     }
5890
5891     /**
5892      * Removes the suggestion spans.
5893      */
5894     CharSequence removeSuggestionSpans(CharSequence text) {
5895         if (text instanceof Spanned) {
5896             Spannable spannable;
5897             if (text instanceof Spannable) {
5898                 spannable = (Spannable) text;
5899             } else {
5900                 spannable = mSpannableFactory.newSpannable(text);
5901             }
5902
5903             SuggestionSpan[] spans = spannable.getSpans(0, text.length(), SuggestionSpan.class);
5904             if (spans.length == 0) {
5905                 return text;
5906             } else {
5907                 text = spannable;
5908             }
5909
5910             for (int i = 0; i < spans.length; i++) {
5911                 spannable.removeSpan(spans[i]);
5912             }
5913         }
5914         return text;
5915     }
5916
5917     /**
5918      * Set the type of the content with a constant as defined for {@link EditorInfo#inputType}. This
5919      * will take care of changing the key listener, by calling {@link #setKeyListener(KeyListener)},
5920      * to match the given content type.  If the given content type is {@link EditorInfo#TYPE_NULL}
5921      * then a soft keyboard will not be displayed for this text view.
5922      *
5923      * Note that the maximum number of displayed lines (see {@link #setMaxLines(int)}) will be
5924      * modified if you change the {@link EditorInfo#TYPE_TEXT_FLAG_MULTI_LINE} flag of the input
5925      * type.
5926      *
5927      * @see #getInputType()
5928      * @see #setRawInputType(int)
5929      * @see android.text.InputType
5930      * @attr ref android.R.styleable#TextView_inputType
5931      */
5932     public void setInputType(int type) {
5933         final boolean wasPassword = isPasswordInputType(getInputType());
5934         final boolean wasVisiblePassword = isVisiblePasswordInputType(getInputType());
5935         setInputType(type, false);
5936         final boolean isPassword = isPasswordInputType(type);
5937         final boolean isVisiblePassword = isVisiblePasswordInputType(type);
5938         boolean forceUpdate = false;
5939         if (isPassword) {
5940             setTransformationMethod(PasswordTransformationMethod.getInstance());
5941             setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE, 0);
5942         } else if (isVisiblePassword) {
5943             if (mTransformation == PasswordTransformationMethod.getInstance()) {
5944                 forceUpdate = true;
5945             }
5946             setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, MONOSPACE, 0);
5947         } else if (wasPassword || wasVisiblePassword) {
5948             // not in password mode, clean up typeface and transformation
5949             setTypefaceFromAttrs(null/* fontTypeface */, null /* fontFamily */, -1, -1);
5950             if (mTransformation == PasswordTransformationMethod.getInstance()) {
5951                 forceUpdate = true;
5952             }
5953         }
5954
5955         boolean singleLine = !isMultilineInputType(type);
5956
5957         // We need to update the single line mode if it has changed or we
5958         // were previously in password mode.
5959         if (mSingleLine != singleLine || forceUpdate) {
5960             // Change single line mode, but only change the transformation if
5961             // we are not in password mode.
5962             applySingleLine(singleLine, !isPassword, true);
5963         }
5964
5965         if (!isSuggestionsEnabled()) {
5966             mText = removeSuggestionSpans(mText);
5967         }
5968
5969         InputMethodManager imm = InputMethodManager.peekInstance();
5970         if (imm != null) imm.restartInput(this);
5971     }
5972
5973     /**
5974      * It would be better to rely on the input type for everything. A password inputType should have
5975      * a password transformation. We should hence use isPasswordInputType instead of this method.
5976      *
5977      * We should:
5978      * - Call setInputType in setKeyListener instead of changing the input type directly (which
5979      * would install the correct transformation).
5980      * - Refuse the installation of a non-password transformation in setTransformation if the input
5981      * type is password.
5982      *
5983      * However, this is like this for legacy reasons and we cannot break existing apps. This method
5984      * is useful since it matches what the user can see (obfuscated text or not).
5985      *
5986      * @return true if the current transformation method is of the password type.
5987      */
5988     boolean hasPasswordTransformationMethod() {
5989         return mTransformation instanceof PasswordTransformationMethod;
5990     }
5991
5992     static boolean isPasswordInputType(int inputType) {
5993         final int variation =
5994                 inputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION);
5995         return variation
5996                 == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD)
5997                 || variation
5998                 == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD)
5999                 || variation
6000                 == (EditorInfo.TYPE_CLASS_NUMBER | EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD);
6001     }
6002
6003     private static boolean isVisiblePasswordInputType(int inputType) {
6004         final int variation =
6005                 inputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION);
6006         return variation
6007                 == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
6008     }
6009
6010     /**
6011      * Directly change the content type integer of the text view, without
6012      * modifying any other state.
6013      * @see #setInputType(int)
6014      * @see android.text.InputType
6015      * @attr ref android.R.styleable#TextView_inputType
6016      */
6017     public void setRawInputType(int type) {
6018         if (type == InputType.TYPE_NULL && mEditor == null) return; //TYPE_NULL is the default value
6019         createEditorIfNeeded();
6020         mEditor.mInputType = type;
6021     }
6022
6023     /**
6024      * @return {@code null} if the key listener should use pre-O (locale-independent). Otherwise
6025      *         a {@code Locale} object that can be used to customize key various listeners.
6026      * @see DateKeyListener#getInstance(Locale)
6027      * @see DateTimeKeyListener#getInstance(Locale)
6028      * @see DigitsKeyListener#getInstance(Locale)
6029      * @see TimeKeyListener#getInstance(Locale)
6030      */
6031     @Nullable
6032     private Locale getCustomLocaleForKeyListenerOrNull() {
6033         if (!mUseInternationalizedInput) {
6034             // If the application does not target O, stick to the previous behavior.
6035             return null;
6036         }
6037         final LocaleList locales = getImeHintLocales();
6038         if (locales == null) {
6039             // If the application does not explicitly specify IME hint locale, also stick to the
6040             // previous behavior.
6041             return null;
6042         }
6043         return locales.get(0);
6044     }
6045
6046     private void setInputType(int type, boolean direct) {
6047         final int cls = type & EditorInfo.TYPE_MASK_CLASS;
6048         KeyListener input;
6049         if (cls == EditorInfo.TYPE_CLASS_TEXT) {
6050             boolean autotext = (type & EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT) != 0;
6051             TextKeyListener.Capitalize cap;
6052             if ((type & EditorInfo.TYPE_TEXT_FLAG_CAP_CHARACTERS) != 0) {
6053                 cap = TextKeyListener.Capitalize.CHARACTERS;
6054             } else if ((type & EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS) != 0) {
6055                 cap = TextKeyListener.Capitalize.WORDS;
6056             } else if ((type & EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES) != 0) {
6057                 cap = TextKeyListener.Capitalize.SENTENCES;
6058             } else {
6059                 cap = TextKeyListener.Capitalize.NONE;
6060             }
6061             input = TextKeyListener.getInstance(autotext, cap);
6062         } else if (cls == EditorInfo.TYPE_CLASS_NUMBER) {
6063             final Locale locale = getCustomLocaleForKeyListenerOrNull();
6064             input = DigitsKeyListener.getInstance(
6065                     locale,
6066                     (type & EditorInfo.TYPE_NUMBER_FLAG_SIGNED) != 0,
6067                     (type & EditorInfo.TYPE_NUMBER_FLAG_DECIMAL) != 0);
6068             if (locale != null) {
6069                 // Override type, if necessary for i18n.
6070                 int newType = input.getInputType();
6071                 final int newClass = newType & EditorInfo.TYPE_MASK_CLASS;
6072                 if (newClass != EditorInfo.TYPE_CLASS_NUMBER) {
6073                     // The class is different from the original class. So we need to override
6074                     // 'type'. But we want to keep the password flag if it's there.
6075                     if ((type & EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD) != 0) {
6076                         newType |= EditorInfo.TYPE_TEXT_VARIATION_PASSWORD;
6077                     }
6078                     type = newType;
6079                 }
6080             }
6081         } else if (cls == EditorInfo.TYPE_CLASS_DATETIME) {
6082             final Locale locale = getCustomLocaleForKeyListenerOrNull();
6083             switch (type & EditorInfo.TYPE_MASK_VARIATION) {
6084                 case EditorInfo.TYPE_DATETIME_VARIATION_DATE:
6085                     input = DateKeyListener.getInstance(locale);
6086                     break;
6087                 case EditorInfo.TYPE_DATETIME_VARIATION_TIME:
6088                     input = TimeKeyListener.getInstance(locale);
6089                     break;
6090                 default:
6091                     input = DateTimeKeyListener.getInstance(locale);
6092                     break;
6093             }
6094             if (mUseInternationalizedInput) {
6095                 type = input.getInputType(); // Override type, if necessary for i18n.
6096             }
6097         } else if (cls == EditorInfo.TYPE_CLASS_PHONE) {
6098             input = DialerKeyListener.getInstance();
6099         } else {
6100             input = TextKeyListener.getInstance();
6101         }
6102         setRawInputType(type);
6103         mListenerChanged = false;
6104         if (direct) {
6105             createEditorIfNeeded();
6106             mEditor.mKeyListener = input;
6107         } else {
6108             setKeyListenerOnly(input);
6109         }
6110     }
6111
6112     /**
6113      * Get the type of the editable content.
6114      *
6115      * @see #setInputType(int)
6116      * @see android.text.InputType
6117      */
6118     public int getInputType() {
6119         return mEditor == null ? EditorInfo.TYPE_NULL : mEditor.mInputType;
6120     }
6121
6122     /**
6123      * Change the editor type integer associated with the text view, which
6124      * is reported to an Input Method Editor (IME) with {@link EditorInfo#imeOptions}
6125      * when it has focus.
6126      * @see #getImeOptions
6127      * @see android.view.inputmethod.EditorInfo
6128      * @attr ref android.R.styleable#TextView_imeOptions
6129      */
6130     public void setImeOptions(int imeOptions) {
6131         createEditorIfNeeded();
6132         mEditor.createInputContentTypeIfNeeded();
6133         mEditor.mInputContentType.imeOptions = imeOptions;
6134     }
6135
6136     /**
6137      * Get the type of the Input Method Editor (IME).
6138      * @return the type of the IME
6139      * @see #setImeOptions(int)
6140      * @see android.view.inputmethod.EditorInfo
6141      */
6142     public int getImeOptions() {
6143         return mEditor != null && mEditor.mInputContentType != null
6144                 ? mEditor.mInputContentType.imeOptions : EditorInfo.IME_NULL;
6145     }
6146
6147     /**
6148      * Change the custom IME action associated with the text view, which
6149      * will be reported to an IME with {@link EditorInfo#actionLabel}
6150      * and {@link EditorInfo#actionId} when it has focus.
6151      * @see #getImeActionLabel
6152      * @see #getImeActionId
6153      * @see android.view.inputmethod.EditorInfo
6154      * @attr ref android.R.styleable#TextView_imeActionLabel
6155      * @attr ref android.R.styleable#TextView_imeActionId
6156      */
6157     public void setImeActionLabel(CharSequence label, int actionId) {
6158         createEditorIfNeeded();
6159         mEditor.createInputContentTypeIfNeeded();
6160         mEditor.mInputContentType.imeActionLabel = label;
6161         mEditor.mInputContentType.imeActionId = actionId;
6162     }
6163
6164     /**
6165      * Get the IME action label previous set with {@link #setImeActionLabel}.
6166      *
6167      * @see #setImeActionLabel
6168      * @see android.view.inputmethod.EditorInfo
6169      */
6170     public CharSequence getImeActionLabel() {
6171         return mEditor != null && mEditor.mInputContentType != null
6172                 ? mEditor.mInputContentType.imeActionLabel : null;
6173     }
6174
6175     /**
6176      * Get the IME action ID previous set with {@link #setImeActionLabel}.
6177      *
6178      * @see #setImeActionLabel
6179      * @see android.view.inputmethod.EditorInfo
6180      */
6181     public int getImeActionId() {
6182         return mEditor != null && mEditor.mInputContentType != null
6183                 ? mEditor.mInputContentType.imeActionId : 0;
6184     }
6185
6186     /**
6187      * Set a special listener to be called when an action is performed
6188      * on the text view.  This will be called when the enter key is pressed,
6189      * or when an action supplied to the IME is selected by the user.  Setting
6190      * this means that the normal hard key event will not insert a newline
6191      * into the text view, even if it is multi-line; holding down the ALT
6192      * modifier will, however, allow the user to insert a newline character.
6193      */
6194     public void setOnEditorActionListener(OnEditorActionListener l) {
6195         createEditorIfNeeded();
6196         mEditor.createInputContentTypeIfNeeded();
6197         mEditor.mInputContentType.onEditorActionListener = l;
6198     }
6199
6200     /**
6201      * Called when an attached input method calls
6202      * {@link InputConnection#performEditorAction(int)
6203      * InputConnection.performEditorAction()}
6204      * for this text view.  The default implementation will call your action
6205      * listener supplied to {@link #setOnEditorActionListener}, or perform
6206      * a standard operation for {@link EditorInfo#IME_ACTION_NEXT
6207      * EditorInfo.IME_ACTION_NEXT}, {@link EditorInfo#IME_ACTION_PREVIOUS
6208      * EditorInfo.IME_ACTION_PREVIOUS}, or {@link EditorInfo#IME_ACTION_DONE
6209      * EditorInfo.IME_ACTION_DONE}.
6210      *
6211      * <p>For backwards compatibility, if no IME options have been set and the
6212      * text view would not normally advance focus on enter, then
6213      * the NEXT and DONE actions received here will be turned into an enter
6214      * key down/up pair to go through the normal key handling.
6215      *
6216      * @param actionCode The code of the action being performed.
6217      *
6218      * @see #setOnEditorActionListener
6219      */
6220     public void onEditorAction(int actionCode) {
6221         final Editor.InputContentType ict = mEditor == null ? null : mEditor.mInputContentType;
6222         if (ict != null) {
6223             if (ict.onEditorActionListener != null) {
6224                 if (ict.onEditorActionListener.onEditorAction(this,
6225                         actionCode, null)) {
6226                     return;
6227                 }
6228             }
6229
6230             // This is the handling for some default action.
6231             // Note that for backwards compatibility we don't do this
6232             // default handling if explicit ime options have not been given,
6233             // instead turning this into the normal enter key codes that an
6234             // app may be expecting.
6235             if (actionCode == EditorInfo.IME_ACTION_NEXT) {
6236                 View v = focusSearch(FOCUS_FORWARD);
6237                 if (v != null) {
6238                     if (!v.requestFocus(FOCUS_FORWARD)) {
6239                         throw new IllegalStateException("focus search returned a view "
6240                                 + "that wasn't able to take focus!");
6241                     }
6242                 }
6243                 return;
6244
6245             } else if (actionCode == EditorInfo.IME_ACTION_PREVIOUS) {
6246                 View v = focusSearch(FOCUS_BACKWARD);
6247                 if (v != null) {
6248                     if (!v.requestFocus(FOCUS_BACKWARD)) {
6249                         throw new IllegalStateException("focus search returned a view "
6250                                 + "that wasn't able to take focus!");
6251                     }
6252                 }
6253                 return;
6254
6255             } else if (actionCode == EditorInfo.IME_ACTION_DONE) {
6256                 InputMethodManager imm = InputMethodManager.peekInstance();
6257                 if (imm != null && imm.isActive(this)) {
6258                     imm.hideSoftInputFromWindow(getWindowToken(), 0);
6259                 }
6260                 return;
6261             }
6262         }
6263
6264         ViewRootImpl viewRootImpl = getViewRootImpl();
6265         if (viewRootImpl != null) {
6266             long eventTime = SystemClock.uptimeMillis();
6267             viewRootImpl.dispatchKeyFromIme(
6268                     new KeyEvent(eventTime, eventTime,
6269                     KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0,
6270                     KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
6271                     KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
6272                     | KeyEvent.FLAG_EDITOR_ACTION));
6273             viewRootImpl.dispatchKeyFromIme(
6274                     new KeyEvent(SystemClock.uptimeMillis(), eventTime,
6275                     KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0,
6276                     KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
6277                     KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
6278                     | KeyEvent.FLAG_EDITOR_ACTION));
6279         }
6280     }
6281
6282     /**
6283      * Set the private content type of the text, which is the
6284      * {@link EditorInfo#privateImeOptions EditorInfo.privateImeOptions}
6285      * field that will be filled in when creating an input connection.
6286      *
6287      * @see #getPrivateImeOptions()
6288      * @see EditorInfo#privateImeOptions
6289      * @attr ref android.R.styleable#TextView_privateImeOptions
6290      */
6291     public void setPrivateImeOptions(String type) {
6292         createEditorIfNeeded();
6293         mEditor.createInputContentTypeIfNeeded();
6294         mEditor.mInputContentType.privateImeOptions = type;
6295     }
6296
6297     /**
6298      * Get the private type of the content.
6299      *
6300      * @see #setPrivateImeOptions(String)
6301      * @see EditorInfo#privateImeOptions
6302      */
6303     public String getPrivateImeOptions() {
6304         return mEditor != null && mEditor.mInputContentType != null
6305                 ? mEditor.mInputContentType.privateImeOptions : null;
6306     }
6307
6308     /**
6309      * Set the extra input data of the text, which is the
6310      * {@link EditorInfo#extras TextBoxAttribute.extras}
6311      * Bundle that will be filled in when creating an input connection.  The
6312      * given integer is the resource identifier of an XML resource holding an
6313      * {@link android.R.styleable#InputExtras &lt;input-extras&gt;} XML tree.
6314      *
6315      * @see #getInputExtras(boolean)
6316      * @see EditorInfo#extras
6317      * @attr ref android.R.styleable#TextView_editorExtras
6318      */
6319     public void setInputExtras(@XmlRes int xmlResId) throws XmlPullParserException, IOException {
6320         createEditorIfNeeded();
6321         XmlResourceParser parser = getResources().getXml(xmlResId);
6322         mEditor.createInputContentTypeIfNeeded();
6323         mEditor.mInputContentType.extras = new Bundle();
6324         getResources().parseBundleExtras(parser, mEditor.mInputContentType.extras);
6325     }
6326
6327     /**
6328      * Retrieve the input extras currently associated with the text view, which
6329      * can be viewed as well as modified.
6330      *
6331      * @param create If true, the extras will be created if they don't already
6332      * exist.  Otherwise, null will be returned if none have been created.
6333      * @see #setInputExtras(int)
6334      * @see EditorInfo#extras
6335      * @attr ref android.R.styleable#TextView_editorExtras
6336      */
6337     public Bundle getInputExtras(boolean create) {
6338         if (mEditor == null && !create) return null;
6339         createEditorIfNeeded();
6340         if (mEditor.mInputContentType == null) {
6341             if (!create) return null;
6342             mEditor.createInputContentTypeIfNeeded();
6343         }
6344         if (mEditor.mInputContentType.extras == null) {
6345             if (!create) return null;
6346             mEditor.mInputContentType.extras = new Bundle();
6347         }
6348         return mEditor.mInputContentType.extras;
6349     }
6350
6351     /**
6352      * Change "hint" locales associated with the text view, which will be reported to an IME with
6353      * {@link EditorInfo#hintLocales} when it has focus.
6354      *
6355      * Starting with Android O, this also causes internationalized listeners to be created (or
6356      * change locale) based on the first locale in the input locale list.
6357      *
6358      * <p><strong>Note:</strong> If you want new "hint" to take effect immediately you need to
6359      * call {@link InputMethodManager#restartInput(View)}.</p>
6360      * @param hintLocales List of the languages that the user is supposed to switch to no matter
6361      * what input method subtype is currently used. Set {@code null} to clear the current "hint".
6362      * @see #getImeHintLocales()
6363      * @see android.view.inputmethod.EditorInfo#hintLocales
6364      */
6365     public void setImeHintLocales(@Nullable LocaleList hintLocales) {
6366         createEditorIfNeeded();
6367         mEditor.createInputContentTypeIfNeeded();
6368         mEditor.mInputContentType.imeHintLocales = hintLocales;
6369         if (mUseInternationalizedInput) {
6370             changeListenerLocaleTo(hintLocales == null ? null : hintLocales.get(0));
6371         }
6372     }
6373
6374     /**
6375      * @return The current languages list "hint". {@code null} when no "hint" is available.
6376      * @see #setImeHintLocales(LocaleList)
6377      * @see android.view.inputmethod.EditorInfo#hintLocales
6378      */
6379     @Nullable
6380     public LocaleList getImeHintLocales() {
6381         if (mEditor == null) {
6382             return null;
6383         }
6384         if (mEditor.mInputContentType == null) {
6385             return null;
6386         }
6387         return mEditor.mInputContentType.imeHintLocales;
6388     }
6389
6390     /**
6391      * Returns the error message that was set to be displayed with
6392      * {@link #setError}, or <code>null</code> if no error was set
6393      * or if it the error was cleared by the widget after user input.
6394      */
6395     public CharSequence getError() {
6396         return mEditor == null ? null : mEditor.mError;
6397     }
6398
6399     /**
6400      * Sets the right-hand compound drawable of the TextView to the "error"
6401      * icon and sets an error message that will be displayed in a popup when
6402      * the TextView has focus.  The icon and error message will be reset to
6403      * null when any key events cause changes to the TextView's text.  If the
6404      * <code>error</code> is <code>null</code>, the error message and icon
6405      * will be cleared.
6406      */
6407     @android.view.RemotableViewMethod
6408     public void setError(CharSequence error) {
6409         if (error == null) {
6410             setError(null, null);
6411         } else {
6412             Drawable dr = getContext().getDrawable(
6413                     com.android.internal.R.drawable.indicator_input_error);
6414
6415             dr.setBounds(0, 0, dr.getIntrinsicWidth(), dr.getIntrinsicHeight());
6416             setError(error, dr);
6417         }
6418     }
6419
6420     /**
6421      * Sets the right-hand compound drawable of the TextView to the specified
6422      * icon and sets an error message that will be displayed in a popup when
6423      * the TextView has focus.  The icon and error message will be reset to
6424      * null when any key events cause changes to the TextView's text.  The
6425      * drawable must already have had {@link Drawable#setBounds} set on it.
6426      * If the <code>error</code> is <code>null</code>, the error message will
6427      * be cleared (and you should provide a <code>null</code> icon as well).
6428      */
6429     public void setError(CharSequence error, Drawable icon) {
6430         createEditorIfNeeded();
6431         mEditor.setError(error, icon);
6432         notifyViewAccessibilityStateChangedIfNeeded(
6433                 AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
6434     }
6435
6436     @Override
6437     protected boolean setFrame(int l, int t, int r, int b) {
6438         boolean result = super.setFrame(l, t, r, b);
6439
6440         if (mEditor != null) mEditor.setFrame();
6441
6442         restartMarqueeIfNeeded();
6443
6444         return result;
6445     }
6446
6447     private void restartMarqueeIfNeeded() {
6448         if (mRestartMarquee && mEllipsize == TextUtils.TruncateAt.MARQUEE) {
6449             mRestartMarquee = false;
6450             startMarquee();
6451         }
6452     }
6453
6454     /**
6455      * Sets the list of input filters that will be used if the buffer is
6456      * Editable. Has no effect otherwise.
6457      *
6458      * @attr ref android.R.styleable#TextView_maxLength
6459      */
6460     public void setFilters(InputFilter[] filters) {
6461         if (filters == null) {
6462             throw new IllegalArgumentException();
6463         }
6464
6465         mFilters = filters;
6466
6467         if (mText instanceof Editable) {
6468             setFilters((Editable) mText, filters);
6469         }
6470     }
6471
6472     /**
6473      * Sets the list of input filters on the specified Editable,
6474      * and includes mInput in the list if it is an InputFilter.
6475      */
6476     private void setFilters(Editable e, InputFilter[] filters) {
6477         if (mEditor != null) {
6478             final boolean undoFilter = mEditor.mUndoInputFilter != null;
6479             final boolean keyFilter = mEditor.mKeyListener instanceof InputFilter;
6480             int num = 0;
6481             if (undoFilter) num++;
6482             if (keyFilter) num++;
6483             if (num > 0) {
6484                 InputFilter[] nf = new InputFilter[filters.length + num];
6485
6486                 System.arraycopy(filters, 0, nf, 0, filters.length);
6487                 num = 0;
6488                 if (undoFilter) {
6489                     nf[filters.length] = mEditor.mUndoInputFilter;
6490                     num++;
6491                 }
6492                 if (keyFilter) {
6493                     nf[filters.length + num] = (InputFilter) mEditor.mKeyListener;
6494                 }
6495
6496                 e.setFilters(nf);
6497                 return;
6498             }
6499         }
6500         e.setFilters(filters);
6501     }
6502
6503     /**
6504      * Returns the current list of input filters.
6505      *
6506      * @attr ref android.R.styleable#TextView_maxLength
6507      */
6508     public InputFilter[] getFilters() {
6509         return mFilters;
6510     }
6511
6512     /////////////////////////////////////////////////////////////////////////
6513
6514     private int getBoxHeight(Layout l) {
6515         Insets opticalInsets = isLayoutModeOptical(mParent) ? getOpticalInsets() : Insets.NONE;
6516         int padding = (l == mHintLayout)
6517                 ? getCompoundPaddingTop() + getCompoundPaddingBottom()
6518                 : getExtendedPaddingTop() + getExtendedPaddingBottom();
6519         return getMeasuredHeight() - padding + opticalInsets.top + opticalInsets.bottom;
6520     }
6521
6522     int getVerticalOffset(boolean forceNormal) {
6523         int voffset = 0;
6524         final int gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
6525
6526         Layout l = mLayout;
6527         if (!forceNormal && mText.length() == 0 && mHintLayout != null) {
6528             l = mHintLayout;
6529         }
6530
6531         if (gravity != Gravity.TOP) {
6532             int boxht = getBoxHeight(l);
6533             int textht = l.getHeight();
6534
6535             if (textht < boxht) {
6536                 if (gravity == Gravity.BOTTOM) {
6537                     voffset = boxht - textht;
6538                 } else { // (gravity == Gravity.CENTER_VERTICAL)
6539                     voffset = (boxht - textht) >> 1;
6540                 }
6541             }
6542         }
6543         return voffset;
6544     }
6545
6546     private int getBottomVerticalOffset(boolean forceNormal) {
6547         int voffset = 0;
6548         final int gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
6549
6550         Layout l = mLayout;
6551         if (!forceNormal && mText.length() == 0 && mHintLayout != null) {
6552             l = mHintLayout;
6553         }
6554
6555         if (gravity != Gravity.BOTTOM) {
6556             int boxht = getBoxHeight(l);
6557             int textht = l.getHeight();
6558
6559             if (textht < boxht) {
6560                 if (gravity == Gravity.TOP) {
6561                     voffset = boxht - textht;
6562                 } else { // (gravity == Gravity.CENTER_VERTICAL)
6563                     voffset = (boxht - textht) >> 1;
6564                 }
6565             }
6566         }
6567         return voffset;
6568     }
6569
6570     void invalidateCursorPath() {
6571         if (mHighlightPathBogus) {
6572             invalidateCursor();
6573         } else {
6574             final int horizontalPadding = getCompoundPaddingLeft();
6575             final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true);
6576
6577             if (mEditor.mDrawableForCursor == null) {
6578                 synchronized (TEMP_RECTF) {
6579                     /*
6580                      * The reason for this concern about the thickness of the
6581                      * cursor and doing the floor/ceil on the coordinates is that
6582                      * some EditTexts (notably textfields in the Browser) have
6583                      * anti-aliased text where not all the characters are
6584                      * necessarily at integer-multiple locations.  This should
6585                      * make sure the entire cursor gets invalidated instead of
6586                      * sometimes missing half a pixel.
6587                      */
6588                     float thick = (float) Math.ceil(mTextPaint.getStrokeWidth());
6589                     if (thick < 1.0f) {
6590                         thick = 1.0f;
6591                     }
6592
6593                     thick /= 2.0f;
6594
6595                     // mHighlightPath is guaranteed to be non null at that point.
6596                     mHighlightPath.computeBounds(TEMP_RECTF, false);
6597
6598                     invalidate((int) Math.floor(horizontalPadding + TEMP_RECTF.left - thick),
6599                             (int) Math.floor(verticalPadding + TEMP_RECTF.top - thick),
6600                             (int) Math.ceil(horizontalPadding + TEMP_RECTF.right + thick),
6601                             (int) Math.ceil(verticalPadding + TEMP_RECTF.bottom + thick));
6602                 }
6603             } else {
6604                 final Rect bounds = mEditor.mDrawableForCursor.getBounds();
6605                 invalidate(bounds.left + horizontalPadding, bounds.top + verticalPadding,
6606                         bounds.right + horizontalPadding, bounds.bottom + verticalPadding);
6607             }
6608         }
6609     }
6610
6611     void invalidateCursor() {
6612         int where = getSelectionEnd();
6613
6614         invalidateCursor(where, where, where);
6615     }
6616
6617     private void invalidateCursor(int a, int b, int c) {
6618         if (a >= 0 || b >= 0 || c >= 0) {
6619             int start = Math.min(Math.min(a, b), c);
6620             int end = Math.max(Math.max(a, b), c);
6621             invalidateRegion(start, end, true /* Also invalidates blinking cursor */);
6622         }
6623     }
6624
6625     /**
6626      * Invalidates the region of text enclosed between the start and end text offsets.
6627      */
6628     void invalidateRegion(int start, int end, boolean invalidateCursor) {
6629         if (mLayout == null) {
6630             invalidate();
6631         } else {
6632             int lineStart = mLayout.getLineForOffset(start);
6633             int top = mLayout.getLineTop(lineStart);
6634
6635             // This is ridiculous, but the descent from the line above
6636             // can hang down into the line we really want to redraw,
6637             // so we have to invalidate part of the line above to make
6638             // sure everything that needs to be redrawn really is.
6639             // (But not the whole line above, because that would cause
6640             // the same problem with the descenders on the line above it!)
6641             if (lineStart > 0) {
6642                 top -= mLayout.getLineDescent(lineStart - 1);
6643             }
6644
6645             int lineEnd;
6646
6647             if (start == end) {
6648                 lineEnd = lineStart;
6649             } else {
6650                 lineEnd = mLayout.getLineForOffset(end);
6651             }
6652
6653             int bottom = mLayout.getLineBottom(lineEnd);
6654
6655             // mEditor can be null in case selection is set programmatically.
6656             if (invalidateCursor && mEditor != null && mEditor.mDrawableForCursor != null) {
6657                 final Rect bounds = mEditor.mDrawableForCursor.getBounds();
6658                 top = Math.min(top, bounds.top);
6659                 bottom = Math.max(bottom, bounds.bottom);
6660             }
6661
6662             final int compoundPaddingLeft = getCompoundPaddingLeft();
6663             final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true);
6664
6665             int left, right;
6666             if (lineStart == lineEnd && !invalidateCursor) {
6667                 left = (int) mLayout.getPrimaryHorizontal(start);
6668                 right = (int) (mLayout.getPrimaryHorizontal(end) + 1.0);
6669                 left += compoundPaddingLeft;
6670                 right += compoundPaddingLeft;
6671             } else {
6672                 // Rectangle bounding box when the region spans several lines
6673                 left = compoundPaddingLeft;
6674                 right = getWidth() - getCompoundPaddingRight();
6675             }
6676
6677             invalidate(mScrollX + left, verticalPadding + top,
6678                     mScrollX + right, verticalPadding + bottom);
6679         }
6680     }
6681
6682     private void registerForPreDraw() {
6683         if (!mPreDrawRegistered) {
6684             getViewTreeObserver().addOnPreDrawListener(this);
6685             mPreDrawRegistered = true;
6686         }
6687     }
6688
6689     private void unregisterForPreDraw() {
6690         getViewTreeObserver().removeOnPreDrawListener(this);
6691         mPreDrawRegistered = false;
6692         mPreDrawListenerDetached = false;
6693     }
6694
6695     /**
6696      * {@inheritDoc}
6697      */
6698     @Override
6699     public boolean onPreDraw() {
6700         if (mLayout == null) {
6701             assumeLayout();
6702         }
6703
6704         if (mMovement != null) {
6705             /* This code also provides auto-scrolling when a cursor is moved using a
6706              * CursorController (insertion point or selection limits).
6707              * For selection, ensure start or end is visible depending on controller's state.
6708              */
6709             int curs = getSelectionEnd();
6710             // Do not create the controller if it is not already created.
6711             if (mEditor != null && mEditor.mSelectionModifierCursorController != null
6712                     && mEditor.mSelectionModifierCursorController.isSelectionStartDragged()) {
6713                 curs = getSelectionStart();
6714             }
6715
6716             /*
6717              * TODO: This should really only keep the end in view if
6718              * it already was before the text changed.  I'm not sure
6719              * of a good way to tell from here if it was.
6720              */
6721             if (curs < 0 && (mGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM) {
6722                 curs = mText.length();
6723             }
6724
6725             if (curs >= 0) {
6726                 bringPointIntoView(curs);
6727             }
6728         } else {
6729             bringTextIntoView();
6730         }
6731
6732         // This has to be checked here since:
6733         // - onFocusChanged cannot start it when focus is given to a view with selected text (after
6734         //   a screen rotation) since layout is not yet initialized at that point.
6735         if (mEditor != null && mEditor.mCreatedWithASelection) {
6736             mEditor.refreshTextActionMode();
6737             mEditor.mCreatedWithASelection = false;
6738         }
6739
6740         unregisterForPreDraw();
6741
6742         return true;
6743     }
6744
6745     @Override
6746     protected void onAttachedToWindow() {
6747         super.onAttachedToWindow();
6748
6749         if (mEditor != null) mEditor.onAttachedToWindow();
6750
6751         if (mPreDrawListenerDetached) {
6752             getViewTreeObserver().addOnPreDrawListener(this);
6753             mPreDrawListenerDetached = false;
6754         }
6755     }
6756
6757     /** @hide */
6758     @Override
6759     protected void onDetachedFromWindowInternal() {
6760         if (mPreDrawRegistered) {
6761             getViewTreeObserver().removeOnPreDrawListener(this);
6762             mPreDrawListenerDetached = true;
6763         }
6764
6765         resetResolvedDrawables();
6766
6767         if (mEditor != null) mEditor.onDetachedFromWindow();
6768
6769         super.onDetachedFromWindowInternal();
6770     }
6771
6772     @Override
6773     public void onScreenStateChanged(int screenState) {
6774         super.onScreenStateChanged(screenState);
6775         if (mEditor != null) mEditor.onScreenStateChanged(screenState);
6776     }
6777
6778     @Override
6779     protected boolean isPaddingOffsetRequired() {
6780         return mShadowRadius != 0 || mDrawables != null;
6781     }
6782
6783     @Override
6784     protected int getLeftPaddingOffset() {
6785         return getCompoundPaddingLeft() - mPaddingLeft
6786                 + (int) Math.min(0, mShadowDx - mShadowRadius);
6787     }
6788
6789     @Override
6790     protected int getTopPaddingOffset() {
6791         return (int) Math.min(0, mShadowDy - mShadowRadius);
6792     }
6793
6794     @Override
6795     protected int getBottomPaddingOffset() {
6796         return (int) Math.max(0, mShadowDy + mShadowRadius);
6797     }
6798
6799     @Override
6800     protected int getRightPaddingOffset() {
6801         return -(getCompoundPaddingRight() - mPaddingRight)
6802                 + (int) Math.max(0, mShadowDx + mShadowRadius);
6803     }
6804
6805     @Override
6806     protected boolean verifyDrawable(@NonNull Drawable who) {
6807         final boolean verified = super.verifyDrawable(who);
6808         if (!verified && mDrawables != null) {
6809             for (Drawable dr : mDrawables.mShowing) {
6810                 if (who == dr) {
6811                     return true;
6812                 }
6813             }
6814         }
6815         return verified;
6816     }
6817
6818     @Override
6819     public void jumpDrawablesToCurrentState() {
6820         super.jumpDrawablesToCurrentState();
6821         if (mDrawables != null) {
6822             for (Drawable dr : mDrawables.mShowing) {
6823                 if (dr != null) {
6824                     dr.jumpToCurrentState();
6825                 }
6826             }
6827         }
6828     }
6829
6830     @Override
6831     public void invalidateDrawable(@NonNull Drawable drawable) {
6832         boolean handled = false;
6833
6834         if (verifyDrawable(drawable)) {
6835             final Rect dirty = drawable.getBounds();
6836             int scrollX = mScrollX;
6837             int scrollY = mScrollY;
6838
6839             // IMPORTANT: The coordinates below are based on the coordinates computed
6840             // for each compound drawable in onDraw(). Make sure to update each section
6841             // accordingly.
6842             final TextView.Drawables drawables = mDrawables;
6843             if (drawables != null) {
6844                 if (drawable == drawables.mShowing[Drawables.LEFT]) {
6845                     final int compoundPaddingTop = getCompoundPaddingTop();
6846                     final int compoundPaddingBottom = getCompoundPaddingBottom();
6847                     final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop;
6848
6849                     scrollX += mPaddingLeft;
6850                     scrollY += compoundPaddingTop + (vspace - drawables.mDrawableHeightLeft) / 2;
6851                     handled = true;
6852                 } else if (drawable == drawables.mShowing[Drawables.RIGHT]) {
6853                     final int compoundPaddingTop = getCompoundPaddingTop();
6854                     final int compoundPaddingBottom = getCompoundPaddingBottom();
6855                     final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop;
6856
6857                     scrollX += (mRight - mLeft - mPaddingRight - drawables.mDrawableSizeRight);
6858                     scrollY += compoundPaddingTop + (vspace - drawables.mDrawableHeightRight) / 2;
6859                     handled = true;
6860                 } else if (drawable == drawables.mShowing[Drawables.TOP]) {
6861                     final int compoundPaddingLeft = getCompoundPaddingLeft();
6862                     final int compoundPaddingRight = getCompoundPaddingRight();
6863                     final int hspace = mRight - mLeft - compoundPaddingRight - compoundPaddingLeft;
6864
6865                     scrollX += compoundPaddingLeft + (hspace - drawables.mDrawableWidthTop) / 2;
6866                     scrollY += mPaddingTop;
6867                     handled = true;
6868                 } else if (drawable == drawables.mShowing[Drawables.BOTTOM]) {
6869                     final int compoundPaddingLeft = getCompoundPaddingLeft();
6870                     final int compoundPaddingRight = getCompoundPaddingRight();
6871                     final int hspace = mRight - mLeft - compoundPaddingRight - compoundPaddingLeft;
6872
6873                     scrollX += compoundPaddingLeft + (hspace - drawables.mDrawableWidthBottom) / 2;
6874                     scrollY += (mBottom - mTop - mPaddingBottom - drawables.mDrawableSizeBottom);
6875                     handled = true;
6876                 }
6877             }
6878
6879             if (handled) {
6880                 invalidate(dirty.left + scrollX, dirty.top + scrollY,
6881                         dirty.right + scrollX, dirty.bottom + scrollY);
6882             }
6883         }
6884
6885         if (!handled) {
6886             super.invalidateDrawable(drawable);
6887         }
6888     }
6889
6890     @Override
6891     public boolean hasOverlappingRendering() {
6892         // horizontal fading edge causes SaveLayerAlpha, which doesn't support alpha modulation
6893         return ((getBackground() != null && getBackground().getCurrent() != null)
6894                 || mText instanceof Spannable || hasSelection()
6895                 || isHorizontalFadingEdgeEnabled());
6896     }
6897
6898     /**
6899      *
6900      * Returns the state of the {@code textIsSelectable} flag (See
6901      * {@link #setTextIsSelectable setTextIsSelectable()}). Although you have to set this flag
6902      * to allow users to select and copy text in a non-editable TextView, the content of an
6903      * {@link EditText} can always be selected, independently of the value of this flag.
6904      * <p>
6905      *
6906      * @return True if the text displayed in this TextView can be selected by the user.
6907      *
6908      * @attr ref android.R.styleable#TextView_textIsSelectable
6909      */
6910     public boolean isTextSelectable() {
6911         return mEditor == null ? false : mEditor.mTextIsSelectable;
6912     }
6913
6914     /**
6915      * Sets whether the content of this view is selectable by the user. The default is
6916      * {@code false}, meaning that the content is not selectable.
6917      * <p>
6918      * When you use a TextView to display a useful piece of information to the user (such as a
6919      * contact's address), make it selectable, so that the user can select and copy its
6920      * content. You can also use set the XML attribute
6921      * {@link android.R.styleable#TextView_textIsSelectable} to "true".
6922      * <p>
6923      * When you call this method to set the value of {@code textIsSelectable}, it sets
6924      * the flags {@code focusable}, {@code focusableInTouchMode}, {@code clickable},
6925      * and {@code longClickable} to the same value. These flags correspond to the attributes
6926      * {@link android.R.styleable#View_focusable android:focusable},
6927      * {@link android.R.styleable#View_focusableInTouchMode android:focusableInTouchMode},
6928      * {@link android.R.styleable#View_clickable android:clickable}, and
6929      * {@link android.R.styleable#View_longClickable android:longClickable}. To restore any of these
6930      * flags to a state you had set previously, call one or more of the following methods:
6931      * {@link #setFocusable(boolean) setFocusable()},
6932      * {@link #setFocusableInTouchMode(boolean) setFocusableInTouchMode()},
6933      * {@link #setClickable(boolean) setClickable()} or
6934      * {@link #setLongClickable(boolean) setLongClickable()}.
6935      *
6936      * @param selectable Whether the content of this TextView should be selectable.
6937      */
6938     public void setTextIsSelectable(boolean selectable) {
6939         if (!selectable && mEditor == null) return; // false is default value with no edit data
6940
6941         createEditorIfNeeded();
6942         if (mEditor.mTextIsSelectable == selectable) return;
6943
6944         mEditor.mTextIsSelectable = selectable;
6945         setFocusableInTouchMode(selectable);
6946         setFocusable(FOCUSABLE_AUTO);
6947         setClickable(selectable);
6948         setLongClickable(selectable);
6949
6950         // mInputType should already be EditorInfo.TYPE_NULL and mInput should be null
6951
6952         setMovementMethod(selectable ? ArrowKeyMovementMethod.getInstance() : null);
6953         setText(mText, selectable ? BufferType.SPANNABLE : BufferType.NORMAL);
6954
6955         // Called by setText above, but safer in case of future code changes
6956         mEditor.prepareCursorControllers();
6957     }
6958
6959     @Override
6960     protected int[] onCreateDrawableState(int extraSpace) {
6961         final int[] drawableState;
6962
6963         if (mSingleLine) {
6964             drawableState = super.onCreateDrawableState(extraSpace);
6965         } else {
6966             drawableState = super.onCreateDrawableState(extraSpace + 1);
6967             mergeDrawableStates(drawableState, MULTILINE_STATE_SET);
6968         }
6969
6970         if (isTextSelectable()) {
6971             // Disable pressed state, which was introduced when TextView was made clickable.
6972             // Prevents text color change.
6973             // setClickable(false) would have a similar effect, but it also disables focus changes
6974             // and long press actions, which are both needed by text selection.
6975             final int length = drawableState.length;
6976             for (int i = 0; i < length; i++) {
6977                 if (drawableState[i] == R.attr.state_pressed) {
6978                     final int[] nonPressedState = new int[length - 1];
6979                     System.arraycopy(drawableState, 0, nonPressedState, 0, i);
6980                     System.arraycopy(drawableState, i + 1, nonPressedState, i, length - i - 1);
6981                     return nonPressedState;
6982                 }
6983             }
6984         }
6985
6986         return drawableState;
6987     }
6988
6989     private Path getUpdatedHighlightPath() {
6990         Path highlight = null;
6991         Paint highlightPaint = mHighlightPaint;
6992
6993         final int selStart = getSelectionStart();
6994         final int selEnd = getSelectionEnd();
6995         if (mMovement != null && (isFocused() || isPressed()) && selStart >= 0) {
6996             if (selStart == selEnd) {
6997                 if (mEditor != null && mEditor.shouldRenderCursor()) {
6998                     if (mHighlightPathBogus) {
6999                         if (mHighlightPath == null) mHighlightPath = new Path();
7000                         mHighlightPath.reset();
7001                         mLayout.getCursorPath(selStart, mHighlightPath, mText);
7002                         mEditor.updateCursorPosition();
7003                         mHighlightPathBogus = false;
7004                     }
7005
7006                     // XXX should pass to skin instead of drawing directly
7007                     highlightPaint.setColor(mCurTextColor);
7008                     highlightPaint.setStyle(Paint.Style.STROKE);
7009                     highlight = mHighlightPath;
7010                 }
7011             } else {
7012                 if (mHighlightPathBogus) {
7013                     if (mHighlightPath == null) mHighlightPath = new Path();
7014                     mHighlightPath.reset();
7015                     mLayout.getSelectionPath(selStart, selEnd, mHighlightPath);
7016                     mHighlightPathBogus = false;
7017                 }
7018
7019                 // XXX should pass to skin instead of drawing directly
7020                 highlightPaint.setColor(mHighlightColor);
7021                 highlightPaint.setStyle(Paint.Style.FILL);
7022
7023                 highlight = mHighlightPath;
7024             }
7025         }
7026         return highlight;
7027     }
7028
7029     /**
7030      * @hide
7031      */
7032     public int getHorizontalOffsetForDrawables() {
7033         return 0;
7034     }
7035
7036     @Override
7037     protected void onDraw(Canvas canvas) {
7038         restartMarqueeIfNeeded();
7039
7040         // Draw the background for this view
7041         super.onDraw(canvas);
7042
7043         final int compoundPaddingLeft = getCompoundPaddingLeft();
7044         final int compoundPaddingTop = getCompoundPaddingTop();
7045         final int compoundPaddingRight = getCompoundPaddingRight();
7046         final int compoundPaddingBottom = getCompoundPaddingBottom();
7047         final int scrollX = mScrollX;
7048         final int scrollY = mScrollY;
7049         final int right = mRight;
7050         final int left = mLeft;
7051         final int bottom = mBottom;
7052         final int top = mTop;
7053         final boolean isLayoutRtl = isLayoutRtl();
7054         final int offset = getHorizontalOffsetForDrawables();
7055         final int leftOffset = isLayoutRtl ? 0 : offset;
7056         final int rightOffset = isLayoutRtl ? offset : 0;
7057
7058         final Drawables dr = mDrawables;
7059         if (dr != null) {
7060             /*
7061              * Compound, not extended, because the icon is not clipped
7062              * if the text height is smaller.
7063              */
7064
7065             int vspace = bottom - top - compoundPaddingBottom - compoundPaddingTop;
7066             int hspace = right - left - compoundPaddingRight - compoundPaddingLeft;
7067
7068             // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
7069             // Make sure to update invalidateDrawable() when changing this code.
7070             if (dr.mShowing[Drawables.LEFT] != null) {
7071                 canvas.save();
7072                 canvas.translate(scrollX + mPaddingLeft + leftOffset,
7073                         scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightLeft) / 2);
7074                 dr.mShowing[Drawables.LEFT].draw(canvas);
7075                 canvas.restore();
7076             }
7077
7078             // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
7079             // Make sure to update invalidateDrawable() when changing this code.
7080             if (dr.mShowing[Drawables.RIGHT] != null) {
7081                 canvas.save();
7082                 canvas.translate(scrollX + right - left - mPaddingRight
7083                         - dr.mDrawableSizeRight - rightOffset,
7084                          scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightRight) / 2);
7085                 dr.mShowing[Drawables.RIGHT].draw(canvas);
7086                 canvas.restore();
7087             }
7088
7089             // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
7090             // Make sure to update invalidateDrawable() when changing this code.
7091             if (dr.mShowing[Drawables.TOP] != null) {
7092                 canvas.save();
7093                 canvas.translate(scrollX + compoundPaddingLeft
7094                         + (hspace - dr.mDrawableWidthTop) / 2, scrollY + mPaddingTop);
7095                 dr.mShowing[Drawables.TOP].draw(canvas);
7096                 canvas.restore();
7097             }
7098
7099             // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
7100             // Make sure to update invalidateDrawable() when changing this code.
7101             if (dr.mShowing[Drawables.BOTTOM] != null) {
7102                 canvas.save();
7103                 canvas.translate(scrollX + compoundPaddingLeft
7104                         + (hspace - dr.mDrawableWidthBottom) / 2,
7105                          scrollY + bottom - top - mPaddingBottom - dr.mDrawableSizeBottom);
7106                 dr.mShowing[Drawables.BOTTOM].draw(canvas);
7107                 canvas.restore();
7108             }
7109         }
7110
7111         int color = mCurTextColor;
7112
7113         if (mLayout == null) {
7114             assumeLayout();
7115         }
7116
7117         Layout layout = mLayout;
7118
7119         if (mHint != null && mText.length() == 0) {
7120             if (mHintTextColor != null) {
7121                 color = mCurHintTextColor;
7122             }
7123
7124             layout = mHintLayout;
7125         }
7126
7127         mTextPaint.setColor(color);
7128         mTextPaint.drawableState = getDrawableState();
7129
7130         canvas.save();
7131         /*  Would be faster if we didn't have to do this. Can we chop the
7132             (displayable) text so that we don't need to do this ever?
7133         */
7134
7135         int extendedPaddingTop = getExtendedPaddingTop();
7136         int extendedPaddingBottom = getExtendedPaddingBottom();
7137
7138         final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop;
7139         final int maxScrollY = mLayout.getHeight() - vspace;
7140
7141         float clipLeft = compoundPaddingLeft + scrollX;
7142         float clipTop = (scrollY == 0) ? 0 : extendedPaddingTop + scrollY;
7143         float clipRight = right - left - getCompoundPaddingRight() + scrollX;
7144         float clipBottom = bottom - top + scrollY
7145                 - ((scrollY == maxScrollY) ? 0 : extendedPaddingBottom);
7146
7147         if (mShadowRadius != 0) {
7148             clipLeft += Math.min(0, mShadowDx - mShadowRadius);
7149             clipRight += Math.max(0, mShadowDx + mShadowRadius);
7150
7151             clipTop += Math.min(0, mShadowDy - mShadowRadius);
7152             clipBottom += Math.max(0, mShadowDy + mShadowRadius);
7153         }
7154
7155         canvas.clipRect(clipLeft, clipTop, clipRight, clipBottom);
7156
7157         int voffsetText = 0;
7158         int voffsetCursor = 0;
7159
7160         // translate in by our padding
7161         /* shortcircuit calling getVerticaOffset() */
7162         if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
7163             voffsetText = getVerticalOffset(false);
7164             voffsetCursor = getVerticalOffset(true);
7165         }
7166         canvas.translate(compoundPaddingLeft, extendedPaddingTop + voffsetText);
7167
7168         final int layoutDirection = getLayoutDirection();
7169         final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
7170         if (isMarqueeFadeEnabled()) {
7171             if (!mSingleLine && getLineCount() == 1 && canMarquee()
7172                     && (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.LEFT) {
7173                 final int width = mRight - mLeft;
7174                 final int padding = getCompoundPaddingLeft() + getCompoundPaddingRight();
7175                 final float dx = mLayout.getLineRight(0) - (width - padding);
7176                 canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
7177             }
7178
7179             if (mMarquee != null && mMarquee.isRunning()) {
7180                 final float dx = -mMarquee.getScroll();
7181                 canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
7182             }
7183         }
7184
7185         final int cursorOffsetVertical = voffsetCursor - voffsetText;
7186
7187         Path highlight = getUpdatedHighlightPath();
7188         if (mEditor != null) {
7189             mEditor.onDraw(canvas, layout, highlight, mHighlightPaint, cursorOffsetVertical);
7190         } else {
7191             layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
7192         }
7193
7194         if (mMarquee != null && mMarquee.shouldDrawGhost()) {
7195             final float dx = mMarquee.getGhostOffset();
7196             canvas.translate(layout.getParagraphDirection(0) * dx, 0.0f);
7197             layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical);
7198         }
7199
7200         canvas.restore();
7201     }
7202
7203     @Override
7204     public void getFocusedRect(Rect r) {
7205         if (mLayout == null) {
7206             super.getFocusedRect(r);
7207             return;
7208         }
7209
7210         int selEnd = getSelectionEnd();
7211         if (selEnd < 0) {
7212             super.getFocusedRect(r);
7213             return;
7214         }
7215
7216         int selStart = getSelectionStart();
7217         if (selStart < 0 || selStart >= selEnd) {
7218             int line = mLayout.getLineForOffset(selEnd);
7219             r.top = mLayout.getLineTop(line);
7220             r.bottom = mLayout.getLineBottom(line);
7221             r.left = (int) mLayout.getPrimaryHorizontal(selEnd) - 2;
7222             r.right = r.left + 4;
7223         } else {
7224             int lineStart = mLayout.getLineForOffset(selStart);
7225             int lineEnd = mLayout.getLineForOffset(selEnd);
7226             r.top = mLayout.getLineTop(lineStart);
7227             r.bottom = mLayout.getLineBottom(lineEnd);
7228             if (lineStart == lineEnd) {
7229                 r.left = (int) mLayout.getPrimaryHorizontal(selStart);
7230                 r.right = (int) mLayout.getPrimaryHorizontal(selEnd);
7231             } else {
7232                 // Selection extends across multiple lines -- make the focused
7233                 // rect cover the entire width.
7234                 if (mHighlightPathBogus) {
7235                     if (mHighlightPath == null) mHighlightPath = new Path();
7236                     mHighlightPath.reset();
7237                     mLayout.getSelectionPath(selStart, selEnd, mHighlightPath);
7238                     mHighlightPathBogus = false;
7239                 }
7240                 synchronized (TEMP_RECTF) {
7241                     mHighlightPath.computeBounds(TEMP_RECTF, true);
7242                     r.left = (int) TEMP_RECTF.left - 1;
7243                     r.right = (int) TEMP_RECTF.right + 1;
7244                 }
7245             }
7246         }
7247
7248         // Adjust for padding and gravity.
7249         int paddingLeft = getCompoundPaddingLeft();
7250         int paddingTop = getExtendedPaddingTop();
7251         if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
7252             paddingTop += getVerticalOffset(false);
7253         }
7254         r.offset(paddingLeft, paddingTop);
7255         int paddingBottom = getExtendedPaddingBottom();
7256         r.bottom += paddingBottom;
7257     }
7258
7259     /**
7260      * Return the number of lines of text, or 0 if the internal Layout has not
7261      * been built.
7262      */
7263     public int getLineCount() {
7264         return mLayout != null ? mLayout.getLineCount() : 0;
7265     }
7266
7267     /**
7268      * Return the baseline for the specified line (0...getLineCount() - 1)
7269      * If bounds is not null, return the top, left, right, bottom extents
7270      * of the specified line in it. If the internal Layout has not been built,
7271      * return 0 and set bounds to (0, 0, 0, 0)
7272      * @param line which line to examine (0..getLineCount() - 1)
7273      * @param bounds Optional. If not null, it returns the extent of the line
7274      * @return the Y-coordinate of the baseline
7275      */
7276     public int getLineBounds(int line, Rect bounds) {
7277         if (mLayout == null) {
7278             if (bounds != null) {
7279                 bounds.set(0, 0, 0, 0);
7280             }
7281             return 0;
7282         } else {
7283             int baseline = mLayout.getLineBounds(line, bounds);
7284
7285             int voffset = getExtendedPaddingTop();
7286             if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
7287                 voffset += getVerticalOffset(true);
7288             }
7289             if (bounds != null) {
7290                 bounds.offset(getCompoundPaddingLeft(), voffset);
7291             }
7292             return baseline + voffset;
7293         }
7294     }
7295
7296     @Override
7297     public int getBaseline() {
7298         if (mLayout == null) {
7299             return super.getBaseline();
7300         }
7301
7302         return getBaselineOffset() + mLayout.getLineBaseline(0);
7303     }
7304
7305     int getBaselineOffset() {
7306         int voffset = 0;
7307         if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
7308             voffset = getVerticalOffset(true);
7309         }
7310
7311         if (isLayoutModeOptical(mParent)) {
7312             voffset -= getOpticalInsets().top;
7313         }
7314
7315         return getExtendedPaddingTop() + voffset;
7316     }
7317
7318     /**
7319      * @hide
7320      */
7321     @Override
7322     protected int getFadeTop(boolean offsetRequired) {
7323         if (mLayout == null) return 0;
7324
7325         int voffset = 0;
7326         if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
7327             voffset = getVerticalOffset(true);
7328         }
7329
7330         if (offsetRequired) voffset += getTopPaddingOffset();
7331
7332         return getExtendedPaddingTop() + voffset;
7333     }
7334
7335     /**
7336      * @hide
7337      */
7338     @Override
7339     protected int getFadeHeight(boolean offsetRequired) {
7340         return mLayout != null ? mLayout.getHeight() : 0;
7341     }
7342
7343     @Override
7344     public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
7345         if (mText instanceof Spannable && mLinksClickable) {
7346             final float x = event.getX(pointerIndex);
7347             final float y = event.getY(pointerIndex);
7348             final int offset = getOffsetForPosition(x, y);
7349             final ClickableSpan[] clickables = ((Spannable) mText).getSpans(offset, offset,
7350                     ClickableSpan.class);
7351             if (clickables.length > 0) {
7352                 return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_HAND);
7353             }
7354         }
7355         if (isTextSelectable() || isTextEditable()) {
7356             return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_TEXT);
7357         }
7358         return super.onResolvePointerIcon(event, pointerIndex);
7359     }
7360
7361     @Override
7362     public boolean onKeyPreIme(int keyCode, KeyEvent event) {
7363         // Note: If the IME is in fullscreen mode and IMS#mExtractEditText is in text action mode,
7364         // InputMethodService#onKeyDown and InputMethodService#onKeyUp are responsible to call
7365         // InputMethodService#mExtractEditText.maybeHandleBackInTextActionMode(event).
7366         if (keyCode == KeyEvent.KEYCODE_BACK && handleBackInTextActionModeIfNeeded(event)) {
7367             return true;
7368         }
7369         return super.onKeyPreIme(keyCode, event);
7370     }
7371
7372     /**
7373      * @hide
7374      */
7375     public boolean handleBackInTextActionModeIfNeeded(KeyEvent event) {
7376         // Do nothing unless mEditor is in text action mode.
7377         if (mEditor == null || mEditor.getTextActionMode() == null) {
7378             return false;
7379         }
7380
7381         if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
7382             KeyEvent.DispatcherState state = getKeyDispatcherState();
7383             if (state != null) {
7384                 state.startTracking(event, this);
7385             }
7386             return true;
7387         } else if (event.getAction() == KeyEvent.ACTION_UP) {
7388             KeyEvent.DispatcherState state = getKeyDispatcherState();
7389             if (state != null) {
7390                 state.handleUpEvent(event);
7391             }
7392             if (event.isTracking() && !event.isCanceled()) {
7393                 stopTextActionMode();
7394                 return true;
7395             }
7396         }
7397         return false;
7398     }
7399
7400     @Override
7401     public boolean onKeyDown(int keyCode, KeyEvent event) {
7402         final int which = doKeyDown(keyCode, event, null);
7403         if (which == KEY_EVENT_NOT_HANDLED) {
7404             return super.onKeyDown(keyCode, event);
7405         }
7406
7407         return true;
7408     }
7409
7410     @Override
7411     public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
7412         KeyEvent down = KeyEvent.changeAction(event, KeyEvent.ACTION_DOWN);
7413         final int which = doKeyDown(keyCode, down, event);
7414         if (which == KEY_EVENT_NOT_HANDLED) {
7415             // Go through default dispatching.
7416             return super.onKeyMultiple(keyCode, repeatCount, event);
7417         }
7418         if (which == KEY_EVENT_HANDLED) {
7419             // Consumed the whole thing.
7420             return true;
7421         }
7422
7423         repeatCount--;
7424
7425         // We are going to dispatch the remaining events to either the input
7426         // or movement method.  To do this, we will just send a repeated stream
7427         // of down and up events until we have done the complete repeatCount.
7428         // It would be nice if those interfaces had an onKeyMultiple() method,
7429         // but adding that is a more complicated change.
7430         KeyEvent up = KeyEvent.changeAction(event, KeyEvent.ACTION_UP);
7431         if (which == KEY_DOWN_HANDLED_BY_KEY_LISTENER) {
7432             // mEditor and mEditor.mInput are not null from doKeyDown
7433             mEditor.mKeyListener.onKeyUp(this, (Editable) mText, keyCode, up);
7434             while (--repeatCount > 0) {
7435                 mEditor.mKeyListener.onKeyDown(this, (Editable) mText, keyCode, down);
7436                 mEditor.mKeyListener.onKeyUp(this, (Editable) mText, keyCode, up);
7437             }
7438             hideErrorIfUnchanged();
7439
7440         } else if (which == KEY_DOWN_HANDLED_BY_MOVEMENT_METHOD) {
7441             // mMovement is not null from doKeyDown
7442             mMovement.onKeyUp(this, (Spannable) mText, keyCode, up);
7443             while (--repeatCount > 0) {
7444                 mMovement.onKeyDown(this, (Spannable) mText, keyCode, down);
7445                 mMovement.onKeyUp(this, (Spannable) mText, keyCode, up);
7446             }
7447         }
7448
7449         return true;
7450     }
7451
7452     /**
7453      * Returns true if pressing ENTER in this field advances focus instead
7454      * of inserting the character.  This is true mostly in single-line fields,
7455      * but also in mail addresses and subjects which will display on multiple
7456      * lines but where it doesn't make sense to insert newlines.
7457      */
7458     private boolean shouldAdvanceFocusOnEnter() {
7459         if (getKeyListener() == null) {
7460             return false;
7461         }
7462
7463         if (mSingleLine) {
7464             return true;
7465         }
7466
7467         if (mEditor != null
7468                 && (mEditor.mInputType & EditorInfo.TYPE_MASK_CLASS)
7469                         == EditorInfo.TYPE_CLASS_TEXT) {
7470             int variation = mEditor.mInputType & EditorInfo.TYPE_MASK_VARIATION;
7471             if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
7472                     || variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_SUBJECT) {
7473                 return true;
7474             }
7475         }
7476
7477         return false;
7478     }
7479
7480     /**
7481      * Returns true if pressing TAB in this field advances focus instead
7482      * of inserting the character.  Insert tabs only in multi-line editors.
7483      */
7484     private boolean shouldAdvanceFocusOnTab() {
7485         if (getKeyListener() != null && !mSingleLine && mEditor != null
7486                 && (mEditor.mInputType & EditorInfo.TYPE_MASK_CLASS)
7487                         == EditorInfo.TYPE_CLASS_TEXT) {
7488             int variation = mEditor.mInputType & EditorInfo.TYPE_MASK_VARIATION;
7489             if (variation == EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE
7490                     || variation == EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) {
7491                 return false;
7492             }
7493         }
7494         return true;
7495     }
7496
7497     private boolean isDirectionalNavigationKey(int keyCode) {
7498         switch(keyCode) {
7499             case KeyEvent.KEYCODE_DPAD_UP:
7500             case KeyEvent.KEYCODE_DPAD_DOWN:
7501             case KeyEvent.KEYCODE_DPAD_LEFT:
7502             case KeyEvent.KEYCODE_DPAD_RIGHT:
7503                 return true;
7504         }
7505         return false;
7506     }
7507
7508     private int doKeyDown(int keyCode, KeyEvent event, KeyEvent otherEvent) {
7509         if (!isEnabled()) {
7510             return KEY_EVENT_NOT_HANDLED;
7511         }
7512
7513         // If this is the initial keydown, we don't want to prevent a movement away from this view.
7514         // While this shouldn't be necessary because any time we're preventing default movement we
7515         // should be restricting the focus to remain within this view, thus we'll also receive
7516         // the key up event, occasionally key up events will get dropped and we don't want to
7517         // prevent the user from traversing out of this on the next key down.
7518         if (event.getRepeatCount() == 0 && !KeyEvent.isModifierKey(keyCode)) {
7519             mPreventDefaultMovement = false;
7520         }
7521
7522         switch (keyCode) {
7523             case KeyEvent.KEYCODE_ENTER:
7524                 if (event.hasNoModifiers()) {
7525                     // When mInputContentType is set, we know that we are
7526                     // running in a "modern" cupcake environment, so don't need
7527                     // to worry about the application trying to capture
7528                     // enter key events.
7529                     if (mEditor != null && mEditor.mInputContentType != null) {
7530                         // If there is an action listener, given them a
7531                         // chance to consume the event.
7532                         if (mEditor.mInputContentType.onEditorActionListener != null
7533                                 && mEditor.mInputContentType.onEditorActionListener.onEditorAction(
7534                                         this, EditorInfo.IME_NULL, event)) {
7535                             mEditor.mInputContentType.enterDown = true;
7536                             // We are consuming the enter key for them.
7537                             return KEY_EVENT_HANDLED;
7538                         }
7539                     }
7540
7541                     // If our editor should move focus when enter is pressed, or
7542                     // this is a generated event from an IME action button, then
7543                     // don't let it be inserted into the text.
7544                     if ((event.getFlags() & KeyEvent.FLAG_EDITOR_ACTION) != 0
7545                             || shouldAdvanceFocusOnEnter()) {
7546                         if (hasOnClickListeners()) {
7547                             return KEY_EVENT_NOT_HANDLED;
7548                         }
7549                         return KEY_EVENT_HANDLED;
7550                     }
7551                 }
7552                 break;
7553
7554             case KeyEvent.KEYCODE_DPAD_CENTER:
7555                 if (event.hasNoModifiers()) {
7556                     if (shouldAdvanceFocusOnEnter()) {
7557                         return KEY_EVENT_NOT_HANDLED;
7558                     }
7559                 }
7560                 break;
7561
7562             case KeyEvent.KEYCODE_TAB:
7563                 if (event.hasNoModifiers() || event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
7564                     if (shouldAdvanceFocusOnTab()) {
7565                         return KEY_EVENT_NOT_HANDLED;
7566                     }
7567                 }
7568                 break;
7569
7570                 // Has to be done on key down (and not on key up) to correctly be intercepted.
7571             case KeyEvent.KEYCODE_BACK:
7572                 if (mEditor != null && mEditor.getTextActionMode() != null) {
7573                     stopTextActionMode();
7574                     return KEY_EVENT_HANDLED;
7575                 }
7576                 break;
7577
7578             case KeyEvent.KEYCODE_CUT:
7579                 if (event.hasNoModifiers() && canCut()) {
7580                     if (onTextContextMenuItem(ID_CUT)) {
7581                         return KEY_EVENT_HANDLED;
7582                     }
7583                 }
7584                 break;
7585
7586             case KeyEvent.KEYCODE_COPY:
7587                 if (event.hasNoModifiers() && canCopy()) {
7588                     if (onTextContextMenuItem(ID_COPY)) {
7589                         return KEY_EVENT_HANDLED;
7590                     }
7591                 }
7592                 break;
7593
7594             case KeyEvent.KEYCODE_PASTE:
7595                 if (event.hasNoModifiers() && canPaste()) {
7596                     if (onTextContextMenuItem(ID_PASTE)) {
7597                         return KEY_EVENT_HANDLED;
7598                     }
7599                 }
7600                 break;
7601         }
7602
7603         if (mEditor != null && mEditor.mKeyListener != null) {
7604             boolean doDown = true;
7605             if (otherEvent != null) {
7606                 try {
7607                     beginBatchEdit();
7608                     final boolean handled = mEditor.mKeyListener.onKeyOther(this, (Editable) mText,
7609                             otherEvent);
7610                     hideErrorIfUnchanged();
7611                     doDown = false;
7612                     if (handled) {
7613                         return KEY_EVENT_HANDLED;
7614                     }
7615                 } catch (AbstractMethodError e) {
7616                     // onKeyOther was added after 1.0, so if it isn't
7617                     // implemented we need to try to dispatch as a regular down.
7618                 } finally {
7619                     endBatchEdit();
7620                 }
7621             }
7622
7623             if (doDown) {
7624                 beginBatchEdit();
7625                 final boolean handled = mEditor.mKeyListener.onKeyDown(this, (Editable) mText,
7626                         keyCode, event);
7627                 endBatchEdit();
7628                 hideErrorIfUnchanged();
7629                 if (handled) return KEY_DOWN_HANDLED_BY_KEY_LISTENER;
7630             }
7631         }
7632
7633         // bug 650865: sometimes we get a key event before a layout.
7634         // don't try to move around if we don't know the layout.
7635
7636         if (mMovement != null && mLayout != null) {
7637             boolean doDown = true;
7638             if (otherEvent != null) {
7639                 try {
7640                     boolean handled = mMovement.onKeyOther(this, (Spannable) mText,
7641                             otherEvent);
7642                     doDown = false;
7643                     if (handled) {
7644                         return KEY_EVENT_HANDLED;
7645                     }
7646                 } catch (AbstractMethodError e) {
7647                     // onKeyOther was added after 1.0, so if it isn't
7648                     // implemented we need to try to dispatch as a regular down.
7649                 }
7650             }
7651             if (doDown) {
7652                 if (mMovement.onKeyDown(this, (Spannable) mText, keyCode, event)) {
7653                     if (event.getRepeatCount() == 0 && !KeyEvent.isModifierKey(keyCode)) {
7654                         mPreventDefaultMovement = true;
7655                     }
7656                     return KEY_DOWN_HANDLED_BY_MOVEMENT_METHOD;
7657                 }
7658             }
7659             // Consume arrows from keyboard devices to prevent focus leaving the editor.
7660             // DPAD/JOY devices (Gamepads, TV remotes) often lack a TAB key so allow those
7661             // to move focus with arrows.
7662             if (event.getSource() == InputDevice.SOURCE_KEYBOARD
7663                     && isDirectionalNavigationKey(keyCode)) {
7664                 return KEY_EVENT_HANDLED;
7665             }
7666         }
7667
7668         return mPreventDefaultMovement && !KeyEvent.isModifierKey(keyCode)
7669                 ? KEY_EVENT_HANDLED : KEY_EVENT_NOT_HANDLED;
7670     }
7671
7672     /**
7673      * Resets the mErrorWasChanged flag, so that future calls to {@link #setError(CharSequence)}
7674      * can be recorded.
7675      * @hide
7676      */
7677     public void resetErrorChangedFlag() {
7678         /*
7679          * Keep track of what the error was before doing the input
7680          * so that if an input filter changed the error, we leave
7681          * that error showing.  Otherwise, we take down whatever
7682          * error was showing when the user types something.
7683          */
7684         if (mEditor != null) mEditor.mErrorWasChanged = false;
7685     }
7686
7687     /**
7688      * @hide
7689      */
7690     public void hideErrorIfUnchanged() {
7691         if (mEditor != null && mEditor.mError != null && !mEditor.mErrorWasChanged) {
7692             setError(null, null);
7693         }
7694     }
7695
7696     @Override
7697     public boolean onKeyUp(int keyCode, KeyEvent event) {
7698         if (!isEnabled()) {
7699             return super.onKeyUp(keyCode, event);
7700         }
7701
7702         if (!KeyEvent.isModifierKey(keyCode)) {
7703             mPreventDefaultMovement = false;
7704         }
7705
7706         switch (keyCode) {
7707             case KeyEvent.KEYCODE_DPAD_CENTER:
7708                 if (event.hasNoModifiers()) {
7709                     /*
7710                      * If there is a click listener, just call through to
7711                      * super, which will invoke it.
7712                      *
7713                      * If there isn't a click listener, try to show the soft
7714                      * input method.  (It will also
7715                      * call performClick(), but that won't do anything in
7716                      * this case.)
7717                      */
7718                     if (!hasOnClickListeners()) {
7719                         if (mMovement != null && mText instanceof Editable
7720                                 && mLayout != null && onCheckIsTextEditor()) {
7721                             InputMethodManager imm = InputMethodManager.peekInstance();
7722                             viewClicked(imm);
7723                             if (imm != null && getShowSoftInputOnFocus()) {
7724                                 imm.showSoftInput(this, 0);
7725                             }
7726                         }
7727                     }
7728                 }
7729                 return super.onKeyUp(keyCode, event);
7730
7731             case KeyEvent.KEYCODE_ENTER:
7732                 if (event.hasNoModifiers()) {
7733                     if (mEditor != null && mEditor.mInputContentType != null
7734                             && mEditor.mInputContentType.onEditorActionListener != null
7735                             && mEditor.mInputContentType.enterDown) {
7736                         mEditor.mInputContentType.enterDown = false;
7737                         if (mEditor.mInputContentType.onEditorActionListener.onEditorAction(
7738                                 this, EditorInfo.IME_NULL, event)) {
7739                             return true;
7740                         }
7741                     }
7742
7743                     if ((event.getFlags() & KeyEvent.FLAG_EDITOR_ACTION) != 0
7744                             || shouldAdvanceFocusOnEnter()) {
7745                         /*
7746                          * If there is a click listener, just call through to
7747                          * super, which will invoke it.
7748                          *
7749                          * If there isn't a click listener, try to advance focus,
7750                          * but still call through to super, which will reset the
7751                          * pressed state and longpress state.  (It will also
7752                          * call performClick(), but that won't do anything in
7753                          * this case.)
7754                          */
7755                         if (!hasOnClickListeners()) {
7756                             View v = focusSearch(FOCUS_DOWN);
7757
7758                             if (v != null) {
7759                                 if (!v.requestFocus(FOCUS_DOWN)) {
7760                                     throw new IllegalStateException("focus search returned a view "
7761                                             + "that wasn't able to take focus!");
7762                                 }
7763
7764                                 /*
7765                                  * Return true because we handled the key; super
7766                                  * will return false because there was no click
7767                                  * listener.
7768                                  */
7769                                 super.onKeyUp(keyCode, event);
7770                                 return true;
7771                             } else if ((event.getFlags()
7772                                     & KeyEvent.FLAG_EDITOR_ACTION) != 0) {
7773                                 // No target for next focus, but make sure the IME
7774                                 // if this came from it.
7775                                 InputMethodManager imm = InputMethodManager.peekInstance();
7776                                 if (imm != null && imm.isActive(this)) {
7777                                     imm.hideSoftInputFromWindow(getWindowToken(), 0);
7778                                 }
7779                             }
7780                         }
7781                     }
7782                     return super.onKeyUp(keyCode, event);
7783                 }
7784                 break;
7785         }
7786
7787         if (mEditor != null && mEditor.mKeyListener != null) {
7788             if (mEditor.mKeyListener.onKeyUp(this, (Editable) mText, keyCode, event)) {
7789                 return true;
7790             }
7791         }
7792
7793         if (mMovement != null && mLayout != null) {
7794             if (mMovement.onKeyUp(this, (Spannable) mText, keyCode, event)) {
7795                 return true;
7796             }
7797         }
7798
7799         return super.onKeyUp(keyCode, event);
7800     }
7801
7802     @Override
7803     public boolean onCheckIsTextEditor() {
7804         return mEditor != null && mEditor.mInputType != EditorInfo.TYPE_NULL;
7805     }
7806
7807     @Override
7808     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
7809         if (onCheckIsTextEditor() && isEnabled()) {
7810             mEditor.createInputMethodStateIfNeeded();
7811             outAttrs.inputType = getInputType();
7812             if (mEditor.mInputContentType != null) {
7813                 outAttrs.imeOptions = mEditor.mInputContentType.imeOptions;
7814                 outAttrs.privateImeOptions = mEditor.mInputContentType.privateImeOptions;
7815                 outAttrs.actionLabel = mEditor.mInputContentType.imeActionLabel;
7816                 outAttrs.actionId = mEditor.mInputContentType.imeActionId;
7817                 outAttrs.extras = mEditor.mInputContentType.extras;
7818                 outAttrs.hintLocales = mEditor.mInputContentType.imeHintLocales;
7819             } else {
7820                 outAttrs.imeOptions = EditorInfo.IME_NULL;
7821                 outAttrs.hintLocales = null;
7822             }
7823             if (focusSearch(FOCUS_DOWN) != null) {
7824                 outAttrs.imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT;
7825             }
7826             if (focusSearch(FOCUS_UP) != null) {
7827                 outAttrs.imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS;
7828             }
7829             if ((outAttrs.imeOptions & EditorInfo.IME_MASK_ACTION)
7830                     == EditorInfo.IME_ACTION_UNSPECIFIED) {
7831                 if ((outAttrs.imeOptions & EditorInfo.IME_FLAG_NAVIGATE_NEXT) != 0) {
7832                     // An action has not been set, but the enter key will move to
7833                     // the next focus, so set the action to that.
7834                     outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT;
7835                 } else {
7836                     // An action has not been set, and there is no focus to move
7837                     // to, so let's just supply a "done" action.
7838                     outAttrs.imeOptions |= EditorInfo.IME_ACTION_DONE;
7839                 }
7840                 if (!shouldAdvanceFocusOnEnter()) {
7841                     outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION;
7842                 }
7843             }
7844             if (isMultilineInputType(outAttrs.inputType)) {
7845                 // Multi-line text editors should always show an enter key.
7846                 outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_ENTER_ACTION;
7847             }
7848             outAttrs.hintText = mHint;
7849             if (mText instanceof Editable) {
7850                 InputConnection ic = new EditableInputConnection(this);
7851                 outAttrs.initialSelStart = getSelectionStart();
7852                 outAttrs.initialSelEnd = getSelectionEnd();
7853                 outAttrs.initialCapsMode = ic.getCursorCapsMode(getInputType());
7854                 return ic;
7855             }
7856         }
7857         return null;
7858     }
7859
7860     /**
7861      * If this TextView contains editable content, extract a portion of it
7862      * based on the information in <var>request</var> in to <var>outText</var>.
7863      * @return Returns true if the text was successfully extracted, else false.
7864      */
7865     public boolean extractText(ExtractedTextRequest request, ExtractedText outText) {
7866         createEditorIfNeeded();
7867         return mEditor.extractText(request, outText);
7868     }
7869
7870     /**
7871      * This is used to remove all style-impacting spans from text before new
7872      * extracted text is being replaced into it, so that we don't have any
7873      * lingering spans applied during the replace.
7874      */
7875     static void removeParcelableSpans(Spannable spannable, int start, int end) {
7876         Object[] spans = spannable.getSpans(start, end, ParcelableSpan.class);
7877         int i = spans.length;
7878         while (i > 0) {
7879             i--;
7880             spannable.removeSpan(spans[i]);
7881         }
7882     }
7883
7884     /**
7885      * Apply to this text view the given extracted text, as previously
7886      * returned by {@link #extractText(ExtractedTextRequest, ExtractedText)}.
7887      */
7888     public void setExtractedText(ExtractedText text) {
7889         Editable content = getEditableText();
7890         if (text.text != null) {
7891             if (content == null) {
7892                 setText(text.text, TextView.BufferType.EDITABLE);
7893             } else {
7894                 int start = 0;
7895                 int end = content.length();
7896
7897                 if (text.partialStartOffset >= 0) {
7898                     final int N = content.length();
7899                     start = text.partialStartOffset;
7900                     if (start > N) start = N;
7901                     end = text.partialEndOffset;
7902                     if (end > N) end = N;
7903                 }
7904
7905                 removeParcelableSpans(content, start, end);
7906                 if (TextUtils.equals(content.subSequence(start, end), text.text)) {
7907                     if (text.text instanceof Spanned) {
7908                         // OK to copy spans only.
7909                         TextUtils.copySpansFrom((Spanned) text.text, 0, end - start,
7910                                 Object.class, content, start);
7911                     }
7912                 } else {
7913                     content.replace(start, end, text.text);
7914                 }
7915             }
7916         }
7917
7918         // Now set the selection position...  make sure it is in range, to
7919         // avoid crashes.  If this is a partial update, it is possible that
7920         // the underlying text may have changed, causing us problems here.
7921         // Also we just don't want to trust clients to do the right thing.
7922         Spannable sp = (Spannable) getText();
7923         final int N = sp.length();
7924         int start = text.selectionStart;
7925         if (start < 0) {
7926             start = 0;
7927         } else if (start > N) {
7928             start = N;
7929         }
7930         int end = text.selectionEnd;
7931         if (end < 0) {
7932             end = 0;
7933         } else if (end > N) {
7934             end = N;
7935         }
7936         Selection.setSelection(sp, start, end);
7937
7938         // Finally, update the selection mode.
7939         if ((text.flags & ExtractedText.FLAG_SELECTING) != 0) {
7940             MetaKeyKeyListener.startSelecting(this, sp);
7941         } else {
7942             MetaKeyKeyListener.stopSelecting(this, sp);
7943         }
7944
7945         setHintInternal(text.hint);
7946     }
7947
7948     /**
7949      * @hide
7950      */
7951     public void setExtracting(ExtractedTextRequest req) {
7952         if (mEditor.mInputMethodState != null) {
7953             mEditor.mInputMethodState.mExtractedTextRequest = req;
7954         }
7955         // This would stop a possible selection mode, but no such mode is started in case
7956         // extracted mode will start. Some text is selected though, and will trigger an action mode
7957         // in the extracted view.
7958         mEditor.hideCursorAndSpanControllers();
7959         stopTextActionMode();
7960         if (mEditor.mSelectionModifierCursorController != null) {
7961             mEditor.mSelectionModifierCursorController.resetTouchOffsets();
7962         }
7963     }
7964
7965     /**
7966      * Called by the framework in response to a text completion from
7967      * the current input method, provided by it calling
7968      * {@link InputConnection#commitCompletion
7969      * InputConnection.commitCompletion()}.  The default implementation does
7970      * nothing; text views that are supporting auto-completion should override
7971      * this to do their desired behavior.
7972      *
7973      * @param text The auto complete text the user has selected.
7974      */
7975     public void onCommitCompletion(CompletionInfo text) {
7976         // intentionally empty
7977     }
7978
7979     /**
7980      * Called by the framework in response to a text auto-correction (such as fixing a typo using a
7981      * dictionary) from the current input method, provided by it calling
7982      * {@link InputConnection#commitCorrection(CorrectionInfo) InputConnection.commitCorrection()}.
7983      * The default implementation flashes the background of the corrected word to provide
7984      * feedback to the user.
7985      *
7986      * @param info The auto correct info about the text that was corrected.
7987      */
7988     public void onCommitCorrection(CorrectionInfo info) {
7989         if (mEditor != null) mEditor.onCommitCorrection(info);
7990     }
7991
7992     public void beginBatchEdit() {
7993         if (mEditor != null) mEditor.beginBatchEdit();
7994     }
7995
7996     public void endBatchEdit() {
7997         if (mEditor != null) mEditor.endBatchEdit();
7998     }
7999
8000     /**
8001      * Called by the framework in response to a request to begin a batch
8002      * of edit operations through a call to link {@link #beginBatchEdit()}.
8003      */
8004     public void onBeginBatchEdit() {
8005         // intentionally empty
8006     }
8007
8008     /**
8009      * Called by the framework in response to a request to end a batch
8010      * of edit operations through a call to link {@link #endBatchEdit}.
8011      */
8012     public void onEndBatchEdit() {
8013         // intentionally empty
8014     }
8015
8016     /**
8017      * Called by the framework in response to a private command from the
8018      * current method, provided by it calling
8019      * {@link InputConnection#performPrivateCommand
8020      * InputConnection.performPrivateCommand()}.
8021      *
8022      * @param action The action name of the command.
8023      * @param data Any additional data for the command.  This may be null.
8024      * @return Return true if you handled the command, else false.
8025      */
8026     public boolean onPrivateIMECommand(String action, Bundle data) {
8027         return false;
8028     }
8029
8030     private void nullLayouts() {
8031         if (mLayout instanceof BoringLayout && mSavedLayout == null) {
8032             mSavedLayout = (BoringLayout) mLayout;
8033         }
8034         if (mHintLayout instanceof BoringLayout && mSavedHintLayout == null) {
8035             mSavedHintLayout = (BoringLayout) mHintLayout;
8036         }
8037
8038         mSavedMarqueeModeLayout = mLayout = mHintLayout = null;
8039
8040         mBoring = mHintBoring = null;
8041
8042         // Since it depends on the value of mLayout
8043         if (mEditor != null) mEditor.prepareCursorControllers();
8044     }
8045
8046     /**
8047      * Make a new Layout based on the already-measured size of the view,
8048      * on the assumption that it was measured correctly at some point.
8049      */
8050     private void assumeLayout() {
8051         int width = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight();
8052
8053         if (width < 1) {
8054             width = 0;
8055         }
8056
8057         int physicalWidth = width;
8058
8059         if (mHorizontallyScrolling) {
8060             width = VERY_WIDE;
8061         }
8062
8063         makeNewLayout(width, physicalWidth, UNKNOWN_BORING, UNKNOWN_BORING,
8064                       physicalWidth, false);
8065     }
8066
8067     private Layout.Alignment getLayoutAlignment() {
8068         Layout.Alignment alignment;
8069         switch (getTextAlignment()) {
8070             case TEXT_ALIGNMENT_GRAVITY:
8071                 switch (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) {
8072                     case Gravity.START:
8073                         alignment = Layout.Alignment.ALIGN_NORMAL;
8074                         break;
8075                     case Gravity.END:
8076                         alignment = Layout.Alignment.ALIGN_OPPOSITE;
8077                         break;
8078                     case Gravity.LEFT:
8079                         alignment = Layout.Alignment.ALIGN_LEFT;
8080                         break;
8081                     case Gravity.RIGHT:
8082                         alignment = Layout.Alignment.ALIGN_RIGHT;
8083                         break;
8084                     case Gravity.CENTER_HORIZONTAL:
8085                         alignment = Layout.Alignment.ALIGN_CENTER;
8086                         break;
8087                     default:
8088                         alignment = Layout.Alignment.ALIGN_NORMAL;
8089                         break;
8090                 }
8091                 break;
8092             case TEXT_ALIGNMENT_TEXT_START:
8093                 alignment = Layout.Alignment.ALIGN_NORMAL;
8094                 break;
8095             case TEXT_ALIGNMENT_TEXT_END:
8096                 alignment = Layout.Alignment.ALIGN_OPPOSITE;
8097                 break;
8098             case TEXT_ALIGNMENT_CENTER:
8099                 alignment = Layout.Alignment.ALIGN_CENTER;
8100                 break;
8101             case TEXT_ALIGNMENT_VIEW_START:
8102                 alignment = (getLayoutDirection() == LAYOUT_DIRECTION_RTL)
8103                         ? Layout.Alignment.ALIGN_RIGHT : Layout.Alignment.ALIGN_LEFT;
8104                 break;
8105             case TEXT_ALIGNMENT_VIEW_END:
8106                 alignment = (getLayoutDirection() == LAYOUT_DIRECTION_RTL)
8107                         ? Layout.Alignment.ALIGN_LEFT : Layout.Alignment.ALIGN_RIGHT;
8108                 break;
8109             case TEXT_ALIGNMENT_INHERIT:
8110                 // This should never happen as we have already resolved the text alignment
8111                 // but better safe than sorry so we just fall through
8112             default:
8113                 alignment = Layout.Alignment.ALIGN_NORMAL;
8114                 break;
8115         }
8116         return alignment;
8117     }
8118
8119     /**
8120      * The width passed in is now the desired layout width,
8121      * not the full view width with padding.
8122      * {@hide}
8123      */
8124     protected void makeNewLayout(int wantWidth, int hintWidth,
8125                                  BoringLayout.Metrics boring,
8126                                  BoringLayout.Metrics hintBoring,
8127                                  int ellipsisWidth, boolean bringIntoView) {
8128         stopMarquee();
8129
8130         // Update "old" cached values
8131         mOldMaximum = mMaximum;
8132         mOldMaxMode = mMaxMode;
8133
8134         mHighlightPathBogus = true;
8135
8136         if (wantWidth < 0) {
8137             wantWidth = 0;
8138         }
8139         if (hintWidth < 0) {
8140             hintWidth = 0;
8141         }
8142
8143         Layout.Alignment alignment = getLayoutAlignment();
8144         final boolean testDirChange = mSingleLine && mLayout != null
8145                 && (alignment == Layout.Alignment.ALIGN_NORMAL
8146                         || alignment == Layout.Alignment.ALIGN_OPPOSITE);
8147         int oldDir = 0;
8148         if (testDirChange) oldDir = mLayout.getParagraphDirection(0);
8149         boolean shouldEllipsize = mEllipsize != null && getKeyListener() == null;
8150         final boolean switchEllipsize = mEllipsize == TruncateAt.MARQUEE
8151                 && mMarqueeFadeMode != MARQUEE_FADE_NORMAL;
8152         TruncateAt effectiveEllipsize = mEllipsize;
8153         if (mEllipsize == TruncateAt.MARQUEE
8154                 && mMarqueeFadeMode == MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) {
8155             effectiveEllipsize = TruncateAt.END_SMALL;
8156         }
8157
8158         if (mTextDir == null) {
8159             mTextDir = getTextDirectionHeuristic();
8160         }
8161
8162         mLayout = makeSingleLayout(wantWidth, boring, ellipsisWidth, alignment, shouldEllipsize,
8163                 effectiveEllipsize, effectiveEllipsize == mEllipsize);
8164         if (switchEllipsize) {
8165             TruncateAt oppositeEllipsize = effectiveEllipsize == TruncateAt.MARQUEE
8166                     ? TruncateAt.END : TruncateAt.MARQUEE;
8167             mSavedMarqueeModeLayout = makeSingleLayout(wantWidth, boring, ellipsisWidth, alignment,
8168                     shouldEllipsize, oppositeEllipsize, effectiveEllipsize != mEllipsize);
8169         }
8170
8171         shouldEllipsize = mEllipsize != null;
8172         mHintLayout = null;
8173
8174         if (mHint != null) {
8175             if (shouldEllipsize) hintWidth = wantWidth;
8176
8177             if (hintBoring == UNKNOWN_BORING) {
8178                 hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir,
8179                                                    mHintBoring);
8180                 if (hintBoring != null) {
8181                     mHintBoring = hintBoring;
8182                 }
8183             }
8184
8185             if (hintBoring != null) {
8186                 if (hintBoring.width <= hintWidth
8187                         && (!shouldEllipsize || hintBoring.width <= ellipsisWidth)) {
8188                     if (mSavedHintLayout != null) {
8189                         mHintLayout = mSavedHintLayout.replaceOrMake(mHint, mTextPaint,
8190                                 hintWidth, alignment, mSpacingMult, mSpacingAdd,
8191                                 hintBoring, mIncludePad);
8192                     } else {
8193                         mHintLayout = BoringLayout.make(mHint, mTextPaint,
8194                                 hintWidth, alignment, mSpacingMult, mSpacingAdd,
8195                                 hintBoring, mIncludePad);
8196                     }
8197
8198                     mSavedHintLayout = (BoringLayout) mHintLayout;
8199                 } else if (shouldEllipsize && hintBoring.width <= hintWidth) {
8200                     if (mSavedHintLayout != null) {
8201                         mHintLayout = mSavedHintLayout.replaceOrMake(mHint, mTextPaint,
8202                                 hintWidth, alignment, mSpacingMult, mSpacingAdd,
8203                                 hintBoring, mIncludePad, mEllipsize,
8204                                 ellipsisWidth);
8205                     } else {
8206                         mHintLayout = BoringLayout.make(mHint, mTextPaint,
8207                                 hintWidth, alignment, mSpacingMult, mSpacingAdd,
8208                                 hintBoring, mIncludePad, mEllipsize,
8209                                 ellipsisWidth);
8210                     }
8211                 }
8212             }
8213             // TODO: code duplication with makeSingleLayout()
8214             if (mHintLayout == null) {
8215                 StaticLayout.Builder builder = StaticLayout.Builder.obtain(mHint, 0,
8216                         mHint.length(), mTextPaint, hintWidth)
8217                         .setAlignment(alignment)
8218                         .setTextDirection(mTextDir)
8219                         .setLineSpacing(mSpacingAdd, mSpacingMult)
8220                         .setIncludePad(mIncludePad)
8221                         .setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
8222                         .setBreakStrategy(mBreakStrategy)
8223                         .setHyphenationFrequency(mHyphenationFrequency)
8224                         .setJustificationMode(mJustificationMode)
8225                         .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
8226                 if (shouldEllipsize) {
8227                     builder.setEllipsize(mEllipsize)
8228                             .setEllipsizedWidth(ellipsisWidth);
8229                 }
8230                 mHintLayout = builder.build();
8231             }
8232         }
8233
8234         if (bringIntoView || (testDirChange && oldDir != mLayout.getParagraphDirection(0))) {
8235             registerForPreDraw();
8236         }
8237
8238         if (mEllipsize == TextUtils.TruncateAt.MARQUEE) {
8239             if (!compressText(ellipsisWidth)) {
8240                 final int height = mLayoutParams.height;
8241                 // If the size of the view does not depend on the size of the text, try to
8242                 // start the marquee immediately
8243                 if (height != LayoutParams.WRAP_CONTENT && height != LayoutParams.MATCH_PARENT) {
8244                     startMarquee();
8245                 } else {
8246                     // Defer the start of the marquee until we know our width (see setFrame())
8247                     mRestartMarquee = true;
8248                 }
8249             }
8250         }
8251
8252         // CursorControllers need a non-null mLayout
8253         if (mEditor != null) mEditor.prepareCursorControllers();
8254     }
8255
8256     /**
8257      * @hide
8258      */
8259     protected Layout makeSingleLayout(int wantWidth, BoringLayout.Metrics boring, int ellipsisWidth,
8260             Layout.Alignment alignment, boolean shouldEllipsize, TruncateAt effectiveEllipsize,
8261             boolean useSaved) {
8262         Layout result = null;
8263         if (mText instanceof Spannable) {
8264             final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(mText, mTextPaint,
8265                     wantWidth)
8266                     .setDisplayText(mTransformed)
8267                     .setAlignment(alignment)
8268                     .setTextDirection(mTextDir)
8269                     .setLineSpacing(mSpacingAdd, mSpacingMult)
8270                     .setIncludePad(mIncludePad)
8271                     .setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
8272                     .setBreakStrategy(mBreakStrategy)
8273                     .setHyphenationFrequency(mHyphenationFrequency)
8274                     .setJustificationMode(mJustificationMode)
8275                     .setEllipsize(getKeyListener() == null ? effectiveEllipsize : null)
8276                     .setEllipsizedWidth(ellipsisWidth);
8277             result = builder.build();
8278         } else {
8279             if (boring == UNKNOWN_BORING) {
8280                 boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
8281                 if (boring != null) {
8282                     mBoring = boring;
8283                 }
8284             }
8285
8286             if (boring != null) {
8287                 if (boring.width <= wantWidth
8288                         && (effectiveEllipsize == null || boring.width <= ellipsisWidth)) {
8289                     if (useSaved && mSavedLayout != null) {
8290                         result = mSavedLayout.replaceOrMake(mTransformed, mTextPaint,
8291                                 wantWidth, alignment, mSpacingMult, mSpacingAdd,
8292                                 boring, mIncludePad);
8293                     } else {
8294                         result = BoringLayout.make(mTransformed, mTextPaint,
8295                                 wantWidth, alignment, mSpacingMult, mSpacingAdd,
8296                                 boring, mIncludePad);
8297                     }
8298
8299                     if (useSaved) {
8300                         mSavedLayout = (BoringLayout) result;
8301                     }
8302                 } else if (shouldEllipsize && boring.width <= wantWidth) {
8303                     if (useSaved && mSavedLayout != null) {
8304                         result = mSavedLayout.replaceOrMake(mTransformed, mTextPaint,
8305                                 wantWidth, alignment, mSpacingMult, mSpacingAdd,
8306                                 boring, mIncludePad, effectiveEllipsize,
8307                                 ellipsisWidth);
8308                     } else {
8309                         result = BoringLayout.make(mTransformed, mTextPaint,
8310                                 wantWidth, alignment, mSpacingMult, mSpacingAdd,
8311                                 boring, mIncludePad, effectiveEllipsize,
8312                                 ellipsisWidth);
8313                     }
8314                 }
8315             }
8316         }
8317         if (result == null) {
8318             StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed,
8319                     0, mTransformed.length(), mTextPaint, wantWidth)
8320                     .setAlignment(alignment)
8321                     .setTextDirection(mTextDir)
8322                     .setLineSpacing(mSpacingAdd, mSpacingMult)
8323                     .setIncludePad(mIncludePad)
8324                     .setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
8325                     .setBreakStrategy(mBreakStrategy)
8326                     .setHyphenationFrequency(mHyphenationFrequency)
8327                     .setJustificationMode(mJustificationMode)
8328                     .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
8329             if (shouldEllipsize) {
8330                 builder.setEllipsize(effectiveEllipsize)
8331                         .setEllipsizedWidth(ellipsisWidth);
8332             }
8333             result = builder.build();
8334         }
8335         return result;
8336     }
8337
8338     private boolean compressText(float width) {
8339         if (isHardwareAccelerated()) return false;
8340
8341         // Only compress the text if it hasn't been compressed by the previous pass
8342         if (width > 0.0f && mLayout != null && getLineCount() == 1 && !mUserSetTextScaleX
8343                 && mTextPaint.getTextScaleX() == 1.0f) {
8344             final float textWidth = mLayout.getLineWidth(0);
8345             final float overflow = (textWidth + 1.0f - width) / width;
8346             if (overflow > 0.0f && overflow <= Marquee.MARQUEE_DELTA_MAX) {
8347                 mTextPaint.setTextScaleX(1.0f - overflow - 0.005f);
8348                 post(new Runnable() {
8349                     public void run() {
8350                         requestLayout();
8351                     }
8352                 });
8353                 return true;
8354             }
8355         }
8356
8357         return false;
8358     }
8359
8360     private static int desired(Layout layout) {
8361         int n = layout.getLineCount();
8362         CharSequence text = layout.getText();
8363         float max = 0;
8364
8365         // if any line was wrapped, we can't use it.
8366         // but it's ok for the last line not to have a newline
8367
8368         for (int i = 0; i < n - 1; i++) {
8369             if (text.charAt(layout.getLineEnd(i) - 1) != '\n') {
8370                 return -1;
8371             }
8372         }
8373
8374         for (int i = 0; i < n; i++) {
8375             max = Math.max(max, layout.getLineWidth(i));
8376         }
8377
8378         return (int) Math.ceil(max);
8379     }
8380
8381     /**
8382      * Set whether the TextView includes extra top and bottom padding to make
8383      * room for accents that go above the normal ascent and descent.
8384      * The default is true.
8385      *
8386      * @see #getIncludeFontPadding()
8387      *
8388      * @attr ref android.R.styleable#TextView_includeFontPadding
8389      */
8390     public void setIncludeFontPadding(boolean includepad) {
8391         if (mIncludePad != includepad) {
8392             mIncludePad = includepad;
8393
8394             if (mLayout != null) {
8395                 nullLayouts();
8396                 requestLayout();
8397                 invalidate();
8398             }
8399         }
8400     }
8401
8402     /**
8403      * Gets whether the TextView includes extra top and bottom padding to make
8404      * room for accents that go above the normal ascent and descent.
8405      *
8406      * @see #setIncludeFontPadding(boolean)
8407      *
8408      * @attr ref android.R.styleable#TextView_includeFontPadding
8409      */
8410     public boolean getIncludeFontPadding() {
8411         return mIncludePad;
8412     }
8413
8414     private static final BoringLayout.Metrics UNKNOWN_BORING = new BoringLayout.Metrics();
8415
8416     @Override
8417     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
8418         int widthMode = MeasureSpec.getMode(widthMeasureSpec);
8419         int heightMode = MeasureSpec.getMode(heightMeasureSpec);
8420         int widthSize = MeasureSpec.getSize(widthMeasureSpec);
8421         int heightSize = MeasureSpec.getSize(heightMeasureSpec);
8422
8423         int width;
8424         int height;
8425
8426         BoringLayout.Metrics boring = UNKNOWN_BORING;
8427         BoringLayout.Metrics hintBoring = UNKNOWN_BORING;
8428
8429         if (mTextDir == null) {
8430             mTextDir = getTextDirectionHeuristic();
8431         }
8432
8433         int des = -1;
8434         boolean fromexisting = false;
8435         final float widthLimit = (widthMode == MeasureSpec.AT_MOST)
8436                 ?  (float) widthSize : Float.MAX_VALUE;
8437
8438         if (widthMode == MeasureSpec.EXACTLY) {
8439             // Parent has told us how big to be. So be it.
8440             width = widthSize;
8441         } else {
8442             if (mLayout != null && mEllipsize == null) {
8443                 des = desired(mLayout);
8444             }
8445
8446             if (des < 0) {
8447                 boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
8448                 if (boring != null) {
8449                     mBoring = boring;
8450                 }
8451             } else {
8452                 fromexisting = true;
8453             }
8454
8455             if (boring == null || boring == UNKNOWN_BORING) {
8456                 if (des < 0) {
8457                     des = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mTransformed, 0,
8458                             mTransformed.length(), mTextPaint, mTextDir, widthLimit));
8459                 }
8460                 width = des;
8461             } else {
8462                 width = boring.width;
8463             }
8464
8465             final Drawables dr = mDrawables;
8466             if (dr != null) {
8467                 width = Math.max(width, dr.mDrawableWidthTop);
8468                 width = Math.max(width, dr.mDrawableWidthBottom);
8469             }
8470
8471             if (mHint != null) {
8472                 int hintDes = -1;
8473                 int hintWidth;
8474
8475                 if (mHintLayout != null && mEllipsize == null) {
8476                     hintDes = desired(mHintLayout);
8477                 }
8478
8479                 if (hintDes < 0) {
8480                     hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir, mHintBoring);
8481                     if (hintBoring != null) {
8482                         mHintBoring = hintBoring;
8483                     }
8484                 }
8485
8486                 if (hintBoring == null || hintBoring == UNKNOWN_BORING) {
8487                     if (hintDes < 0) {
8488                         hintDes = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mHint, 0,
8489                                 mHint.length(), mTextPaint, mTextDir, widthLimit));
8490                     }
8491                     hintWidth = hintDes;
8492                 } else {
8493                     hintWidth = hintBoring.width;
8494                 }
8495
8496                 if (hintWidth > width) {
8497                     width = hintWidth;
8498                 }
8499             }
8500
8501             width += getCompoundPaddingLeft() + getCompoundPaddingRight();
8502
8503             if (mMaxWidthMode == EMS) {
8504                 width = Math.min(width, mMaxWidth * getLineHeight());
8505             } else {
8506                 width = Math.min(width, mMaxWidth);
8507             }
8508
8509             if (mMinWidthMode == EMS) {
8510                 width = Math.max(width, mMinWidth * getLineHeight());
8511             } else {
8512                 width = Math.max(width, mMinWidth);
8513             }
8514
8515             // Check against our minimum width
8516             width = Math.max(width, getSuggestedMinimumWidth());
8517
8518             if (widthMode == MeasureSpec.AT_MOST) {
8519                 width = Math.min(widthSize, width);
8520             }
8521         }
8522
8523         int want = width - getCompoundPaddingLeft() - getCompoundPaddingRight();
8524         int unpaddedWidth = want;
8525
8526         if (mHorizontallyScrolling) want = VERY_WIDE;
8527
8528         int hintWant = want;
8529         int hintWidth = (mHintLayout == null) ? hintWant : mHintLayout.getWidth();
8530
8531         if (mLayout == null) {
8532             makeNewLayout(want, hintWant, boring, hintBoring,
8533                           width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false);
8534         } else {
8535             final boolean layoutChanged = (mLayout.getWidth() != want) || (hintWidth != hintWant)
8536                     || (mLayout.getEllipsizedWidth()
8537                             != width - getCompoundPaddingLeft() - getCompoundPaddingRight());
8538
8539             final boolean widthChanged = (mHint == null) && (mEllipsize == null)
8540                     && (want > mLayout.getWidth())
8541                     && (mLayout instanceof BoringLayout
8542                             || (fromexisting && des >= 0 && des <= want));
8543
8544             final boolean maximumChanged = (mMaxMode != mOldMaxMode) || (mMaximum != mOldMaximum);
8545
8546             if (layoutChanged || maximumChanged) {
8547                 if (!maximumChanged && widthChanged) {
8548                     mLayout.increaseWidthTo(want);
8549                 } else {
8550                     makeNewLayout(want, hintWant, boring, hintBoring,
8551                             width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false);
8552                 }
8553             } else {
8554                 // Nothing has changed
8555             }
8556         }
8557
8558         if (heightMode == MeasureSpec.EXACTLY) {
8559             // Parent has told us how big to be. So be it.
8560             height = heightSize;
8561             mDesiredHeightAtMeasure = -1;
8562         } else {
8563             int desired = getDesiredHeight();
8564
8565             height = desired;
8566             mDesiredHeightAtMeasure = desired;
8567
8568             if (heightMode == MeasureSpec.AT_MOST) {
8569                 height = Math.min(desired, heightSize);
8570             }
8571         }
8572
8573         int unpaddedHeight = height - getCompoundPaddingTop() - getCompoundPaddingBottom();
8574         if (mMaxMode == LINES && mLayout.getLineCount() > mMaximum) {
8575             unpaddedHeight = Math.min(unpaddedHeight, mLayout.getLineTop(mMaximum));
8576         }
8577
8578         /*
8579          * We didn't let makeNewLayout() register to bring the cursor into view,
8580          * so do it here if there is any possibility that it is needed.
8581          */
8582         if (mMovement != null
8583                 || mLayout.getWidth() > unpaddedWidth
8584                 || mLayout.getHeight() > unpaddedHeight) {
8585             registerForPreDraw();
8586         } else {
8587             scrollTo(0, 0);
8588         }
8589
8590         setMeasuredDimension(width, height);
8591     }
8592
8593     /**
8594      * Automatically computes and sets the text size.
8595      */
8596     private void autoSizeText() {
8597         if (!isAutoSizeEnabled()) {
8598             return;
8599         }
8600
8601         if (mNeedsAutoSizeText) {
8602             if (getMeasuredWidth() <= 0 || getMeasuredHeight() <= 0) {
8603                 return;
8604             }
8605
8606             final int availableWidth = mHorizontallyScrolling
8607                     ? VERY_WIDE
8608                     : getMeasuredWidth() - getTotalPaddingLeft() - getTotalPaddingRight();
8609             final int availableHeight = getMeasuredHeight() - getExtendedPaddingBottom()
8610                     - getExtendedPaddingTop();
8611
8612             if (availableWidth <= 0 || availableHeight <= 0) {
8613                 return;
8614             }
8615
8616             synchronized (TEMP_RECTF) {
8617                 TEMP_RECTF.setEmpty();
8618                 TEMP_RECTF.right = availableWidth;
8619                 TEMP_RECTF.bottom = availableHeight;
8620                 final float optimalTextSize = findLargestTextSizeWhichFits(TEMP_RECTF);
8621
8622                 if (optimalTextSize != getTextSize()) {
8623                     setTextSizeInternal(TypedValue.COMPLEX_UNIT_PX, optimalTextSize,
8624                             false /* shouldRequestLayout */);
8625
8626                     makeNewLayout(availableWidth, 0 /* hintWidth */, UNKNOWN_BORING, UNKNOWN_BORING,
8627                             mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(),
8628                             false /* bringIntoView */);
8629                 }
8630             }
8631         }
8632         // Always try to auto-size if enabled. Functions that do not want to trigger auto-sizing
8633         // after the next layout pass should set this to false.
8634         mNeedsAutoSizeText = true;
8635     }
8636
8637     /**
8638      * Performs a binary search to find the largest text size that will still fit within the size
8639      * available to this view.
8640      */
8641     private int findLargestTextSizeWhichFits(RectF availableSpace) {
8642         final int sizesCount = mAutoSizeTextSizesInPx.length;
8643         if (sizesCount == 0) {
8644             throw new IllegalStateException("No available text sizes to choose from.");
8645         }
8646
8647         int bestSizeIndex = 0;
8648         int lowIndex = bestSizeIndex + 1;
8649         int highIndex = sizesCount - 1;
8650         int sizeToTryIndex;
8651         while (lowIndex <= highIndex) {
8652             sizeToTryIndex = (lowIndex + highIndex) / 2;
8653             if (suggestedSizeFitsInSpace(mAutoSizeTextSizesInPx[sizeToTryIndex], availableSpace)) {
8654                 bestSizeIndex = lowIndex;
8655                 lowIndex = sizeToTryIndex + 1;
8656             } else {
8657                 highIndex = sizeToTryIndex - 1;
8658                 bestSizeIndex = highIndex;
8659             }
8660         }
8661
8662         return mAutoSizeTextSizesInPx[bestSizeIndex];
8663     }
8664
8665     private boolean suggestedSizeFitsInSpace(int suggestedSizeInPx, RectF availableSpace) {
8666         final CharSequence text = mTransformed != null
8667                 ? mTransformed
8668                 : getText();
8669         final int maxLines = getMaxLines();
8670         if (mTempTextPaint == null) {
8671             mTempTextPaint = new TextPaint();
8672         } else {
8673             mTempTextPaint.reset();
8674         }
8675         mTempTextPaint.set(getPaint());
8676         mTempTextPaint.setTextSize(suggestedSizeInPx);
8677
8678         final StaticLayout.Builder layoutBuilder = StaticLayout.Builder.obtain(
8679                 text, 0, text.length(),  mTempTextPaint, Math.round(availableSpace.right));
8680
8681         layoutBuilder.setAlignment(getLayoutAlignment())
8682                 .setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier())
8683                 .setIncludePad(getIncludeFontPadding())
8684                 .setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
8685                 .setBreakStrategy(getBreakStrategy())
8686                 .setHyphenationFrequency(getHyphenationFrequency())
8687                 .setJustificationMode(getJustificationMode())
8688                 .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE)
8689                 .setTextDirection(getTextDirectionHeuristic());
8690
8691         final StaticLayout layout = layoutBuilder.build();
8692
8693         // Lines overflow.
8694         if (maxLines != -1 && layout.getLineCount() > maxLines) {
8695             return false;
8696         }
8697
8698         // Height overflow.
8699         if (layout.getHeight() > availableSpace.bottom) {
8700             return false;
8701         }
8702
8703         return true;
8704     }
8705
8706     private int getDesiredHeight() {
8707         return Math.max(
8708                 getDesiredHeight(mLayout, true),
8709                 getDesiredHeight(mHintLayout, mEllipsize != null));
8710     }
8711
8712     private int getDesiredHeight(Layout layout, boolean cap) {
8713         if (layout == null) {
8714             return 0;
8715         }
8716
8717         /*
8718         * Don't cap the hint to a certain number of lines.
8719         * (Do cap it, though, if we have a maximum pixel height.)
8720         */
8721         int desired = layout.getHeight(cap);
8722
8723         final Drawables dr = mDrawables;
8724         if (dr != null) {
8725             desired = Math.max(desired, dr.mDrawableHeightLeft);
8726             desired = Math.max(desired, dr.mDrawableHeightRight);
8727         }
8728
8729         int linecount = layout.getLineCount();
8730         final int padding = getCompoundPaddingTop() + getCompoundPaddingBottom();
8731         desired += padding;
8732
8733         if (mMaxMode != LINES) {
8734             desired = Math.min(desired, mMaximum);
8735         } else if (cap && linecount > mMaximum && (layout instanceof DynamicLayout
8736                 || layout instanceof BoringLayout)) {
8737             desired = layout.getLineTop(mMaximum);
8738
8739             if (dr != null) {
8740                 desired = Math.max(desired, dr.mDrawableHeightLeft);
8741                 desired = Math.max(desired, dr.mDrawableHeightRight);
8742             }
8743
8744             desired += padding;
8745             linecount = mMaximum;
8746         }
8747
8748         if (mMinMode == LINES) {
8749             if (linecount < mMinimum) {
8750                 desired += getLineHeight() * (mMinimum - linecount);
8751             }
8752         } else {
8753             desired = Math.max(desired, mMinimum);
8754         }
8755
8756         // Check against our minimum height
8757         desired = Math.max(desired, getSuggestedMinimumHeight());
8758
8759         return desired;
8760     }
8761
8762     /**
8763      * Check whether a change to the existing text layout requires a
8764      * new view layout.
8765      */
8766     private void checkForResize() {
8767         boolean sizeChanged = false;
8768
8769         if (mLayout != null) {
8770             // Check if our width changed
8771             if (mLayoutParams.width == LayoutParams.WRAP_CONTENT) {
8772                 sizeChanged = true;
8773                 invalidate();
8774             }
8775
8776             // Check if our height changed
8777             if (mLayoutParams.height == LayoutParams.WRAP_CONTENT) {
8778                 int desiredHeight = getDesiredHeight();
8779
8780                 if (desiredHeight != this.getHeight()) {
8781                     sizeChanged = true;
8782                 }
8783             } else if (mLayoutParams.height == LayoutParams.MATCH_PARENT) {
8784                 if (mDesiredHeightAtMeasure >= 0) {
8785                     int desiredHeight = getDesiredHeight();
8786
8787                     if (desiredHeight != mDesiredHeightAtMeasure) {
8788                         sizeChanged = true;
8789                     }
8790                 }
8791             }
8792         }
8793
8794         if (sizeChanged) {
8795             requestLayout();
8796             // caller will have already invalidated
8797         }
8798     }
8799
8800     /**
8801      * Check whether entirely new text requires a new view layout
8802      * or merely a new text layout.
8803      */
8804     private void checkForRelayout() {
8805         // If we have a fixed width, we can just swap in a new text layout
8806         // if the text height stays the same or if the view height is fixed.
8807
8808         if ((mLayoutParams.width != LayoutParams.WRAP_CONTENT
8809                 || (mMaxWidthMode == mMinWidthMode && mMaxWidth == mMinWidth))
8810                 && (mHint == null || mHintLayout != null)
8811                 && (mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight() > 0)) {
8812             // Static width, so try making a new text layout.
8813
8814             int oldht = mLayout.getHeight();
8815             int want = mLayout.getWidth();
8816             int hintWant = mHintLayout == null ? 0 : mHintLayout.getWidth();
8817
8818             /*
8819              * No need to bring the text into view, since the size is not
8820              * changing (unless we do the requestLayout(), in which case it
8821              * will happen at measure).
8822              */
8823             makeNewLayout(want, hintWant, UNKNOWN_BORING, UNKNOWN_BORING,
8824                           mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(),
8825                           false);
8826
8827             if (mEllipsize != TextUtils.TruncateAt.MARQUEE) {
8828                 // In a fixed-height view, so use our new text layout.
8829                 if (mLayoutParams.height != LayoutParams.WRAP_CONTENT
8830                         && mLayoutParams.height != LayoutParams.MATCH_PARENT) {
8831                     autoSizeText();
8832                     invalidate();
8833                     return;
8834                 }
8835
8836                 // Dynamic height, but height has stayed the same,
8837                 // so use our new text layout.
8838                 if (mLayout.getHeight() == oldht
8839                         && (mHintLayout == null || mHintLayout.getHeight() == oldht)) {
8840                     autoSizeText();
8841                     invalidate();
8842                     return;
8843                 }
8844             }
8845
8846             // We lose: the height has changed and we have a dynamic height.
8847             // Request a new view layout using our new text layout.
8848             requestLayout();
8849             invalidate();
8850         } else {
8851             // Dynamic width, so we have no choice but to request a new
8852             // view layout with a new text layout.
8853             nullLayouts();
8854             requestLayout();
8855             invalidate();
8856         }
8857     }
8858
8859     @Override
8860     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
8861         super.onLayout(changed, left, top, right, bottom);
8862         if (mDeferScroll >= 0) {
8863             int curs = mDeferScroll;
8864             mDeferScroll = -1;
8865             bringPointIntoView(Math.min(curs, mText.length()));
8866         }
8867         // Call auto-size after the width and height have been calculated.
8868         autoSizeText();
8869     }
8870
8871     private boolean isShowingHint() {
8872         return TextUtils.isEmpty(mText) && !TextUtils.isEmpty(mHint);
8873     }
8874
8875     /**
8876      * Returns true if anything changed.
8877      */
8878     private boolean bringTextIntoView() {
8879         Layout layout = isShowingHint() ? mHintLayout : mLayout;
8880         int line = 0;
8881         if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM) {
8882             line = layout.getLineCount() - 1;
8883         }
8884
8885         Layout.Alignment a = layout.getParagraphAlignment(line);
8886         int dir = layout.getParagraphDirection(line);
8887         int hspace = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight();
8888         int vspace = mBottom - mTop - getExtendedPaddingTop() - getExtendedPaddingBottom();
8889         int ht = layout.getHeight();
8890
8891         int scrollx, scrolly;
8892
8893         // Convert to left, center, or right alignment.
8894         if (a == Layout.Alignment.ALIGN_NORMAL) {
8895             a = dir == Layout.DIR_LEFT_TO_RIGHT
8896                     ? Layout.Alignment.ALIGN_LEFT : Layout.Alignment.ALIGN_RIGHT;
8897         } else if (a == Layout.Alignment.ALIGN_OPPOSITE) {
8898             a = dir == Layout.DIR_LEFT_TO_RIGHT
8899                     ? Layout.Alignment.ALIGN_RIGHT : Layout.Alignment.ALIGN_LEFT;
8900         }
8901
8902         if (a == Layout.Alignment.ALIGN_CENTER) {
8903             /*
8904              * Keep centered if possible, or, if it is too wide to fit,
8905              * keep leading edge in view.
8906              */
8907
8908             int left = (int) Math.floor(layout.getLineLeft(line));
8909             int right = (int) Math.ceil(layout.getLineRight(line));
8910
8911             if (right - left < hspace) {
8912                 scrollx = (right + left) / 2 - hspace / 2;
8913             } else {
8914                 if (dir < 0) {
8915                     scrollx = right - hspace;
8916                 } else {
8917                     scrollx = left;
8918                 }
8919             }
8920         } else if (a == Layout.Alignment.ALIGN_RIGHT) {
8921             int right = (int) Math.ceil(layout.getLineRight(line));
8922             scrollx = right - hspace;
8923         } else { // a == Layout.Alignment.ALIGN_LEFT (will also be the default)
8924             scrollx = (int) Math.floor(layout.getLineLeft(line));
8925         }
8926
8927         if (ht < vspace) {
8928             scrolly = 0;
8929         } else {
8930             if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM) {
8931                 scrolly = ht - vspace;
8932             } else {
8933                 scrolly = 0;
8934             }
8935         }
8936
8937         if (scrollx != mScrollX || scrolly != mScrollY) {
8938             scrollTo(scrollx, scrolly);
8939             return true;
8940         } else {
8941             return false;
8942         }
8943     }
8944
8945     /**
8946      * Move the point, specified by the offset, into the view if it is needed.
8947      * This has to be called after layout. Returns true if anything changed.
8948      */
8949     public boolean bringPointIntoView(int offset) {
8950         if (isLayoutRequested()) {
8951             mDeferScroll = offset;
8952             return false;
8953         }
8954         boolean changed = false;
8955
8956         Layout layout = isShowingHint() ? mHintLayout : mLayout;
8957
8958         if (layout == null) return changed;
8959
8960         int line = layout.getLineForOffset(offset);
8961
8962         int grav;
8963
8964         switch (layout.getParagraphAlignment(line)) {
8965             case ALIGN_LEFT:
8966                 grav = 1;
8967                 break;
8968             case ALIGN_RIGHT:
8969                 grav = -1;
8970                 break;
8971             case ALIGN_NORMAL:
8972                 grav = layout.getParagraphDirection(line);
8973                 break;
8974             case ALIGN_OPPOSITE:
8975                 grav = -layout.getParagraphDirection(line);
8976                 break;
8977             case ALIGN_CENTER:
8978             default:
8979                 grav = 0;
8980                 break;
8981         }
8982
8983         // We only want to clamp the cursor to fit within the layout width
8984         // in left-to-right modes, because in a right to left alignment,
8985         // we want to scroll to keep the line-right on the screen, as other
8986         // lines are likely to have text flush with the right margin, which
8987         // we want to keep visible.
8988         // A better long-term solution would probably be to measure both
8989         // the full line and a blank-trimmed version, and, for example, use
8990         // the latter measurement for centering and right alignment, but for
8991         // the time being we only implement the cursor clamping in left to
8992         // right where it is most likely to be annoying.
8993         final boolean clamped = grav > 0;
8994         // FIXME: Is it okay to truncate this, or should we round?
8995         final int x = (int) layout.getPrimaryHorizontal(offset, clamped);
8996         final int top = layout.getLineTop(line);
8997         final int bottom = layout.getLineTop(line + 1);
8998
8999         int left = (int) Math.floor(layout.getLineLeft(line));
9000         int right = (int) Math.ceil(layout.getLineRight(line));
9001         int ht = layout.getHeight();
9002
9003         int hspace = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight();
9004         int vspace = mBottom - mTop - getExtendedPaddingTop() - getExtendedPaddingBottom();
9005         if (!mHorizontallyScrolling && right - left > hspace && right > x) {
9006             // If cursor has been clamped, make sure we don't scroll.
9007             right = Math.max(x, left + hspace);
9008         }
9009
9010         int hslack = (bottom - top) / 2;
9011         int vslack = hslack;
9012
9013         if (vslack > vspace / 4) {
9014             vslack = vspace / 4;
9015         }
9016         if (hslack > hspace / 4) {
9017             hslack = hspace / 4;
9018         }
9019
9020         int hs = mScrollX;
9021         int vs = mScrollY;
9022
9023         if (top - vs < vslack) {
9024             vs = top - vslack;
9025         }
9026         if (bottom - vs > vspace - vslack) {
9027             vs = bottom - (vspace - vslack);
9028         }
9029         if (ht - vs < vspace) {
9030             vs = ht - vspace;
9031         }
9032         if (0 - vs > 0) {
9033             vs = 0;
9034         }
9035
9036         if (grav != 0) {
9037             if (x - hs < hslack) {
9038                 hs = x - hslack;
9039             }
9040             if (x - hs > hspace - hslack) {
9041                 hs = x - (hspace - hslack);
9042             }
9043         }
9044
9045         if (grav < 0) {
9046             if (left - hs > 0) {
9047                 hs = left;
9048             }
9049             if (right - hs < hspace) {
9050                 hs = right - hspace;
9051             }
9052         } else if (grav > 0) {
9053             if (right - hs < hspace) {
9054                 hs = right - hspace;
9055             }
9056             if (left - hs > 0) {
9057                 hs = left;
9058             }
9059         } else /* grav == 0 */ {
9060             if (right - left <= hspace) {
9061                 /*
9062                  * If the entire text fits, center it exactly.
9063                  */
9064                 hs = left - (hspace - (right - left)) / 2;
9065             } else if (x > right - hslack) {
9066                 /*
9067                  * If we are near the right edge, keep the right edge
9068                  * at the edge of the view.
9069                  */
9070                 hs = right - hspace;
9071             } else if (x < left + hslack) {
9072                 /*
9073                  * If we are near the left edge, keep the left edge
9074                  * at the edge of the view.
9075                  */
9076                 hs = left;
9077             } else if (left > hs) {
9078                 /*
9079                  * Is there whitespace visible at the left?  Fix it if so.
9080                  */
9081                 hs = left;
9082             } else if (right < hs + hspace) {
9083                 /*
9084                  * Is there whitespace visible at the right?  Fix it if so.
9085                  */
9086                 hs = right - hspace;
9087             } else {
9088                 /*
9089                  * Otherwise, float as needed.
9090                  */
9091                 if (x - hs < hslack) {
9092                     hs = x - hslack;
9093                 }
9094                 if (x - hs > hspace - hslack) {
9095                     hs = x - (hspace - hslack);
9096                 }
9097             }
9098         }
9099
9100         if (hs != mScrollX || vs != mScrollY) {
9101             if (mScroller == null) {
9102                 scrollTo(hs, vs);
9103             } else {
9104                 long duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll;
9105                 int dx = hs - mScrollX;
9106                 int dy = vs - mScrollY;
9107
9108                 if (duration > ANIMATED_SCROLL_GAP) {
9109                     mScroller.startScroll(mScrollX, mScrollY, dx, dy);
9110                     awakenScrollBars(mScroller.getDuration());
9111                     invalidate();
9112                 } else {
9113                     if (!mScroller.isFinished()) {
9114                         mScroller.abortAnimation();
9115                     }
9116
9117                     scrollBy(dx, dy);
9118                 }
9119
9120                 mLastScroll = AnimationUtils.currentAnimationTimeMillis();
9121             }
9122
9123             changed = true;
9124         }
9125
9126         if (isFocused()) {
9127             // This offsets because getInterestingRect() is in terms of viewport coordinates, but
9128             // requestRectangleOnScreen() is in terms of content coordinates.
9129
9130             // The offsets here are to ensure the rectangle we are using is
9131             // within our view bounds, in case the cursor is on the far left
9132             // or right.  If it isn't withing the bounds, then this request
9133             // will be ignored.
9134             if (mTempRect == null) mTempRect = new Rect();
9135             mTempRect.set(x - 2, top, x + 2, bottom);
9136             getInterestingRect(mTempRect, line);
9137             mTempRect.offset(mScrollX, mScrollY);
9138
9139             if (requestRectangleOnScreen(mTempRect)) {
9140                 changed = true;
9141             }
9142         }
9143
9144         return changed;
9145     }
9146
9147     /**
9148      * Move the cursor, if needed, so that it is at an offset that is visible
9149      * to the user.  This will not move the cursor if it represents more than
9150      * one character (a selection range).  This will only work if the
9151      * TextView contains spannable text; otherwise it will do nothing.
9152      *
9153      * @return True if the cursor was actually moved, false otherwise.
9154      */
9155     public boolean moveCursorToVisibleOffset() {
9156         if (!(mText instanceof Spannable)) {
9157             return false;
9158         }
9159         int start = getSelectionStart();
9160         int end = getSelectionEnd();
9161         if (start != end) {
9162             return false;
9163         }
9164
9165         // First: make sure the line is visible on screen:
9166
9167         int line = mLayout.getLineForOffset(start);
9168
9169         final int top = mLayout.getLineTop(line);
9170         final int bottom = mLayout.getLineTop(line + 1);
9171         final int vspace = mBottom - mTop - getExtendedPaddingTop() - getExtendedPaddingBottom();
9172         int vslack = (bottom - top) / 2;
9173         if (vslack > vspace / 4) {
9174             vslack = vspace / 4;
9175         }
9176         final int vs = mScrollY;
9177
9178         if (top < (vs + vslack)) {
9179             line = mLayout.getLineForVertical(vs + vslack + (bottom - top));
9180         } else if (bottom > (vspace + vs - vslack)) {
9181             line = mLayout.getLineForVertical(vspace + vs - vslack - (bottom - top));
9182         }
9183
9184         // Next: make sure the character is visible on screen:
9185
9186         final int hspace = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight();
9187         final int hs = mScrollX;
9188         final int leftChar = mLayout.getOffsetForHorizontal(line, hs);
9189         final int rightChar = mLayout.getOffsetForHorizontal(line, hspace + hs);
9190
9191         // line might contain bidirectional text
9192         final int lowChar = leftChar < rightChar ? leftChar : rightChar;
9193         final int highChar = leftChar > rightChar ? leftChar : rightChar;
9194
9195         int newStart = start;
9196         if (newStart < lowChar) {
9197             newStart = lowChar;
9198         } else if (newStart > highChar) {
9199             newStart = highChar;
9200         }
9201
9202         if (newStart != start) {
9203             Selection.setSelection((Spannable) mText, newStart);
9204             return true;
9205         }
9206
9207         return false;
9208     }
9209
9210     @Override
9211     public void computeScroll() {
9212         if (mScroller != null) {
9213             if (mScroller.computeScrollOffset()) {
9214                 mScrollX = mScroller.getCurrX();
9215                 mScrollY = mScroller.getCurrY();
9216                 invalidateParentCaches();
9217                 postInvalidate();  // So we draw again
9218             }
9219         }
9220     }
9221
9222     private void getInterestingRect(Rect r, int line) {
9223         convertFromViewportToContentCoordinates(r);
9224
9225         // Rectangle can can be expanded on first and last line to take
9226         // padding into account.
9227         // TODO Take left/right padding into account too?
9228         if (line == 0) r.top -= getExtendedPaddingTop();
9229         if (line == mLayout.getLineCount() - 1) r.bottom += getExtendedPaddingBottom();
9230     }
9231
9232     private void convertFromViewportToContentCoordinates(Rect r) {
9233         final int horizontalOffset = viewportToContentHorizontalOffset();
9234         r.left += horizontalOffset;
9235         r.right += horizontalOffset;
9236
9237         final int verticalOffset = viewportToContentVerticalOffset();
9238         r.top += verticalOffset;
9239         r.bottom += verticalOffset;
9240     }
9241
9242     int viewportToContentHorizontalOffset() {
9243         return getCompoundPaddingLeft() - mScrollX;
9244     }
9245
9246     int viewportToContentVerticalOffset() {
9247         int offset = getExtendedPaddingTop() - mScrollY;
9248         if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
9249             offset += getVerticalOffset(false);
9250         }
9251         return offset;
9252     }
9253
9254     @Override
9255     public void debug(int depth) {
9256         super.debug(depth);
9257
9258         String output = debugIndent(depth);
9259         output += "frame={" + mLeft + ", " + mTop + ", " + mRight
9260                 + ", " + mBottom + "} scroll={" + mScrollX + ", " + mScrollY
9261                 + "} ";
9262
9263         if (mText != null) {
9264
9265             output += "mText=\"" + mText + "\" ";
9266             if (mLayout != null) {
9267                 output += "mLayout width=" + mLayout.getWidth()
9268                         + " height=" + mLayout.getHeight();
9269             }
9270         } else {
9271             output += "mText=NULL";
9272         }
9273         Log.d(VIEW_LOG_TAG, output);
9274     }
9275
9276     /**
9277      * Convenience for {@link Selection#getSelectionStart}.
9278      */
9279     @ViewDebug.ExportedProperty(category = "text")
9280     public int getSelectionStart() {
9281         return Selection.getSelectionStart(getText());
9282     }
9283
9284     /**
9285      * Convenience for {@link Selection#getSelectionEnd}.
9286      */
9287     @ViewDebug.ExportedProperty(category = "text")
9288     public int getSelectionEnd() {
9289         return Selection.getSelectionEnd(getText());
9290     }
9291
9292     /**
9293      * Return true iff there is a selection inside this text view.
9294      */
9295     public boolean hasSelection() {
9296         final int selectionStart = getSelectionStart();
9297         final int selectionEnd = getSelectionEnd();
9298
9299         return selectionStart >= 0 && selectionStart != selectionEnd;
9300     }
9301
9302     String getSelectedText() {
9303         if (!hasSelection()) {
9304             return null;
9305         }
9306
9307         final int start = getSelectionStart();
9308         final int end = getSelectionEnd();
9309         return String.valueOf(
9310                 start > end ? mText.subSequence(end, start) : mText.subSequence(start, end));
9311     }
9312
9313     /**
9314      * Sets the properties of this field (lines, horizontally scrolling,
9315      * transformation method) to be for a single-line input.
9316      *
9317      * @attr ref android.R.styleable#TextView_singleLine
9318      */
9319     public void setSingleLine() {
9320         setSingleLine(true);
9321     }
9322
9323     /**
9324      * Sets the properties of this field to transform input to ALL CAPS
9325      * display. This may use a "small caps" formatting if available.
9326      * This setting will be ignored if this field is editable or selectable.
9327      *
9328      * This call replaces the current transformation method. Disabling this
9329      * will not necessarily restore the previous behavior from before this
9330      * was enabled.
9331      *
9332      * @see #setTransformationMethod(TransformationMethod)
9333      * @attr ref android.R.styleable#TextView_textAllCaps
9334      */
9335     public void setAllCaps(boolean allCaps) {
9336         if (allCaps) {
9337             setTransformationMethod(new AllCapsTransformationMethod(getContext()));
9338         } else {
9339             setTransformationMethod(null);
9340         }
9341     }
9342
9343     /**
9344      *
9345      * Checks whether the transformation method applied to this TextView is set to ALL CAPS.
9346      * @return Whether the current transformation method is for ALL CAPS.
9347      *
9348      * @see #setAllCaps(boolean)
9349      * @see #setTransformationMethod(TransformationMethod)
9350      */
9351     public boolean isAllCaps() {
9352         final TransformationMethod method = getTransformationMethod();
9353         return method != null && method instanceof AllCapsTransformationMethod;
9354     }
9355
9356     /**
9357      * If true, sets the properties of this field (number of lines, horizontally scrolling,
9358      * transformation method) to be for a single-line input; if false, restores these to the default
9359      * conditions.
9360      *
9361      * Note that the default conditions are not necessarily those that were in effect prior this
9362      * method, and you may want to reset these properties to your custom values.
9363      *
9364      * @attr ref android.R.styleable#TextView_singleLine
9365      */
9366     @android.view.RemotableViewMethod
9367     public void setSingleLine(boolean singleLine) {
9368         // Could be used, but may break backward compatibility.
9369         // if (mSingleLine == singleLine) return;
9370         setInputTypeSingleLine(singleLine);
9371         applySingleLine(singleLine, true, true);
9372     }
9373
9374     /**
9375      * Adds or remove the EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE on the mInputType.
9376      * @param singleLine
9377      */
9378     private void setInputTypeSingleLine(boolean singleLine) {
9379         if (mEditor != null
9380                 && (mEditor.mInputType & EditorInfo.TYPE_MASK_CLASS)
9381                         == EditorInfo.TYPE_CLASS_TEXT) {
9382             if (singleLine) {
9383                 mEditor.mInputType &= ~EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
9384             } else {
9385                 mEditor.mInputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
9386             }
9387         }
9388     }
9389
9390     private void applySingleLine(boolean singleLine, boolean applyTransformation,
9391             boolean changeMaxLines) {
9392         mSingleLine = singleLine;
9393         if (singleLine) {
9394             setLines(1);
9395             setHorizontallyScrolling(true);
9396             if (applyTransformation) {
9397                 setTransformationMethod(SingleLineTransformationMethod.getInstance());
9398             }
9399         } else {
9400             if (changeMaxLines) {
9401                 setMaxLines(Integer.MAX_VALUE);
9402             }
9403             setHorizontallyScrolling(false);
9404             if (applyTransformation) {
9405                 setTransformationMethod(null);
9406             }
9407         }
9408     }
9409
9410     /**
9411      * Causes words in the text that are longer than the view's width
9412      * to be ellipsized instead of broken in the middle.  You may also
9413      * want to {@link #setSingleLine} or {@link #setHorizontallyScrolling}
9414      * to constrain the text to a single line.  Use <code>null</code>
9415      * to turn off ellipsizing.
9416      *
9417      * If {@link #setMaxLines} has been used to set two or more lines,
9418      * only {@link android.text.TextUtils.TruncateAt#END} and
9419      * {@link android.text.TextUtils.TruncateAt#MARQUEE} are supported
9420      * (other ellipsizing types will not do anything).
9421      *
9422      * @attr ref android.R.styleable#TextView_ellipsize
9423      */
9424     public void setEllipsize(TextUtils.TruncateAt where) {
9425         // TruncateAt is an enum. != comparison is ok between these singleton objects.
9426         if (mEllipsize != where) {
9427             mEllipsize = where;
9428
9429             if (mLayout != null) {
9430                 nullLayouts();
9431                 requestLayout();
9432                 invalidate();
9433             }
9434         }
9435     }
9436
9437     /**
9438      * Sets how many times to repeat the marquee animation. Only applied if the
9439      * TextView has marquee enabled. Set to -1 to repeat indefinitely.
9440      *
9441      * @see #getMarqueeRepeatLimit()
9442      *
9443      * @attr ref android.R.styleable#TextView_marqueeRepeatLimit
9444      */
9445     public void setMarqueeRepeatLimit(int marqueeLimit) {
9446         mMarqueeRepeatLimit = marqueeLimit;
9447     }
9448
9449     /**
9450      * Gets the number of times the marquee animation is repeated. Only meaningful if the
9451      * TextView has marquee enabled.
9452      *
9453      * @return the number of times the marquee animation is repeated. -1 if the animation
9454      * repeats indefinitely
9455      *
9456      * @see #setMarqueeRepeatLimit(int)
9457      *
9458      * @attr ref android.R.styleable#TextView_marqueeRepeatLimit
9459      */
9460     public int getMarqueeRepeatLimit() {
9461         return mMarqueeRepeatLimit;
9462     }
9463
9464     /**
9465      * Returns where, if anywhere, words that are longer than the view
9466      * is wide should be ellipsized.
9467      */
9468     @ViewDebug.ExportedProperty
9469     public TextUtils.TruncateAt getEllipsize() {
9470         return mEllipsize;
9471     }
9472
9473     /**
9474      * Set the TextView so that when it takes focus, all the text is
9475      * selected.
9476      *
9477      * @attr ref android.R.styleable#TextView_selectAllOnFocus
9478      */
9479     @android.view.RemotableViewMethod
9480     public void setSelectAllOnFocus(boolean selectAllOnFocus) {
9481         createEditorIfNeeded();
9482         mEditor.mSelectAllOnFocus = selectAllOnFocus;
9483
9484         if (selectAllOnFocus && !(mText instanceof Spannable)) {
9485             setText(mText, BufferType.SPANNABLE);
9486         }
9487     }
9488
9489     /**
9490      * Set whether the cursor is visible. The default is true. Note that this property only
9491      * makes sense for editable TextView.
9492      *
9493      * @see #isCursorVisible()
9494      *
9495      * @attr ref android.R.styleable#TextView_cursorVisible
9496      */
9497     @android.view.RemotableViewMethod
9498     public void setCursorVisible(boolean visible) {
9499         if (visible && mEditor == null) return; // visible is the default value with no edit data
9500         createEditorIfNeeded();
9501         if (mEditor.mCursorVisible != visible) {
9502             mEditor.mCursorVisible = visible;
9503             invalidate();
9504
9505             mEditor.makeBlink();
9506
9507             // InsertionPointCursorController depends on mCursorVisible
9508             mEditor.prepareCursorControllers();
9509         }
9510     }
9511
9512     /**
9513      * @return whether or not the cursor is visible (assuming this TextView is editable)
9514      *
9515      * @see #setCursorVisible(boolean)
9516      *
9517      * @attr ref android.R.styleable#TextView_cursorVisible
9518      */
9519     public boolean isCursorVisible() {
9520         // true is the default value
9521         return mEditor == null ? true : mEditor.mCursorVisible;
9522     }
9523
9524     private boolean canMarquee() {
9525         int width = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight();
9526         return width > 0 && (mLayout.getLineWidth(0) > width
9527                 || (mMarqueeFadeMode != MARQUEE_FADE_NORMAL && mSavedMarqueeModeLayout != null
9528                         && mSavedMarqueeModeLayout.getLineWidth(0) > width));
9529     }
9530
9531     private void startMarquee() {
9532         // Do not ellipsize EditText
9533         if (getKeyListener() != null) return;
9534
9535         if (compressText(getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight())) {
9536             return;
9537         }
9538
9539         if ((mMarquee == null || mMarquee.isStopped()) && (isFocused() || isSelected())
9540                 && getLineCount() == 1 && canMarquee()) {
9541
9542             if (mMarqueeFadeMode == MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) {
9543                 mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_FADE;
9544                 final Layout tmp = mLayout;
9545                 mLayout = mSavedMarqueeModeLayout;
9546                 mSavedMarqueeModeLayout = tmp;
9547                 setHorizontalFadingEdgeEnabled(true);
9548                 requestLayout();
9549                 invalidate();
9550             }
9551
9552             if (mMarquee == null) mMarquee = new Marquee(this);
9553             mMarquee.start(mMarqueeRepeatLimit);
9554         }
9555     }
9556
9557     private void stopMarquee() {
9558         if (mMarquee != null && !mMarquee.isStopped()) {
9559             mMarquee.stop();
9560         }
9561
9562         if (mMarqueeFadeMode == MARQUEE_FADE_SWITCH_SHOW_FADE) {
9563             mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS;
9564             final Layout tmp = mSavedMarqueeModeLayout;
9565             mSavedMarqueeModeLayout = mLayout;
9566             mLayout = tmp;
9567             setHorizontalFadingEdgeEnabled(false);
9568             requestLayout();
9569             invalidate();
9570         }
9571     }
9572
9573     private void startStopMarquee(boolean start) {
9574         if (mEllipsize == TextUtils.TruncateAt.MARQUEE) {
9575             if (start) {
9576                 startMarquee();
9577             } else {
9578                 stopMarquee();
9579             }
9580         }
9581     }
9582
9583     /**
9584      * This method is called when the text is changed, in case any subclasses
9585      * would like to know.
9586      *
9587      * Within <code>text</code>, the <code>lengthAfter</code> characters
9588      * beginning at <code>start</code> have just replaced old text that had
9589      * length <code>lengthBefore</code>. It is an error to attempt to make
9590      * changes to <code>text</code> from this callback.
9591      *
9592      * @param text The text the TextView is displaying
9593      * @param start The offset of the start of the range of the text that was
9594      * modified
9595      * @param lengthBefore The length of the former text that has been replaced
9596      * @param lengthAfter The length of the replacement modified text
9597      */
9598     protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
9599         // intentionally empty, template pattern method can be overridden by subclasses
9600     }
9601
9602     /**
9603      * This method is called when the selection has changed, in case any
9604      * subclasses would like to know.
9605      *
9606      * @param selStart The new selection start location.
9607      * @param selEnd The new selection end location.
9608      */
9609     protected void onSelectionChanged(int selStart, int selEnd) {
9610         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED);
9611     }
9612
9613     /**
9614      * Adds a TextWatcher to the list of those whose methods are called
9615      * whenever this TextView's text changes.
9616      * <p>
9617      * In 1.0, the {@link TextWatcher#afterTextChanged} method was erroneously
9618      * not called after {@link #setText} calls.  Now, doing {@link #setText}
9619      * if there are any text changed listeners forces the buffer type to
9620      * Editable if it would not otherwise be and does call this method.
9621      */
9622     public void addTextChangedListener(TextWatcher watcher) {
9623         if (mListeners == null) {
9624             mListeners = new ArrayList<TextWatcher>();
9625         }
9626
9627         mListeners.add(watcher);
9628     }
9629
9630     /**
9631      * Removes the specified TextWatcher from the list of those whose
9632      * methods are called
9633      * whenever this TextView's text changes.
9634      */
9635     public void removeTextChangedListener(TextWatcher watcher) {
9636         if (mListeners != null) {
9637             int i = mListeners.indexOf(watcher);
9638
9639             if (i >= 0) {
9640                 mListeners.remove(i);
9641             }
9642         }
9643     }
9644
9645     private void sendBeforeTextChanged(CharSequence text, int start, int before, int after) {
9646         if (mListeners != null) {
9647             final ArrayList<TextWatcher> list = mListeners;
9648             final int count = list.size();
9649             for (int i = 0; i < count; i++) {
9650                 list.get(i).beforeTextChanged(text, start, before, after);
9651             }
9652         }
9653
9654         // The spans that are inside or intersect the modified region no longer make sense
9655         removeIntersectingNonAdjacentSpans(start, start + before, SpellCheckSpan.class);
9656         removeIntersectingNonAdjacentSpans(start, start + before, SuggestionSpan.class);
9657     }
9658
9659     // Removes all spans that are inside or actually overlap the start..end range
9660     private <T> void removeIntersectingNonAdjacentSpans(int start, int end, Class<T> type) {
9661         if (!(mText instanceof Editable)) return;
9662         Editable text = (Editable) mText;
9663
9664         T[] spans = text.getSpans(start, end, type);
9665         final int length = spans.length;
9666         for (int i = 0; i < length; i++) {
9667             final int spanStart = text.getSpanStart(spans[i]);
9668             final int spanEnd = text.getSpanEnd(spans[i]);
9669             if (spanEnd == start || spanStart == end) break;
9670             text.removeSpan(spans[i]);
9671         }
9672     }
9673
9674     void removeAdjacentSuggestionSpans(final int pos) {
9675         if (!(mText instanceof Editable)) return;
9676         final Editable text = (Editable) mText;
9677
9678         final SuggestionSpan[] spans = text.getSpans(pos, pos, SuggestionSpan.class);
9679         final int length = spans.length;
9680         for (int i = 0; i < length; i++) {
9681             final int spanStart = text.getSpanStart(spans[i]);
9682             final int spanEnd = text.getSpanEnd(spans[i]);
9683             if (spanEnd == pos || spanStart == pos) {
9684                 if (SpellChecker.haveWordBoundariesChanged(text, pos, pos, spanStart, spanEnd)) {
9685                     text.removeSpan(spans[i]);
9686                 }
9687             }
9688         }
9689     }
9690
9691     /**
9692      * Not private so it can be called from an inner class without going
9693      * through a thunk.
9694      */
9695     void sendOnTextChanged(CharSequence text, int start, int before, int after) {
9696         if (mListeners != null) {
9697             final ArrayList<TextWatcher> list = mListeners;
9698             final int count = list.size();
9699             for (int i = 0; i < count; i++) {
9700                 list.get(i).onTextChanged(text, start, before, after);
9701             }
9702         }
9703
9704         if (mEditor != null) mEditor.sendOnTextChanged(start, before, after);
9705     }
9706
9707     /**
9708      * Not private so it can be called from an inner class without going
9709      * through a thunk.
9710      */
9711     void sendAfterTextChanged(Editable text) {
9712         if (mListeners != null) {
9713             final ArrayList<TextWatcher> list = mListeners;
9714             final int count = list.size();
9715             for (int i = 0; i < count; i++) {
9716                 list.get(i).afterTextChanged(text);
9717             }
9718         }
9719
9720         // Always notify AutoFillManager - it will return right away if autofill is disabled.
9721         notifyAutoFillManagerAfterTextChangedIfNeeded();
9722
9723         hideErrorIfUnchanged();
9724     }
9725
9726     private void notifyAutoFillManagerAfterTextChangedIfNeeded() {
9727         // It is important to not check whether the view is important for autofill
9728         // since the user can trigger autofill manually on not important views.
9729         if (!isAutofillable()) {
9730             return;
9731         }
9732         final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
9733         if (afm == null) {
9734             return;
9735         }
9736
9737         if (mLastValueSentToAutofillManager == null
9738                 || !mLastValueSentToAutofillManager.equals(mText)) {
9739             if (android.view.autofill.Helper.sVerbose) {
9740                 Log.v(LOG_TAG, "notifying AFM after text changed");
9741             }
9742             afm.notifyValueChanged(TextView.this);
9743             mLastValueSentToAutofillManager = mText;
9744         } else {
9745             if (android.view.autofill.Helper.sVerbose) {
9746                 Log.v(LOG_TAG, "not notifying AFM on unchanged text");
9747             }
9748         }
9749     }
9750
9751     private boolean isAutofillable() {
9752         // It is important to not check whether the view is important for autofill
9753         // since the user can trigger autofill manually on not important views.
9754         return getAutofillType() != AUTOFILL_TYPE_NONE;
9755     }
9756
9757     void updateAfterEdit() {
9758         invalidate();
9759         int curs = getSelectionStart();
9760
9761         if (curs >= 0 || (mGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM) {
9762             registerForPreDraw();
9763         }
9764
9765         checkForResize();
9766
9767         if (curs >= 0) {
9768             mHighlightPathBogus = true;
9769             if (mEditor != null) mEditor.makeBlink();
9770             bringPointIntoView(curs);
9771         }
9772     }
9773
9774     /**
9775      * Not private so it can be called from an inner class without going
9776      * through a thunk.
9777      */
9778     void handleTextChanged(CharSequence buffer, int start, int before, int after) {
9779         sLastCutCopyOrTextChangedTime = 0;
9780
9781         final Editor.InputMethodState ims = mEditor == null ? null : mEditor.mInputMethodState;
9782         if (ims == null || ims.mBatchEditNesting == 0) {
9783             updateAfterEdit();
9784         }
9785         if (ims != null) {
9786             ims.mContentChanged = true;
9787             if (ims.mChangedStart < 0) {
9788                 ims.mChangedStart = start;
9789                 ims.mChangedEnd = start + before;
9790             } else {
9791                 ims.mChangedStart = Math.min(ims.mChangedStart, start);
9792                 ims.mChangedEnd = Math.max(ims.mChangedEnd, start + before - ims.mChangedDelta);
9793             }
9794             ims.mChangedDelta += after - before;
9795         }
9796         resetErrorChangedFlag();
9797         sendOnTextChanged(buffer, start, before, after);
9798         onTextChanged(buffer, start, before, after);
9799     }
9800
9801     /**
9802      * Not private so it can be called from an inner class without going
9803      * through a thunk.
9804      */
9805     void spanChange(Spanned buf, Object what, int oldStart, int newStart, int oldEnd, int newEnd) {
9806         // XXX Make the start and end move together if this ends up
9807         // spending too much time invalidating.
9808
9809         boolean selChanged = false;
9810         int newSelStart = -1, newSelEnd = -1;
9811
9812         final Editor.InputMethodState ims = mEditor == null ? null : mEditor.mInputMethodState;
9813
9814         if (what == Selection.SELECTION_END) {
9815             selChanged = true;
9816             newSelEnd = newStart;
9817
9818             if (oldStart >= 0 || newStart >= 0) {
9819                 invalidateCursor(Selection.getSelectionStart(buf), oldStart, newStart);
9820                 checkForResize();
9821                 registerForPreDraw();
9822                 if (mEditor != null) mEditor.makeBlink();
9823             }
9824         }
9825
9826         if (what == Selection.SELECTION_START) {
9827             selChanged = true;
9828             newSelStart = newStart;
9829
9830             if (oldStart >= 0 || newStart >= 0) {
9831                 int end = Selection.getSelectionEnd(buf);
9832                 invalidateCursor(end, oldStart, newStart);
9833             }
9834         }
9835
9836         if (selChanged) {
9837             mHighlightPathBogus = true;
9838             if (mEditor != null && !isFocused()) mEditor.mSelectionMoved = true;
9839
9840             if ((buf.getSpanFlags(what) & Spanned.SPAN_INTERMEDIATE) == 0) {
9841                 if (newSelStart < 0) {
9842                     newSelStart = Selection.getSelectionStart(buf);
9843                 }
9844                 if (newSelEnd < 0) {
9845                     newSelEnd = Selection.getSelectionEnd(buf);
9846                 }
9847
9848                 if (mEditor != null) {
9849                     mEditor.refreshTextActionMode();
9850                     if (!hasSelection()
9851                             && mEditor.getTextActionMode() == null && hasTransientState()) {
9852                         // User generated selection has been removed.
9853                         setHasTransientState(false);
9854                     }
9855                 }
9856                 onSelectionChanged(newSelStart, newSelEnd);
9857             }
9858         }
9859
9860         if (what instanceof UpdateAppearance || what instanceof ParagraphStyle
9861                 || what instanceof CharacterStyle) {
9862             if (ims == null || ims.mBatchEditNesting == 0) {
9863                 invalidate();
9864                 mHighlightPathBogus = true;
9865                 checkForResize();
9866             } else {
9867                 ims.mContentChanged = true;
9868             }
9869             if (mEditor != null) {
9870                 if (oldStart >= 0) mEditor.invalidateTextDisplayList(mLayout, oldStart, oldEnd);
9871                 if (newStart >= 0) mEditor.invalidateTextDisplayList(mLayout, newStart, newEnd);
9872                 mEditor.invalidateHandlesAndActionMode();
9873             }
9874         }
9875
9876         if (MetaKeyKeyListener.isMetaTracker(buf, what)) {
9877             mHighlightPathBogus = true;
9878             if (ims != null && MetaKeyKeyListener.isSelectingMetaTracker(buf, what)) {
9879                 ims.mSelectionModeChanged = true;
9880             }
9881
9882             if (Selection.getSelectionStart(buf) >= 0) {
9883                 if (ims == null || ims.mBatchEditNesting == 0) {
9884                     invalidateCursor();
9885                 } else {
9886                     ims.mCursorChanged = true;
9887                 }
9888             }
9889         }
9890
9891         if (what instanceof ParcelableSpan) {
9892             // If this is a span that can be sent to a remote process,
9893             // the current extract editor would be interested in it.
9894             if (ims != null && ims.mExtractedTextRequest != null) {
9895                 if (ims.mBatchEditNesting != 0) {
9896                     if (oldStart >= 0) {
9897                         if (ims.mChangedStart > oldStart) {
9898                             ims.mChangedStart = oldStart;
9899                         }
9900                         if (ims.mChangedStart > oldEnd) {
9901                             ims.mChangedStart = oldEnd;
9902                         }
9903                     }
9904                     if (newStart >= 0) {
9905                         if (ims.mChangedStart > newStart) {
9906                             ims.mChangedStart = newStart;
9907                         }
9908                         if (ims.mChangedStart > newEnd) {
9909                             ims.mChangedStart = newEnd;
9910                         }
9911                     }
9912                 } else {
9913                     if (DEBUG_EXTRACT) {
9914                         Log.v(LOG_TAG, "Span change outside of batch: "
9915                                 + oldStart + "-" + oldEnd + ","
9916                                 + newStart + "-" + newEnd + " " + what);
9917                     }
9918                     ims.mContentChanged = true;
9919                 }
9920             }
9921         }
9922
9923         if (mEditor != null && mEditor.mSpellChecker != null && newStart < 0
9924                 && what instanceof SpellCheckSpan) {
9925             mEditor.mSpellChecker.onSpellCheckSpanRemoved((SpellCheckSpan) what);
9926         }
9927     }
9928
9929     @Override
9930     protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
9931         if (isTemporarilyDetached()) {
9932             // If we are temporarily in the detach state, then do nothing.
9933             super.onFocusChanged(focused, direction, previouslyFocusedRect);
9934             return;
9935         }
9936
9937         if (mEditor != null) mEditor.onFocusChanged(focused, direction);
9938
9939         if (focused) {
9940             if (mText instanceof Spannable) {
9941                 Spannable sp = (Spannable) mText;
9942                 MetaKeyKeyListener.resetMetaState(sp);
9943             }
9944         }
9945
9946         startStopMarquee(focused);
9947
9948         if (mTransformation != null) {
9949             mTransformation.onFocusChanged(this, mText, focused, direction, previouslyFocusedRect);
9950         }
9951
9952         super.onFocusChanged(focused, direction, previouslyFocusedRect);
9953     }
9954
9955     @Override
9956     public void onWindowFocusChanged(boolean hasWindowFocus) {
9957         super.onWindowFocusChanged(hasWindowFocus);
9958
9959         if (mEditor != null) mEditor.onWindowFocusChanged(hasWindowFocus);
9960
9961         startStopMarquee(hasWindowFocus);
9962     }
9963
9964     @Override
9965     protected void onVisibilityChanged(View changedView, int visibility) {
9966         super.onVisibilityChanged(changedView, visibility);
9967         if (mEditor != null && visibility != VISIBLE) {
9968             mEditor.hideCursorAndSpanControllers();
9969             stopTextActionMode();
9970         }
9971     }
9972
9973     /**
9974      * Use {@link BaseInputConnection#removeComposingSpans
9975      * BaseInputConnection.removeComposingSpans()} to remove any IME composing
9976      * state from this text view.
9977      */
9978     public void clearComposingText() {
9979         if (mText instanceof Spannable) {
9980             BaseInputConnection.removeComposingSpans((Spannable) mText);
9981         }
9982     }
9983
9984     @Override
9985     public void setSelected(boolean selected) {
9986         boolean wasSelected = isSelected();
9987
9988         super.setSelected(selected);
9989
9990         if (selected != wasSelected && mEllipsize == TextUtils.TruncateAt.MARQUEE) {
9991             if (selected) {
9992                 startMarquee();
9993             } else {
9994                 stopMarquee();
9995             }
9996         }
9997     }
9998
9999     @Override
10000     public boolean onTouchEvent(MotionEvent event) {
10001         final int action = event.getActionMasked();
10002         if (mEditor != null) {
10003             mEditor.onTouchEvent(event);
10004
10005             if (mEditor.mSelectionModifierCursorController != null
10006                     && mEditor.mSelectionModifierCursorController.isDragAcceleratorActive()) {
10007                 return true;
10008             }
10009         }
10010
10011         final boolean superResult = super.onTouchEvent(event);
10012
10013         /*
10014          * Don't handle the release after a long press, because it will move the selection away from
10015          * whatever the menu action was trying to affect. If the long press should have triggered an
10016          * insertion action mode, we can now actually show it.
10017          */
10018         if (mEditor != null && mEditor.mDiscardNextActionUp && action == MotionEvent.ACTION_UP) {
10019             mEditor.mDiscardNextActionUp = false;
10020
10021             if (mEditor.mIsInsertionActionModeStartPending) {
10022                 mEditor.startInsertionActionMode();
10023                 mEditor.mIsInsertionActionModeStartPending = false;
10024             }
10025             return superResult;
10026         }
10027
10028         final boolean touchIsFinished = (action == MotionEvent.ACTION_UP)
10029                 && (mEditor == null || !mEditor.mIgnoreActionUpEvent) && isFocused();
10030
10031         if ((mMovement != null || onCheckIsTextEditor()) && isEnabled()
10032                 && mText instanceof Spannable && mLayout != null) {
10033             boolean handled = false;
10034
10035             if (mMovement != null) {
10036                 handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
10037             }
10038
10039             final boolean textIsSelectable = isTextSelectable();
10040             if (touchIsFinished && mLinksClickable && mAutoLinkMask != 0 && textIsSelectable) {
10041                 // The LinkMovementMethod which should handle taps on links has not been installed
10042                 // on non editable text that support text selection.
10043                 // We reproduce its behavior here to open links for these.
10044                 ClickableSpan[] links = ((Spannable) mText).getSpans(getSelectionStart(),
10045                     getSelectionEnd(), ClickableSpan.class);
10046
10047                 if (links.length > 0) {
10048                     links[0].onClick(this);
10049                     handled = true;
10050                 }
10051             }
10052
10053             if (touchIsFinished && (isTextEditable() || textIsSelectable)) {
10054                 // Show the IME, except when selecting in read-only text.
10055                 final InputMethodManager imm = InputMethodManager.peekInstance();
10056                 viewClicked(imm);
10057                 if (isTextEditable() && mEditor.mShowSoftInputOnFocus && imm != null) {
10058                     imm.showSoftInput(this, 0);
10059                 }
10060
10061                 // The above condition ensures that the mEditor is not null
10062                 mEditor.onTouchUpEvent(event);
10063
10064                 handled = true;
10065             }
10066
10067             if (handled) {
10068                 return true;
10069             }
10070         }
10071
10072         return superResult;
10073     }
10074
10075     @Override
10076     public boolean onGenericMotionEvent(MotionEvent event) {
10077         if (mMovement != null && mText instanceof Spannable && mLayout != null) {
10078             try {
10079                 if (mMovement.onGenericMotionEvent(this, (Spannable) mText, event)) {
10080                     return true;
10081                 }
10082             } catch (AbstractMethodError ex) {
10083                 // onGenericMotionEvent was added to the MovementMethod interface in API 12.
10084                 // Ignore its absence in case third party applications implemented the
10085                 // interface directly.
10086             }
10087         }
10088         return super.onGenericMotionEvent(event);
10089     }
10090
10091     @Override
10092     protected void onCreateContextMenu(ContextMenu menu) {
10093         if (mEditor != null) {
10094             mEditor.onCreateContextMenu(menu);
10095         }
10096     }
10097
10098     @Override
10099     public boolean showContextMenu() {
10100         if (mEditor != null) {
10101             mEditor.setContextMenuAnchor(Float.NaN, Float.NaN);
10102         }
10103         return super.showContextMenu();
10104     }
10105
10106     @Override
10107     public boolean showContextMenu(float x, float y) {
10108         if (mEditor != null) {
10109             mEditor.setContextMenuAnchor(x, y);
10110         }
10111         return super.showContextMenu(x, y);
10112     }
10113
10114     /**
10115      * @return True iff this TextView contains a text that can be edited, or if this is
10116      * a selectable TextView.
10117      */
10118     boolean isTextEditable() {
10119         return mText instanceof Editable && onCheckIsTextEditor() && isEnabled();
10120     }
10121
10122     /**
10123      * Returns true, only while processing a touch gesture, if the initial
10124      * touch down event caused focus to move to the text view and as a result
10125      * its selection changed.  Only valid while processing the touch gesture
10126      * of interest, in an editable text view.
10127      */
10128     public boolean didTouchFocusSelect() {
10129         return mEditor != null && mEditor.mTouchFocusSelected;
10130     }
10131
10132     @Override
10133     public void cancelLongPress() {
10134         super.cancelLongPress();
10135         if (mEditor != null) mEditor.mIgnoreActionUpEvent = true;
10136     }
10137
10138     @Override
10139     public boolean onTrackballEvent(MotionEvent event) {
10140         if (mMovement != null && mText instanceof Spannable && mLayout != null) {
10141             if (mMovement.onTrackballEvent(this, (Spannable) mText, event)) {
10142                 return true;
10143             }
10144         }
10145
10146         return super.onTrackballEvent(event);
10147     }
10148
10149     /**
10150      * Sets the Scroller used for producing a scrolling animation
10151      *
10152      * @param s A Scroller instance
10153      */
10154     public void setScroller(Scroller s) {
10155         mScroller = s;
10156     }
10157
10158     @Override
10159     protected float getLeftFadingEdgeStrength() {
10160         if (isMarqueeFadeEnabled() && mMarquee != null && !mMarquee.isStopped()) {
10161             final Marquee marquee = mMarquee;
10162             if (marquee.shouldDrawLeftFade()) {
10163                 return getHorizontalFadingEdgeStrength(marquee.getScroll(), 0.0f);
10164             } else {
10165                 return 0.0f;
10166             }
10167         } else if (getLineCount() == 1) {
10168             final float lineLeft = getLayout().getLineLeft(0);
10169             if (lineLeft > mScrollX) return 0.0f;
10170             return getHorizontalFadingEdgeStrength(mScrollX, lineLeft);
10171         }
10172         return super.getLeftFadingEdgeStrength();
10173     }
10174
10175     @Override
10176     protected float getRightFadingEdgeStrength() {
10177         if (isMarqueeFadeEnabled() && mMarquee != null && !mMarquee.isStopped()) {
10178             final Marquee marquee = mMarquee;
10179             return getHorizontalFadingEdgeStrength(marquee.getMaxFadeScroll(), marquee.getScroll());
10180         } else if (getLineCount() == 1) {
10181             final float rightEdge = mScrollX +
10182                     (getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight());
10183             final float lineRight = getLayout().getLineRight(0);
10184             if (lineRight < rightEdge) return 0.0f;
10185             return getHorizontalFadingEdgeStrength(rightEdge, lineRight);
10186         }
10187         return super.getRightFadingEdgeStrength();
10188     }
10189
10190     /**
10191      * Calculates the fading edge strength as the ratio of the distance between two
10192      * horizontal positions to {@link View#getHorizontalFadingEdgeLength()}. Uses the absolute
10193      * value for the distance calculation.
10194      *
10195      * @param position1 A horizontal position.
10196      * @param position2 A horizontal position.
10197      * @return Fading edge strength between [0.0f, 1.0f].
10198      */
10199     @FloatRange(from = 0.0, to = 1.0)
10200     private float getHorizontalFadingEdgeStrength(float position1, float position2) {
10201         final int horizontalFadingEdgeLength = getHorizontalFadingEdgeLength();
10202         if (horizontalFadingEdgeLength == 0) return 0.0f;
10203         final float diff = Math.abs(position1 - position2);
10204         if (diff > horizontalFadingEdgeLength) return 1.0f;
10205         return diff / horizontalFadingEdgeLength;
10206     }
10207
10208     private boolean isMarqueeFadeEnabled() {
10209         return mEllipsize == TextUtils.TruncateAt.MARQUEE
10210                 && mMarqueeFadeMode != MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS;
10211     }
10212
10213     @Override
10214     protected int computeHorizontalScrollRange() {
10215         if (mLayout != null) {
10216             return mSingleLine && (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT
10217                     ? (int) mLayout.getLineWidth(0) : mLayout.getWidth();
10218         }
10219
10220         return super.computeHorizontalScrollRange();
10221     }
10222
10223     @Override
10224     protected int computeVerticalScrollRange() {
10225         if (mLayout != null) {
10226             return mLayout.getHeight();
10227         }
10228         return super.computeVerticalScrollRange();
10229     }
10230
10231     @Override
10232     protected int computeVerticalScrollExtent() {
10233         return getHeight() - getCompoundPaddingTop() - getCompoundPaddingBottom();
10234     }
10235
10236     @Override
10237     public void findViewsWithText(ArrayList<View> outViews, CharSequence searched, int flags) {
10238         super.findViewsWithText(outViews, searched, flags);
10239         if (!outViews.contains(this) && (flags & FIND_VIEWS_WITH_TEXT) != 0
10240                 && !TextUtils.isEmpty(searched) && !TextUtils.isEmpty(mText)) {
10241             String searchedLowerCase = searched.toString().toLowerCase();
10242             String textLowerCase = mText.toString().toLowerCase();
10243             if (textLowerCase.contains(searchedLowerCase)) {
10244                 outViews.add(this);
10245             }
10246         }
10247     }
10248
10249     /**
10250      * Type of the text buffer that defines the characteristics of the text such as static,
10251      * styleable, or editable.
10252      */
10253     public enum BufferType {
10254         NORMAL, SPANNABLE, EDITABLE
10255     }
10256
10257     /**
10258      * Returns the TextView_textColor attribute from the TypedArray, if set, or
10259      * the TextAppearance_textColor from the TextView_textAppearance attribute,
10260      * if TextView_textColor was not set directly.
10261      *
10262      * @removed
10263      */
10264     public static ColorStateList getTextColors(Context context, TypedArray attrs) {
10265         if (attrs == null) {
10266             // Preserve behavior prior to removal of this API.
10267             throw new NullPointerException();
10268         }
10269
10270         // It's not safe to use this method from apps. The parameter 'attrs'
10271         // must have been obtained using the TextView filter array which is not
10272         // available to the SDK. As such, we grab a default TypedArray with the
10273         // right filter instead here.
10274         final TypedArray a = context.obtainStyledAttributes(R.styleable.TextView);
10275         ColorStateList colors = a.getColorStateList(R.styleable.TextView_textColor);
10276         if (colors == null) {
10277             final int ap = a.getResourceId(R.styleable.TextView_textAppearance, 0);
10278             if (ap != 0) {
10279                 final TypedArray appearance = context.obtainStyledAttributes(
10280                         ap, R.styleable.TextAppearance);
10281                 colors = appearance.getColorStateList(R.styleable.TextAppearance_textColor);
10282                 appearance.recycle();
10283             }
10284         }
10285         a.recycle();
10286
10287         return colors;
10288     }
10289
10290     /**
10291      * Returns the default color from the TextView_textColor attribute from the
10292      * AttributeSet, if set, or the default color from the
10293      * TextAppearance_textColor from the TextView_textAppearance attribute, if
10294      * TextView_textColor was not set directly.
10295      *
10296      * @removed
10297      */
10298     public static int getTextColor(Context context, TypedArray attrs, int def) {
10299         final ColorStateList colors = getTextColors(context, attrs);
10300         if (colors == null) {
10301             return def;
10302         } else {
10303             return colors.getDefaultColor();
10304         }
10305     }
10306
10307     @Override
10308     public boolean onKeyShortcut(int keyCode, KeyEvent event) {
10309         if (event.hasModifiers(KeyEvent.META_CTRL_ON)) {
10310             // Handle Ctrl-only shortcuts.
10311             switch (keyCode) {
10312                 case KeyEvent.KEYCODE_A:
10313                     if (canSelectText()) {
10314                         return onTextContextMenuItem(ID_SELECT_ALL);
10315                     }
10316                     break;
10317                 case KeyEvent.KEYCODE_Z:
10318                     if (canUndo()) {
10319                         return onTextContextMenuItem(ID_UNDO);
10320                     }
10321                     break;
10322                 case KeyEvent.KEYCODE_X:
10323                     if (canCut()) {
10324                         return onTextContextMenuItem(ID_CUT);
10325                     }
10326                     break;
10327                 case KeyEvent.KEYCODE_C:
10328                     if (canCopy()) {
10329                         return onTextContextMenuItem(ID_COPY);
10330                     }
10331                     break;
10332                 case KeyEvent.KEYCODE_V:
10333                     if (canPaste()) {
10334                         return onTextContextMenuItem(ID_PASTE);
10335                     }
10336                     break;
10337             }
10338         } else if (event.hasModifiers(KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON)) {
10339             // Handle Ctrl-Shift shortcuts.
10340             switch (keyCode) {
10341                 case KeyEvent.KEYCODE_Z:
10342                     if (canRedo()) {
10343                         return onTextContextMenuItem(ID_REDO);
10344                     }
10345                     break;
10346                 case KeyEvent.KEYCODE_V:
10347                     if (canPaste()) {
10348                         return onTextContextMenuItem(ID_PASTE_AS_PLAIN_TEXT);
10349                     }
10350             }
10351         }
10352         return super.onKeyShortcut(keyCode, event);
10353     }
10354
10355     /**
10356      * Unlike {@link #textCanBeSelected()}, this method is based on the <i>current</i> state of the
10357      * TextView. {@link #textCanBeSelected()} has to be true (this is one of the conditions to have
10358      * a selection controller (see {@link Editor#prepareCursorControllers()}), but this is not
10359      * sufficient.
10360      */
10361     boolean canSelectText() {
10362         return mText.length() != 0 && mEditor != null && mEditor.hasSelectionController();
10363     }
10364
10365     /**
10366      * Test based on the <i>intrinsic</i> charateristics of the TextView.
10367      * The text must be spannable and the movement method must allow for arbitary selection.
10368      *
10369      * See also {@link #canSelectText()}.
10370      */
10371     boolean textCanBeSelected() {
10372         // prepareCursorController() relies on this method.
10373         // If you change this condition, make sure prepareCursorController is called anywhere
10374         // the value of this condition might be changed.
10375         if (mMovement == null || !mMovement.canSelectArbitrarily()) return false;
10376         return isTextEditable()
10377                 || (isTextSelectable() && mText instanceof Spannable && isEnabled());
10378     }
10379
10380     private Locale getTextServicesLocale(boolean allowNullLocale) {
10381         // Start fetching the text services locale asynchronously.
10382         updateTextServicesLocaleAsync();
10383         // If !allowNullLocale and there is no cached text services locale, just return the default
10384         // locale.
10385         return (mCurrentSpellCheckerLocaleCache == null && !allowNullLocale) ? Locale.getDefault()
10386                 : mCurrentSpellCheckerLocaleCache;
10387     }
10388
10389     /**
10390      * This is a temporary method. Future versions may support multi-locale text.
10391      * Caveat: This method may not return the latest text services locale, but this should be
10392      * acceptable and it's more important to make this method asynchronous.
10393      *
10394      * @return The locale that should be used for a word iterator
10395      * in this TextView, based on the current spell checker settings,
10396      * the current IME's locale, or the system default locale.
10397      * Please note that a word iterator in this TextView is different from another word iterator
10398      * used by SpellChecker.java of TextView. This method should be used for the former.
10399      * @hide
10400      */
10401     // TODO: Support multi-locale
10402     // TODO: Update the text services locale immediately after the keyboard locale is switched
10403     // by catching intent of keyboard switch event
10404     public Locale getTextServicesLocale() {
10405         return getTextServicesLocale(false /* allowNullLocale */);
10406     }
10407
10408     /**
10409      * @return {@code true} if this TextView is specialized for showing and interacting with the
10410      * extracted text in a full-screen input method.
10411      * @hide
10412      */
10413     public boolean isInExtractedMode() {
10414         return false;
10415     }
10416
10417     /**
10418      * @return {@code true} if this widget supports auto-sizing text and has been configured to
10419      * auto-size.
10420      */
10421     private boolean isAutoSizeEnabled() {
10422         return supportsAutoSizeText() && mAutoSizeTextType != AUTO_SIZE_TEXT_TYPE_NONE;
10423     }
10424
10425     /**
10426      * @return {@code true} if this TextView supports auto-sizing text to fit within its container.
10427      * @hide
10428      */
10429     protected boolean supportsAutoSizeText() {
10430         return true;
10431     }
10432
10433     /**
10434      * This is a temporary method. Future versions may support multi-locale text.
10435      * Caveat: This method may not return the latest spell checker locale, but this should be
10436      * acceptable and it's more important to make this method asynchronous.
10437      *
10438      * @return The locale that should be used for a spell checker in this TextView,
10439      * based on the current spell checker settings, the current IME's locale, or the system default
10440      * locale.
10441      * @hide
10442      */
10443     public Locale getSpellCheckerLocale() {
10444         return getTextServicesLocale(true /* allowNullLocale */);
10445     }
10446
10447     private void updateTextServicesLocaleAsync() {
10448         // AsyncTask.execute() uses a serial executor which means we don't have
10449         // to lock around updateTextServicesLocaleLocked() to prevent it from
10450         // being executed n times in parallel.
10451         AsyncTask.execute(new Runnable() {
10452             @Override
10453             public void run() {
10454                 updateTextServicesLocaleLocked();
10455             }
10456         });
10457     }
10458
10459     private void updateTextServicesLocaleLocked() {
10460         final TextServicesManager textServicesManager = (TextServicesManager)
10461                 mContext.getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE);
10462         final SpellCheckerSubtype subtype = textServicesManager.getCurrentSpellCheckerSubtype(true);
10463         final Locale locale;
10464         if (subtype != null) {
10465             locale = subtype.getLocaleObject();
10466         } else {
10467             locale = null;
10468         }
10469         mCurrentSpellCheckerLocaleCache = locale;
10470     }
10471
10472     void onLocaleChanged() {
10473         mEditor.onLocaleChanged();
10474     }
10475
10476     /**
10477      * This method is used by the ArrowKeyMovementMethod to jump from one word to the other.
10478      * Made available to achieve a consistent behavior.
10479      * @hide
10480      */
10481     public WordIterator getWordIterator() {
10482         if (mEditor != null) {
10483             return mEditor.getWordIterator();
10484         } else {
10485             return null;
10486         }
10487     }
10488
10489     /** @hide */
10490     @Override
10491     public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) {
10492         super.onPopulateAccessibilityEventInternal(event);
10493
10494         final CharSequence text = getTextForAccessibility();
10495         if (!TextUtils.isEmpty(text)) {
10496             event.getText().add(text);
10497         }
10498     }
10499
10500     @Override
10501     public CharSequence getAccessibilityClassName() {
10502         return TextView.class.getName();
10503     }
10504
10505     @Override
10506     public void onProvideStructure(ViewStructure structure) {
10507         super.onProvideStructure(structure);
10508         onProvideAutoStructureForAssistOrAutofill(structure, false);
10509     }
10510
10511     @Override
10512     public void onProvideAutofillStructure(ViewStructure structure, int flags) {
10513         super.onProvideAutofillStructure(structure, flags);
10514         onProvideAutoStructureForAssistOrAutofill(structure, true);
10515     }
10516
10517     private void onProvideAutoStructureForAssistOrAutofill(ViewStructure structure,
10518             boolean forAutofill) {
10519         final boolean isPassword = hasPasswordTransformationMethod()
10520                 || isPasswordInputType(getInputType());
10521         if (forAutofill) {
10522             structure.setDataIsSensitive(!mTextSetFromXmlOrResourceId);
10523             if (mTextId != ResourceId.ID_NULL) {
10524                 try {
10525                     structure.setTextIdEntry(getResources().getResourceEntryName(mTextId));
10526                 } catch (Resources.NotFoundException e) {
10527                     if (android.view.autofill.Helper.sVerbose) {
10528                         Log.v(LOG_TAG, "onProvideAutofillStructure(): cannot set name for text id "
10529                                 + mTextId + ": " + e.getMessage());
10530                     }
10531                 }
10532             }
10533         }
10534
10535         if (!isPassword || forAutofill) {
10536             if (mLayout == null) {
10537                 assumeLayout();
10538             }
10539             Layout layout = mLayout;
10540             final int lineCount = layout.getLineCount();
10541             if (lineCount <= 1) {
10542                 // Simple case: this is a single line.
10543                 final CharSequence text = getText();
10544                 if (forAutofill) {
10545                     structure.setText(text);
10546                 } else {
10547                     structure.setText(text, getSelectionStart(), getSelectionEnd());
10548                 }
10549             } else {
10550                 // Complex case: multi-line, could be scrolled or within a scroll container
10551                 // so some lines are not visible.
10552                 final int[] tmpCords = new int[2];
10553                 getLocationInWindow(tmpCords);
10554                 final int topWindowLocation = tmpCords[1];
10555                 View root = this;
10556                 ViewParent viewParent = getParent();
10557                 while (viewParent instanceof View) {
10558                     root = (View) viewParent;
10559                     viewParent = root.getParent();
10560                 }
10561                 final int windowHeight = root.getHeight();
10562                 final int topLine;
10563                 final int bottomLine;
10564                 if (topWindowLocation >= 0) {
10565                     // The top of the view is fully within its window; start text at line 0.
10566                     topLine = getLineAtCoordinateUnclamped(0);
10567                     bottomLine = getLineAtCoordinateUnclamped(windowHeight - 1);
10568                 } else {
10569                     // The top of hte window has scrolled off the top of the window; figure out
10570                     // the starting line for this.
10571                     topLine = getLineAtCoordinateUnclamped(-topWindowLocation);
10572                     bottomLine = getLineAtCoordinateUnclamped(windowHeight - 1 - topWindowLocation);
10573                 }
10574                 // We want to return some contextual lines above/below the lines that are
10575                 // actually visible.
10576                 int expandedTopLine = topLine - (bottomLine - topLine) / 2;
10577                 if (expandedTopLine < 0) {
10578                     expandedTopLine = 0;
10579                 }
10580                 int expandedBottomLine = bottomLine + (bottomLine - topLine) / 2;
10581                 if (expandedBottomLine >= lineCount) {
10582                     expandedBottomLine = lineCount - 1;
10583                 }
10584
10585                 // Convert lines into character offsets.
10586                 int expandedTopChar = layout.getLineStart(expandedTopLine);
10587                 int expandedBottomChar = layout.getLineEnd(expandedBottomLine);
10588
10589                 // Take into account selection -- if there is a selection, we need to expand
10590                 // the text we are returning to include that selection.
10591                 final int selStart = getSelectionStart();
10592                 final int selEnd = getSelectionEnd();
10593                 if (selStart < selEnd) {
10594                     if (selStart < expandedTopChar) {
10595                         expandedTopChar = selStart;
10596                     }
10597                     if (selEnd > expandedBottomChar) {
10598                         expandedBottomChar = selEnd;
10599                     }
10600                 }
10601
10602                 // Get the text and trim it to the range we are reporting.
10603                 CharSequence text = getText();
10604                 if (expandedTopChar > 0 || expandedBottomChar < text.length()) {
10605                     text = text.subSequence(expandedTopChar, expandedBottomChar);
10606                 }
10607
10608                 if (forAutofill) {
10609                     structure.setText(text);
10610                 } else {
10611                     structure.setText(text, selStart - expandedTopChar, selEnd - expandedTopChar);
10612
10613                     final int[] lineOffsets = new int[bottomLine - topLine + 1];
10614                     final int[] lineBaselines = new int[bottomLine - topLine + 1];
10615                     final int baselineOffset = getBaselineOffset();
10616                     for (int i = topLine; i <= bottomLine; i++) {
10617                         lineOffsets[i - topLine] = layout.getLineStart(i);
10618                         lineBaselines[i - topLine] = layout.getLineBaseline(i) + baselineOffset;
10619                     }
10620                     structure.setTextLines(lineOffsets, lineBaselines);
10621                 }
10622             }
10623
10624             if (!forAutofill) {
10625                 // Extract style information that applies to the TextView as a whole.
10626                 int style = 0;
10627                 int typefaceStyle = getTypefaceStyle();
10628                 if ((typefaceStyle & Typeface.BOLD) != 0) {
10629                     style |= AssistStructure.ViewNode.TEXT_STYLE_BOLD;
10630                 }
10631                 if ((typefaceStyle & Typeface.ITALIC) != 0) {
10632                     style |= AssistStructure.ViewNode.TEXT_STYLE_ITALIC;
10633                 }
10634
10635                 // Global styles can also be set via TextView.setPaintFlags().
10636                 int paintFlags = mTextPaint.getFlags();
10637                 if ((paintFlags & Paint.FAKE_BOLD_TEXT_FLAG) != 0) {
10638                     style |= AssistStructure.ViewNode.TEXT_STYLE_BOLD;
10639                 }
10640                 if ((paintFlags & Paint.UNDERLINE_TEXT_FLAG) != 0) {
10641                     style |= AssistStructure.ViewNode.TEXT_STYLE_UNDERLINE;
10642                 }
10643                 if ((paintFlags & Paint.STRIKE_THRU_TEXT_FLAG) != 0) {
10644                     style |= AssistStructure.ViewNode.TEXT_STYLE_STRIKE_THRU;
10645                 }
10646
10647                 // TextView does not have its own text background color. A background is either part
10648                 // of the View (and can be any drawable) or a BackgroundColorSpan inside the text.
10649                 structure.setTextStyle(getTextSize(), getCurrentTextColor(),
10650                         AssistStructure.ViewNode.TEXT_COLOR_UNDEFINED /* bgColor */, style);
10651             } else {
10652                 structure.setMinTextEms(getMinEms());
10653                 structure.setMaxTextEms(getMaxEms());
10654                 int maxLength = -1;
10655                 for (InputFilter filter: getFilters()) {
10656                     if (filter instanceof InputFilter.LengthFilter) {
10657                         maxLength = ((InputFilter.LengthFilter) filter).getMax();
10658                         break;
10659                     }
10660                 }
10661                 structure.setMaxTextLength(maxLength);
10662             }
10663         }
10664         structure.setHint(getHint());
10665         structure.setInputType(getInputType());
10666     }
10667
10668     boolean canRequestAutofill() {
10669         if (!isAutofillable()) {
10670             return false;
10671         }
10672         final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
10673         if (afm != null) {
10674             return afm.isEnabled();
10675         }
10676         return false;
10677     }
10678
10679     private void requestAutofill() {
10680         final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
10681         if (afm != null) {
10682             afm.requestAutofill(this);
10683         }
10684     }
10685
10686     @Override
10687     public void autofill(AutofillValue value) {
10688         if (!value.isText() || !isTextEditable()) {
10689             Log.w(LOG_TAG, value + " could not be autofilled into " + this);
10690             return;
10691         }
10692
10693         final CharSequence autofilledValue = value.getTextValue();
10694
10695         // First autofill it...
10696         setText(autofilledValue, mBufferType, true, 0);
10697
10698         // ...then move cursor to the end.
10699         final CharSequence text = getText();
10700         if ((text instanceof Spannable)) {
10701             Selection.setSelection((Spannable) text, text.length());
10702         }
10703     }
10704
10705     @Override
10706     public @AutofillType int getAutofillType() {
10707         return isTextEditable() ? AUTOFILL_TYPE_TEXT : AUTOFILL_TYPE_NONE;
10708     }
10709
10710     /**
10711      * Gets the {@link TextView}'s current text for AutoFill. The value is trimmed to 100K
10712      * {@code char}s if longer.
10713      *
10714      * @return current text, {@code null} if the text is not editable
10715      *
10716      * @see View#getAutofillValue()
10717      */
10718     @Override
10719     @Nullable
10720     public AutofillValue getAutofillValue() {
10721         if (isTextEditable()) {
10722             final CharSequence text = TextUtils.trimToParcelableSize(getText());
10723             return AutofillValue.forText(text);
10724         }
10725         return null;
10726     }
10727
10728     /** @hide */
10729     @Override
10730     public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
10731         super.onInitializeAccessibilityEventInternal(event);
10732
10733         final boolean isPassword = hasPasswordTransformationMethod();
10734         event.setPassword(isPassword);
10735
10736         if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED) {
10737             event.setFromIndex(Selection.getSelectionStart(mText));
10738             event.setToIndex(Selection.getSelectionEnd(mText));
10739             event.setItemCount(mText.length());
10740         }
10741     }
10742
10743     /** @hide */
10744     @Override
10745     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
10746         super.onInitializeAccessibilityNodeInfoInternal(info);
10747
10748         final boolean isPassword = hasPasswordTransformationMethod();
10749         info.setPassword(isPassword);
10750         info.setText(getTextForAccessibility());
10751         info.setHintText(mHint);
10752         info.setShowingHintText(isShowingHint());
10753         info.setHeading(mIsAccessibilityHeading);
10754
10755         if (mBufferType == BufferType.EDITABLE) {
10756             info.setEditable(true);
10757             if (isEnabled()) {
10758                 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_TEXT);
10759             }
10760         }
10761
10762         if (mEditor != null) {
10763             info.setInputType(mEditor.mInputType);
10764
10765             if (mEditor.mError != null) {
10766                 info.setContentInvalid(true);
10767                 info.setError(mEditor.mError);
10768             }
10769         }
10770
10771         if (!TextUtils.isEmpty(mText)) {
10772             info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
10773             info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
10774             info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
10775                     | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
10776                     | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
10777                     | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
10778                     | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
10779             info.addAction(AccessibilityNodeInfo.ACTION_SET_SELECTION);
10780             info.setAvailableExtraData(
10781                     Arrays.asList(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY));
10782         }
10783
10784         if (isFocused()) {
10785             if (canCopy()) {
10786                 info.addAction(AccessibilityNodeInfo.ACTION_COPY);
10787             }
10788             if (canPaste()) {
10789                 info.addAction(AccessibilityNodeInfo.ACTION_PASTE);
10790             }
10791             if (canCut()) {
10792                 info.addAction(AccessibilityNodeInfo.ACTION_CUT);
10793             }
10794             if (canShare()) {
10795                 info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
10796                         ACCESSIBILITY_ACTION_SHARE,
10797                         getResources().getString(com.android.internal.R.string.share)));
10798             }
10799             if (canProcessText()) {  // also implies mEditor is not null.
10800                 mEditor.mProcessTextIntentActionsHandler.onInitializeAccessibilityNodeInfo(info);
10801             }
10802         }
10803
10804         // Check for known input filter types.
10805         final int numFilters = mFilters.length;
10806         for (int i = 0; i < numFilters; i++) {
10807             final InputFilter filter = mFilters[i];
10808             if (filter instanceof InputFilter.LengthFilter) {
10809                 info.setMaxTextLength(((InputFilter.LengthFilter) filter).getMax());
10810             }
10811         }
10812
10813         if (!isSingleLine()) {
10814             info.setMultiLine(true);
10815         }
10816     }
10817
10818     @Override
10819     public void addExtraDataToAccessibilityNodeInfo(
10820             AccessibilityNodeInfo info, String extraDataKey, Bundle arguments) {
10821         // The only extra data we support requires arguments.
10822         if (arguments == null) {
10823             return;
10824         }
10825         if (extraDataKey.equals(EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY)) {
10826             int positionInfoStartIndex = arguments.getInt(
10827                     EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX, -1);
10828             int positionInfoLength = arguments.getInt(
10829                     EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH, -1);
10830             if ((positionInfoLength <= 0) || (positionInfoStartIndex < 0)
10831                     || (positionInfoStartIndex >= mText.length())) {
10832                 Log.e(LOG_TAG, "Invalid arguments for accessibility character locations");
10833                 return;
10834             }
10835             RectF[] boundingRects = new RectF[positionInfoLength];
10836             final CursorAnchorInfo.Builder builder = new CursorAnchorInfo.Builder();
10837             populateCharacterBounds(builder, positionInfoStartIndex,
10838                     positionInfoStartIndex + positionInfoLength,
10839                     viewportToContentHorizontalOffset(), viewportToContentVerticalOffset());
10840             CursorAnchorInfo cursorAnchorInfo = builder.setMatrix(null).build();
10841             for (int i = 0; i < positionInfoLength; i++) {
10842                 int flags = cursorAnchorInfo.getCharacterBoundsFlags(positionInfoStartIndex + i);
10843                 if ((flags & FLAG_HAS_VISIBLE_REGION) == FLAG_HAS_VISIBLE_REGION) {
10844                     RectF bounds = cursorAnchorInfo
10845                             .getCharacterBounds(positionInfoStartIndex + i);
10846                     if (bounds != null) {
10847                         mapRectFromViewToScreenCoords(bounds, true);
10848                         boundingRects[i] = bounds;
10849                     }
10850                 }
10851             }
10852             info.getExtras().putParcelableArray(extraDataKey, boundingRects);
10853         }
10854     }
10855
10856     /**
10857      * Populate requested character bounds in a {@link CursorAnchorInfo.Builder}
10858      *
10859      * @param builder The builder to populate
10860      * @param startIndex The starting character index to populate
10861      * @param endIndex The ending character index to populate
10862      * @param viewportToContentHorizontalOffset The horizontal offset from the viewport to the
10863      * content
10864      * @param viewportToContentVerticalOffset The vertical offset from the viewport to the content
10865      * @hide
10866      */
10867     public void populateCharacterBounds(CursorAnchorInfo.Builder builder,
10868             int startIndex, int endIndex, float viewportToContentHorizontalOffset,
10869             float viewportToContentVerticalOffset) {
10870         final int minLine = mLayout.getLineForOffset(startIndex);
10871         final int maxLine = mLayout.getLineForOffset(endIndex - 1);
10872         for (int line = minLine; line <= maxLine; ++line) {
10873             final int lineStart = mLayout.getLineStart(line);
10874             final int lineEnd = mLayout.getLineEnd(line);
10875             final int offsetStart = Math.max(lineStart, startIndex);
10876             final int offsetEnd = Math.min(lineEnd, endIndex);
10877             final boolean ltrLine =
10878                     mLayout.getParagraphDirection(line) == Layout.DIR_LEFT_TO_RIGHT;
10879             final float[] widths = new float[offsetEnd - offsetStart];
10880             mLayout.getPaint().getTextWidths(mTransformed, offsetStart, offsetEnd, widths);
10881             final float top = mLayout.getLineTop(line);
10882             final float bottom = mLayout.getLineBottom(line);
10883             for (int offset = offsetStart; offset < offsetEnd; ++offset) {
10884                 final float charWidth = widths[offset - offsetStart];
10885                 final boolean isRtl = mLayout.isRtlCharAt(offset);
10886                 final float primary = mLayout.getPrimaryHorizontal(offset);
10887                 final float secondary = mLayout.getSecondaryHorizontal(offset);
10888                 // TODO: This doesn't work perfectly for text with custom styles and
10889                 // TAB chars.
10890                 final float left;
10891                 final float right;
10892                 if (ltrLine) {
10893                     if (isRtl) {
10894                         left = secondary - charWidth;
10895                         right = secondary;
10896                     } else {
10897                         left = primary;
10898                         right = primary + charWidth;
10899                     }
10900                 } else {
10901                     if (!isRtl) {
10902                         left = secondary;
10903                         right = secondary + charWidth;
10904                     } else {
10905                         left = primary - charWidth;
10906                         right = primary;
10907                     }
10908                 }
10909                 // TODO: Check top-right and bottom-left as well.
10910                 final float localLeft = left + viewportToContentHorizontalOffset;
10911                 final float localRight = right + viewportToContentHorizontalOffset;
10912                 final float localTop = top + viewportToContentVerticalOffset;
10913                 final float localBottom = bottom + viewportToContentVerticalOffset;
10914                 final boolean isTopLeftVisible = isPositionVisible(localLeft, localTop);
10915                 final boolean isBottomRightVisible =
10916                         isPositionVisible(localRight, localBottom);
10917                 int characterBoundsFlags = 0;
10918                 if (isTopLeftVisible || isBottomRightVisible) {
10919                     characterBoundsFlags |= FLAG_HAS_VISIBLE_REGION;
10920                 }
10921                 if (!isTopLeftVisible || !isBottomRightVisible) {
10922                     characterBoundsFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
10923                 }
10924                 if (isRtl) {
10925                     characterBoundsFlags |= CursorAnchorInfo.FLAG_IS_RTL;
10926                 }
10927                 // Here offset is the index in Java chars.
10928                 builder.addCharacterBounds(offset, localLeft, localTop, localRight,
10929                         localBottom, characterBoundsFlags);
10930             }
10931         }
10932     }
10933
10934     /**
10935      * @hide
10936      */
10937     public boolean isPositionVisible(final float positionX, final float positionY) {
10938         synchronized (TEMP_POSITION) {
10939             final float[] position = TEMP_POSITION;
10940             position[0] = positionX;
10941             position[1] = positionY;
10942             View view = this;
10943
10944             while (view != null) {
10945                 if (view != this) {
10946                     // Local scroll is already taken into account in positionX/Y
10947                     position[0] -= view.getScrollX();
10948                     position[1] -= view.getScrollY();
10949                 }
10950
10951                 if (position[0] < 0 || position[1] < 0 || position[0] > view.getWidth()
10952                         || position[1] > view.getHeight()) {
10953                     return false;
10954                 }
10955
10956                 if (!view.getMatrix().isIdentity()) {
10957                     view.getMatrix().mapPoints(position);
10958                 }
10959
10960                 position[0] += view.getLeft();
10961                 position[1] += view.getTop();
10962
10963                 final ViewParent parent = view.getParent();
10964                 if (parent instanceof View) {
10965                     view = (View) parent;
10966                 } else {
10967                     // We've reached the ViewRoot, stop iterating
10968                     view = null;
10969                 }
10970             }
10971         }
10972
10973         // We've been able to walk up the view hierarchy and the position was never clipped
10974         return true;
10975     }
10976
10977     /**
10978      * Performs an accessibility action after it has been offered to the
10979      * delegate.
10980      *
10981      * @hide
10982      */
10983     @Override
10984     public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
10985         if (mEditor != null
10986                 && mEditor.mProcessTextIntentActionsHandler.performAccessibilityAction(action)) {
10987             return true;
10988         }
10989         switch (action) {
10990             case AccessibilityNodeInfo.ACTION_CLICK: {
10991                 return performAccessibilityActionClick(arguments);
10992             }
10993             case AccessibilityNodeInfo.ACTION_COPY: {
10994                 if (isFocused() && canCopy()) {
10995                     if (onTextContextMenuItem(ID_COPY)) {
10996                         return true;
10997                     }
10998                 }
10999             } return false;
11000             case AccessibilityNodeInfo.ACTION_PASTE: {
11001                 if (isFocused() && canPaste()) {
11002                     if (onTextContextMenuItem(ID_PASTE)) {
11003                         return true;
11004                     }
11005                 }
11006             } return false;
11007             case AccessibilityNodeInfo.ACTION_CUT: {
11008                 if (isFocused() && canCut()) {
11009                     if (onTextContextMenuItem(ID_CUT)) {
11010                         return true;
11011                     }
11012                 }
11013             } return false;
11014             case AccessibilityNodeInfo.ACTION_SET_SELECTION: {
11015                 ensureIterableTextForAccessibilitySelectable();
11016                 CharSequence text = getIterableTextForAccessibility();
11017                 if (text == null) {
11018                     return false;
11019                 }
11020                 final int start = (arguments != null) ? arguments.getInt(
11021                         AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, -1) : -1;
11022                 final int end = (arguments != null) ? arguments.getInt(
11023                         AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, -1) : -1;
11024                 if ((getSelectionStart() != start || getSelectionEnd() != end)) {
11025                     // No arguments clears the selection.
11026                     if (start == end && end == -1) {
11027                         Selection.removeSelection((Spannable) text);
11028                         return true;
11029                     }
11030                     if (start >= 0 && start <= end && end <= text.length()) {
11031                         Selection.setSelection((Spannable) text, start, end);
11032                         // Make sure selection mode is engaged.
11033                         if (mEditor != null) {
11034                             mEditor.startSelectionActionModeAsync(false);
11035                         }
11036                         return true;
11037                     }
11038                 }
11039             } return false;
11040             case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
11041             case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: {
11042                 ensureIterableTextForAccessibilitySelectable();
11043                 return super.performAccessibilityActionInternal(action, arguments);
11044             }
11045             case ACCESSIBILITY_ACTION_SHARE: {
11046                 if (isFocused() && canShare()) {
11047                     if (onTextContextMenuItem(ID_SHARE)) {
11048                         return true;
11049                     }
11050                 }
11051             } return false;
11052             case AccessibilityNodeInfo.ACTION_SET_TEXT: {
11053                 if (!isEnabled() || (mBufferType != BufferType.EDITABLE)) {
11054                     return false;
11055                 }
11056                 CharSequence text = (arguments != null) ? arguments.getCharSequence(
11057                         AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE) : null;
11058                 setText(text);
11059                 if (mText != null) {
11060                     int updatedTextLength = mText.length();
11061                     if (updatedTextLength > 0) {
11062                         Selection.setSelection((Spannable) mText, updatedTextLength);
11063                     }
11064                 }
11065             } return true;
11066             default: {
11067                 return super.performAccessibilityActionInternal(action, arguments);
11068             }
11069         }
11070     }
11071
11072     private boolean performAccessibilityActionClick(Bundle arguments) {
11073         boolean handled = false;
11074
11075         if (!isEnabled()) {
11076             return false;
11077         }
11078
11079         if (isClickable() || isLongClickable()) {
11080             // Simulate View.onTouchEvent for an ACTION_UP event
11081             if (isFocusable() && !isFocused()) {
11082                 requestFocus();
11083             }
11084
11085             performClick();
11086             handled = true;
11087         }
11088
11089         // Show the IME, except when selecting in read-only text.
11090         if ((mMovement != null || onCheckIsTextEditor()) && hasSpannableText() && mLayout != null
11091                 && (isTextEditable() || isTextSelectable()) && isFocused()) {
11092             final InputMethodManager imm = InputMethodManager.peekInstance();
11093             viewClicked(imm);
11094             if (!isTextSelectable() && mEditor.mShowSoftInputOnFocus && imm != null) {
11095                 handled |= imm.showSoftInput(this, 0);
11096             }
11097         }
11098
11099         return handled;
11100     }
11101
11102     private boolean hasSpannableText() {
11103         return mText != null && mText instanceof Spannable;
11104     }
11105
11106     /** @hide */
11107     @Override
11108     public void sendAccessibilityEventInternal(int eventType) {
11109         if (eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED && mEditor != null) {
11110             mEditor.mProcessTextIntentActionsHandler.initializeAccessibilityActions();
11111         }
11112
11113         super.sendAccessibilityEventInternal(eventType);
11114     }
11115
11116     @Override
11117     public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
11118         // Do not send scroll events since first they are not interesting for
11119         // accessibility and second such events a generated too frequently.
11120         // For details see the implementation of bringTextIntoView().
11121         if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
11122             return;
11123         }
11124         super.sendAccessibilityEventUnchecked(event);
11125     }
11126
11127     /**
11128      * Returns the text that should be exposed to accessibility services.
11129      * <p>
11130      * This approximates what is displayed visually. If the user has specified
11131      * that accessibility services should speak passwords, this method will
11132      * bypass any password transformation method and return unobscured text.
11133      *
11134      * @return the text that should be exposed to accessibility services, may
11135      *         be {@code null} if no text is set
11136      */
11137     @Nullable
11138     private CharSequence getTextForAccessibility() {
11139         // If the text is empty, we must be showing the hint text.
11140         if (TextUtils.isEmpty(mText)) {
11141             return mHint;
11142         }
11143
11144         // Otherwise, return whatever text is being displayed.
11145         return TextUtils.trimToParcelableSize(mTransformed);
11146     }
11147
11148     void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText,
11149             int fromIndex, int removedCount, int addedCount) {
11150         AccessibilityEvent event =
11151                 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
11152         event.setFromIndex(fromIndex);
11153         event.setRemovedCount(removedCount);
11154         event.setAddedCount(addedCount);
11155         event.setBeforeText(beforeText);
11156         sendAccessibilityEventUnchecked(event);
11157     }
11158
11159     /**
11160      * Returns whether this text view is a current input method target.  The
11161      * default implementation just checks with {@link InputMethodManager}.
11162      * @return True if the TextView is a current input method target; false otherwise.
11163      */
11164     public boolean isInputMethodTarget() {
11165         InputMethodManager imm = InputMethodManager.peekInstance();
11166         return imm != null && imm.isActive(this);
11167     }
11168
11169     static final int ID_SELECT_ALL = android.R.id.selectAll;
11170     static final int ID_UNDO = android.R.id.undo;
11171     static final int ID_REDO = android.R.id.redo;
11172     static final int ID_CUT = android.R.id.cut;
11173     static final int ID_COPY = android.R.id.copy;
11174     static final int ID_PASTE = android.R.id.paste;
11175     static final int ID_SHARE = android.R.id.shareText;
11176     static final int ID_PASTE_AS_PLAIN_TEXT = android.R.id.pasteAsPlainText;
11177     static final int ID_REPLACE = android.R.id.replaceText;
11178     static final int ID_ASSIST = android.R.id.textAssist;
11179     static final int ID_AUTOFILL = android.R.id.autofill;
11180
11181     /**
11182      * Called when a context menu option for the text view is selected.  Currently
11183      * this will be one of {@link android.R.id#selectAll}, {@link android.R.id#cut},
11184      * {@link android.R.id#copy}, {@link android.R.id#paste} or {@link android.R.id#shareText}.
11185      *
11186      * @return true if the context menu item action was performed.
11187      */
11188     public boolean onTextContextMenuItem(int id) {
11189         int min = 0;
11190         int max = mText.length();
11191
11192         if (isFocused()) {
11193             final int selStart = getSelectionStart();
11194             final int selEnd = getSelectionEnd();
11195
11196             min = Math.max(0, Math.min(selStart, selEnd));
11197             max = Math.max(0, Math.max(selStart, selEnd));
11198         }
11199
11200         switch (id) {
11201             case ID_SELECT_ALL:
11202                 final boolean hadSelection = hasSelection();
11203                 selectAllText();
11204                 if (mEditor != null && hadSelection) {
11205                     mEditor.invalidateActionModeAsync();
11206                 }
11207                 return true;
11208
11209             case ID_UNDO:
11210                 if (mEditor != null) {
11211                     mEditor.undo();
11212                 }
11213                 return true;  // Returns true even if nothing was undone.
11214
11215             case ID_REDO:
11216                 if (mEditor != null) {
11217                     mEditor.redo();
11218                 }
11219                 return true;  // Returns true even if nothing was undone.
11220
11221             case ID_PASTE:
11222                 paste(min, max, true /* withFormatting */);
11223                 return true;
11224
11225             case ID_PASTE_AS_PLAIN_TEXT:
11226                 paste(min, max, false /* withFormatting */);
11227                 return true;
11228
11229             case ID_CUT:
11230                 final ClipData cutData = ClipData.newPlainText(null, getTransformedText(min, max));
11231                 if (setPrimaryClip(cutData)) {
11232                     deleteText_internal(min, max);
11233                 } else {
11234                     Toast.makeText(getContext(),
11235                             com.android.internal.R.string.failed_to_copy_to_clipboard,
11236                             Toast.LENGTH_SHORT).show();
11237                 }
11238                 return true;
11239
11240             case ID_COPY:
11241                 // For link action mode in a non-selectable/non-focusable TextView,
11242                 // make sure that we set the appropriate min/max.
11243                 final int selStart = getSelectionStart();
11244                 final int selEnd = getSelectionEnd();
11245                 min = Math.max(0, Math.min(selStart, selEnd));
11246                 max = Math.max(0, Math.max(selStart, selEnd));
11247                 final ClipData copyData = ClipData.newPlainText(null, getTransformedText(min, max));
11248                 if (setPrimaryClip(copyData)) {
11249                     stopTextActionMode();
11250                 } else {
11251                     Toast.makeText(getContext(),
11252                             com.android.internal.R.string.failed_to_copy_to_clipboard,
11253                             Toast.LENGTH_SHORT).show();
11254                 }
11255                 return true;
11256
11257             case ID_REPLACE:
11258                 if (mEditor != null) {
11259                     mEditor.replace();
11260                 }
11261                 return true;
11262
11263             case ID_SHARE:
11264                 shareSelectedText();
11265                 return true;
11266
11267             case ID_AUTOFILL:
11268                 requestAutofill();
11269                 stopTextActionMode();
11270                 return true;
11271         }
11272         return false;
11273     }
11274
11275     CharSequence getTransformedText(int start, int end) {
11276         return removeSuggestionSpans(mTransformed.subSequence(start, end));
11277     }
11278
11279     @Override
11280     public boolean performLongClick() {
11281         boolean handled = false;
11282         boolean performedHapticFeedback = false;
11283
11284         if (mEditor != null) {
11285             mEditor.mIsBeingLongClicked = true;
11286         }
11287
11288         if (super.performLongClick()) {
11289             handled = true;
11290             performedHapticFeedback = true;
11291         }
11292
11293         if (mEditor != null) {
11294             handled |= mEditor.performLongClick(handled);
11295             mEditor.mIsBeingLongClicked = false;
11296         }
11297
11298         if (handled) {
11299             if (!performedHapticFeedback) {
11300               performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
11301             }
11302             if (mEditor != null) mEditor.mDiscardNextActionUp = true;
11303         } else {
11304             MetricsLogger.action(
11305                     mContext,
11306                     MetricsEvent.TEXT_LONGPRESS,
11307                     TextViewMetrics.SUBTYPE_LONG_PRESS_OTHER);
11308         }
11309
11310         return handled;
11311     }
11312
11313     @Override
11314     protected void onScrollChanged(int horiz, int vert, int oldHoriz, int oldVert) {
11315         super.onScrollChanged(horiz, vert, oldHoriz, oldVert);
11316         if (mEditor != null) {
11317             mEditor.onScrollChanged();
11318         }
11319     }
11320
11321     /**
11322      * Return whether or not suggestions are enabled on this TextView. The suggestions are generated
11323      * by the IME or by the spell checker as the user types. This is done by adding
11324      * {@link SuggestionSpan}s to the text.
11325      *
11326      * When suggestions are enabled (default), this list of suggestions will be displayed when the
11327      * user asks for them on these parts of the text. This value depends on the inputType of this
11328      * TextView.
11329      *
11330      * The class of the input type must be {@link InputType#TYPE_CLASS_TEXT}.
11331      *
11332      * In addition, the type variation must be one of
11333      * {@link InputType#TYPE_TEXT_VARIATION_NORMAL},
11334      * {@link InputType#TYPE_TEXT_VARIATION_EMAIL_SUBJECT},
11335      * {@link InputType#TYPE_TEXT_VARIATION_LONG_MESSAGE},
11336      * {@link InputType#TYPE_TEXT_VARIATION_SHORT_MESSAGE} or
11337      * {@link InputType#TYPE_TEXT_VARIATION_WEB_EDIT_TEXT}.
11338      *
11339      * And finally, the {@link InputType#TYPE_TEXT_FLAG_NO_SUGGESTIONS} flag must <i>not</i> be set.
11340      *
11341      * @return true if the suggestions popup window is enabled, based on the inputType.
11342      */
11343     public boolean isSuggestionsEnabled() {
11344         if (mEditor == null) return false;
11345         if ((mEditor.mInputType & InputType.TYPE_MASK_CLASS) != InputType.TYPE_CLASS_TEXT) {
11346             return false;
11347         }
11348         if ((mEditor.mInputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) > 0) return false;
11349
11350         final int variation = mEditor.mInputType & EditorInfo.TYPE_MASK_VARIATION;
11351         return (variation == EditorInfo.TYPE_TEXT_VARIATION_NORMAL
11352                 || variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_SUBJECT
11353                 || variation == EditorInfo.TYPE_TEXT_VARIATION_LONG_MESSAGE
11354                 || variation == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE
11355                 || variation == EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT);
11356     }
11357
11358     /**
11359      * If provided, this ActionMode.Callback will be used to create the ActionMode when text
11360      * selection is initiated in this View.
11361      *
11362      * <p>The standard implementation populates the menu with a subset of Select All, Cut, Copy,
11363      * Paste, Replace and Share actions, depending on what this View supports.
11364      *
11365      * <p>A custom implementation can add new entries in the default menu in its
11366      * {@link android.view.ActionMode.Callback#onPrepareActionMode(ActionMode, android.view.Menu)}
11367      * method. The default actions can also be removed from the menu using
11368      * {@link android.view.Menu#removeItem(int)} and passing {@link android.R.id#selectAll},
11369      * {@link android.R.id#cut}, {@link android.R.id#copy}, {@link android.R.id#paste},
11370      * {@link android.R.id#replaceText} or {@link android.R.id#shareText} ids as parameters.
11371      *
11372      * <p>Returning false from
11373      * {@link android.view.ActionMode.Callback#onCreateActionMode(ActionMode, android.view.Menu)}
11374      * will prevent the action mode from being started.
11375      *
11376      * <p>Action click events should be handled by the custom implementation of
11377      * {@link android.view.ActionMode.Callback#onActionItemClicked(ActionMode,
11378      * android.view.MenuItem)}.
11379      *
11380      * <p>Note that text selection mode is not started when a TextView receives focus and the
11381      * {@link android.R.attr#selectAllOnFocus} flag has been set. The content is highlighted in
11382      * that case, to allow for quick replacement.
11383      */
11384     public void setCustomSelectionActionModeCallback(ActionMode.Callback actionModeCallback) {
11385         createEditorIfNeeded();
11386         mEditor.mCustomSelectionActionModeCallback = actionModeCallback;
11387     }
11388
11389     /**
11390      * Retrieves the value set in {@link #setCustomSelectionActionModeCallback}. Default is null.
11391      *
11392      * @return The current custom selection callback.
11393      */
11394     public ActionMode.Callback getCustomSelectionActionModeCallback() {
11395         return mEditor == null ? null : mEditor.mCustomSelectionActionModeCallback;
11396     }
11397
11398     /**
11399      * If provided, this ActionMode.Callback will be used to create the ActionMode when text
11400      * insertion is initiated in this View.
11401      * The standard implementation populates the menu with a subset of Select All,
11402      * Paste and Replace actions, depending on what this View supports.
11403      *
11404      * <p>A custom implementation can add new entries in the default menu in its
11405      * {@link android.view.ActionMode.Callback#onPrepareActionMode(android.view.ActionMode,
11406      * android.view.Menu)} method. The default actions can also be removed from the menu using
11407      * {@link android.view.Menu#removeItem(int)} and passing {@link android.R.id#selectAll},
11408      * {@link android.R.id#paste} or {@link android.R.id#replaceText} ids as parameters.</p>
11409      *
11410      * <p>Returning false from
11411      * {@link android.view.ActionMode.Callback#onCreateActionMode(android.view.ActionMode,
11412      * android.view.Menu)} will prevent the action mode from being started.</p>
11413      *
11414      * <p>Action click events should be handled by the custom implementation of
11415      * {@link android.view.ActionMode.Callback#onActionItemClicked(android.view.ActionMode,
11416      * android.view.MenuItem)}.</p>
11417      *
11418      * <p>Note that text insertion mode is not started when a TextView receives focus and the
11419      * {@link android.R.attr#selectAllOnFocus} flag has been set.</p>
11420      */
11421     public void setCustomInsertionActionModeCallback(ActionMode.Callback actionModeCallback) {
11422         createEditorIfNeeded();
11423         mEditor.mCustomInsertionActionModeCallback = actionModeCallback;
11424     }
11425
11426     /**
11427      * Retrieves the value set in {@link #setCustomInsertionActionModeCallback}. Default is null.
11428      *
11429      * @return The current custom insertion callback.
11430      */
11431     public ActionMode.Callback getCustomInsertionActionModeCallback() {
11432         return mEditor == null ? null : mEditor.mCustomInsertionActionModeCallback;
11433     }
11434
11435     /**
11436      * Sets the {@link TextClassifier} for this TextView.
11437      */
11438     public void setTextClassifier(@Nullable TextClassifier textClassifier) {
11439         mTextClassifier = textClassifier;
11440     }
11441
11442     /**
11443      * Returns the {@link TextClassifier} used by this TextView.
11444      * If no TextClassifier has been set, this TextView uses the default set by the
11445      * {@link TextClassificationManager}.
11446      */
11447     @NonNull
11448     public TextClassifier getTextClassifier() {
11449         if (mTextClassifier == null) {
11450             TextClassificationManager tcm =
11451                     mContext.getSystemService(TextClassificationManager.class);
11452             if (tcm != null) {
11453                 mTextClassifier = tcm.getTextClassifier();
11454             } else {
11455                 mTextClassifier = TextClassifier.NO_OP;
11456             }
11457         }
11458         return mTextClassifier;
11459     }
11460
11461     /**
11462      * Starts an ActionMode for the specified TextLink.
11463      *
11464      * @return Whether or not we're attempting to start the action mode.
11465      * @hide
11466      */
11467     public boolean requestActionMode(@NonNull TextLinks.TextLink link) {
11468         Preconditions.checkNotNull(link);
11469         createEditorIfNeeded();
11470         mEditor.startLinkActionModeAsync(link);
11471         return true;
11472     }
11473     /**
11474      * @hide
11475      */
11476     protected void stopTextActionMode() {
11477         if (mEditor != null) {
11478             mEditor.stopTextActionMode();
11479         }
11480     }
11481
11482     boolean canUndo() {
11483         return mEditor != null && mEditor.canUndo();
11484     }
11485
11486     boolean canRedo() {
11487         return mEditor != null && mEditor.canRedo();
11488     }
11489
11490     boolean canCut() {
11491         if (hasPasswordTransformationMethod()) {
11492             return false;
11493         }
11494
11495         if (mText.length() > 0 && hasSelection() && mText instanceof Editable && mEditor != null
11496                 && mEditor.mKeyListener != null) {
11497             return true;
11498         }
11499
11500         return false;
11501     }
11502
11503     boolean canCopy() {
11504         if (hasPasswordTransformationMethod()) {
11505             return false;
11506         }
11507
11508         if (mText.length() > 0 && hasSelection() && mEditor != null) {
11509             return true;
11510         }
11511
11512         return false;
11513     }
11514
11515     boolean canShare() {
11516         if (!getContext().canStartActivityForResult() || !isDeviceProvisioned()) {
11517             return false;
11518         }
11519         return canCopy();
11520     }
11521
11522     boolean isDeviceProvisioned() {
11523         if (mDeviceProvisionedState == DEVICE_PROVISIONED_UNKNOWN) {
11524             mDeviceProvisionedState = Settings.Global.getInt(
11525                     mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0
11526                     ? DEVICE_PROVISIONED_YES
11527                     : DEVICE_PROVISIONED_NO;
11528         }
11529         return mDeviceProvisionedState == DEVICE_PROVISIONED_YES;
11530     }
11531
11532     boolean canPaste() {
11533         return (mText instanceof Editable
11534                 && mEditor != null && mEditor.mKeyListener != null
11535                 && getSelectionStart() >= 0
11536                 && getSelectionEnd() >= 0
11537                 && ((ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE))
11538                         .hasPrimaryClip());
11539     }
11540
11541     boolean canPasteAsPlainText() {
11542         if (!canPaste()) {
11543             return false;
11544         }
11545
11546         final ClipData clipData =
11547                 ((ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE))
11548                         .getPrimaryClip();
11549         final ClipDescription description = clipData.getDescription();
11550         final boolean isPlainType = description.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN);
11551         final CharSequence text = clipData.getItemAt(0).getText();
11552         if (isPlainType && (text instanceof Spanned)) {
11553             Spanned spanned = (Spanned) text;
11554             if (TextUtils.hasStyleSpan(spanned)) {
11555                 return true;
11556             }
11557         }
11558         return description.hasMimeType(ClipDescription.MIMETYPE_TEXT_HTML);
11559     }
11560
11561     boolean canProcessText() {
11562         if (getId() == View.NO_ID) {
11563             return false;
11564         }
11565         return canShare();
11566     }
11567
11568     boolean canSelectAllText() {
11569         return canSelectText() && !hasPasswordTransformationMethod()
11570                 && !(getSelectionStart() == 0 && getSelectionEnd() == mText.length());
11571     }
11572
11573     boolean selectAllText() {
11574         if (mEditor != null) {
11575             // Hide the toolbar before changing the selection to avoid flickering.
11576             mEditor.hideFloatingToolbar(FLOATING_TOOLBAR_SELECT_ALL_REFRESH_DELAY);
11577         }
11578         final int length = mText.length();
11579         Selection.setSelection((Spannable) mText, 0, length);
11580         return length > 0;
11581     }
11582
11583     void replaceSelectionWithText(CharSequence text) {
11584         ((Editable) mText).replace(getSelectionStart(), getSelectionEnd(), text);
11585     }
11586
11587     /**
11588      * Paste clipboard content between min and max positions.
11589      */
11590     private void paste(int min, int max, boolean withFormatting) {
11591         ClipboardManager clipboard =
11592                 (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
11593         ClipData clip = clipboard.getPrimaryClip();
11594         if (clip != null) {
11595             boolean didFirst = false;
11596             for (int i = 0; i < clip.getItemCount(); i++) {
11597                 final CharSequence paste;
11598                 if (withFormatting) {
11599                     paste = clip.getItemAt(i).coerceToStyledText(getContext());
11600                 } else {
11601                     // Get an item as text and remove all spans by toString().
11602                     final CharSequence text = clip.getItemAt(i).coerceToText(getContext());
11603                     paste = (text instanceof Spanned) ? text.toString() : text;
11604                 }
11605                 if (paste != null) {
11606                     if (!didFirst) {
11607                         Selection.setSelection((Spannable) mText, max);
11608                         ((Editable) mText).replace(min, max, paste);
11609                         didFirst = true;
11610                     } else {
11611                         ((Editable) mText).insert(getSelectionEnd(), "\n");
11612                         ((Editable) mText).insert(getSelectionEnd(), paste);
11613                     }
11614                 }
11615             }
11616             sLastCutCopyOrTextChangedTime = 0;
11617         }
11618     }
11619
11620     private void shareSelectedText() {
11621         String selectedText = getSelectedText();
11622         if (selectedText != null && !selectedText.isEmpty()) {
11623             Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
11624             sharingIntent.setType("text/plain");
11625             sharingIntent.removeExtra(android.content.Intent.EXTRA_TEXT);
11626             selectedText = TextUtils.trimToParcelableSize(selectedText);
11627             sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, selectedText);
11628             getContext().startActivity(Intent.createChooser(sharingIntent, null));
11629             Selection.setSelection((Spannable) mText, getSelectionEnd());
11630         }
11631     }
11632
11633     @CheckResult
11634     private boolean setPrimaryClip(ClipData clip) {
11635         ClipboardManager clipboard =
11636                 (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
11637         try {
11638             clipboard.setPrimaryClip(clip);
11639         } catch (Throwable t) {
11640             return false;
11641         }
11642         sLastCutCopyOrTextChangedTime = SystemClock.uptimeMillis();
11643         return true;
11644     }
11645
11646     /**
11647      * Get the character offset closest to the specified absolute position. A typical use case is to
11648      * pass the result of {@link MotionEvent#getX()} and {@link MotionEvent#getY()} to this method.
11649      *
11650      * @param x The horizontal absolute position of a point on screen
11651      * @param y The vertical absolute position of a point on screen
11652      * @return the character offset for the character whose position is closest to the specified
11653      *  position. Returns -1 if there is no layout.
11654      */
11655     public int getOffsetForPosition(float x, float y) {
11656         if (getLayout() == null) return -1;
11657         final int line = getLineAtCoordinate(y);
11658         final int offset = getOffsetAtCoordinate(line, x);
11659         return offset;
11660     }
11661
11662     float convertToLocalHorizontalCoordinate(float x) {
11663         x -= getTotalPaddingLeft();
11664         // Clamp the position to inside of the view.
11665         x = Math.max(0.0f, x);
11666         x = Math.min(getWidth() - getTotalPaddingRight() - 1, x);
11667         x += getScrollX();
11668         return x;
11669     }
11670
11671     int getLineAtCoordinate(float y) {
11672         y -= getTotalPaddingTop();
11673         // Clamp the position to inside of the view.
11674         y = Math.max(0.0f, y);
11675         y = Math.min(getHeight() - getTotalPaddingBottom() - 1, y);
11676         y += getScrollY();
11677         return getLayout().getLineForVertical((int) y);
11678     }
11679
11680     int getLineAtCoordinateUnclamped(float y) {
11681         y -= getTotalPaddingTop();
11682         y += getScrollY();
11683         return getLayout().getLineForVertical((int) y);
11684     }
11685
11686     int getOffsetAtCoordinate(int line, float x) {
11687         x = convertToLocalHorizontalCoordinate(x);
11688         return getLayout().getOffsetForHorizontal(line, x);
11689     }
11690
11691     @Override
11692     public boolean onDragEvent(DragEvent event) {
11693         switch (event.getAction()) {
11694             case DragEvent.ACTION_DRAG_STARTED:
11695                 return mEditor != null && mEditor.hasInsertionController();
11696
11697             case DragEvent.ACTION_DRAG_ENTERED:
11698                 TextView.this.requestFocus();
11699                 return true;
11700
11701             case DragEvent.ACTION_DRAG_LOCATION:
11702                 if (mText instanceof Spannable) {
11703                     final int offset = getOffsetForPosition(event.getX(), event.getY());
11704                     Selection.setSelection((Spannable) mText, offset);
11705                 }
11706                 return true;
11707
11708             case DragEvent.ACTION_DROP:
11709                 if (mEditor != null) mEditor.onDrop(event);
11710                 return true;
11711
11712             case DragEvent.ACTION_DRAG_ENDED:
11713             case DragEvent.ACTION_DRAG_EXITED:
11714             default:
11715                 return true;
11716         }
11717     }
11718
11719     boolean isInBatchEditMode() {
11720         if (mEditor == null) return false;
11721         final Editor.InputMethodState ims = mEditor.mInputMethodState;
11722         if (ims != null) {
11723             return ims.mBatchEditNesting > 0;
11724         }
11725         return mEditor.mInBatchEditControllers;
11726     }
11727
11728     @Override
11729     public void onRtlPropertiesChanged(int layoutDirection) {
11730         super.onRtlPropertiesChanged(layoutDirection);
11731
11732         final TextDirectionHeuristic newTextDir = getTextDirectionHeuristic();
11733         if (mTextDir != newTextDir) {
11734             mTextDir = newTextDir;
11735             if (mLayout != null) {
11736                 checkForRelayout();
11737             }
11738         }
11739     }
11740
11741     /**
11742      * Returns the current {@link TextDirectionHeuristic}.
11743      *
11744      * @return the current {@link TextDirectionHeuristic}.
11745      * @hide
11746      */
11747     protected TextDirectionHeuristic getTextDirectionHeuristic() {
11748         if (hasPasswordTransformationMethod()) {
11749             // passwords fields should be LTR
11750             return TextDirectionHeuristics.LTR;
11751         }
11752
11753         if (mEditor != null
11754                 && (mEditor.mInputType & EditorInfo.TYPE_MASK_CLASS)
11755                     == EditorInfo.TYPE_CLASS_PHONE) {
11756             // Phone numbers must be in the direction of the locale's digits. Most locales have LTR
11757             // digits, but some locales, such as those written in the Adlam or N'Ko scripts, have
11758             // RTL digits.
11759             final DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(getTextLocale());
11760             final String zero = symbols.getDigitStrings()[0];
11761             // In case the zero digit is multi-codepoint, just use the first codepoint to determine
11762             // direction.
11763             final int firstCodepoint = zero.codePointAt(0);
11764             final byte digitDirection = Character.getDirectionality(firstCodepoint);
11765             if (digitDirection == Character.DIRECTIONALITY_RIGHT_TO_LEFT
11766                     || digitDirection == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) {
11767                 return TextDirectionHeuristics.RTL;
11768             } else {
11769                 return TextDirectionHeuristics.LTR;
11770             }
11771         }
11772
11773         // Always need to resolve layout direction first
11774         final boolean defaultIsRtl = (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
11775
11776         // Now, we can select the heuristic
11777         switch (getTextDirection()) {
11778             default:
11779             case TEXT_DIRECTION_FIRST_STRONG:
11780                 return (defaultIsRtl ? TextDirectionHeuristics.FIRSTSTRONG_RTL :
11781                         TextDirectionHeuristics.FIRSTSTRONG_LTR);
11782             case TEXT_DIRECTION_ANY_RTL:
11783                 return TextDirectionHeuristics.ANYRTL_LTR;
11784             case TEXT_DIRECTION_LTR:
11785                 return TextDirectionHeuristics.LTR;
11786             case TEXT_DIRECTION_RTL:
11787                 return TextDirectionHeuristics.RTL;
11788             case TEXT_DIRECTION_LOCALE:
11789                 return TextDirectionHeuristics.LOCALE;
11790             case TEXT_DIRECTION_FIRST_STRONG_LTR:
11791                 return TextDirectionHeuristics.FIRSTSTRONG_LTR;
11792             case TEXT_DIRECTION_FIRST_STRONG_RTL:
11793                 return TextDirectionHeuristics.FIRSTSTRONG_RTL;
11794         }
11795     }
11796
11797     /**
11798      * @hide
11799      */
11800     @Override
11801     public void onResolveDrawables(int layoutDirection) {
11802         // No need to resolve twice
11803         if (mLastLayoutDirection == layoutDirection) {
11804             return;
11805         }
11806         mLastLayoutDirection = layoutDirection;
11807
11808         // Resolve drawables
11809         if (mDrawables != null) {
11810             if (mDrawables.resolveWithLayoutDirection(layoutDirection)) {
11811                 prepareDrawableForDisplay(mDrawables.mShowing[Drawables.LEFT]);
11812                 prepareDrawableForDisplay(mDrawables.mShowing[Drawables.RIGHT]);
11813                 applyCompoundDrawableTint();
11814             }
11815         }
11816     }
11817
11818     /**
11819      * Prepares a drawable for display by propagating layout direction and
11820      * drawable state.
11821      *
11822      * @param dr the drawable to prepare
11823      */
11824     private void prepareDrawableForDisplay(@Nullable Drawable dr) {
11825         if (dr == null) {
11826             return;
11827         }
11828
11829         dr.setLayoutDirection(getLayoutDirection());
11830
11831         if (dr.isStateful()) {
11832             dr.setState(getDrawableState());
11833             dr.jumpToCurrentState();
11834         }
11835     }
11836
11837     /**
11838      * @hide
11839      */
11840     protected void resetResolvedDrawables() {
11841         super.resetResolvedDrawables();
11842         mLastLayoutDirection = -1;
11843     }
11844
11845     /**
11846      * @hide
11847      */
11848     protected void viewClicked(InputMethodManager imm) {
11849         if (imm != null) {
11850             imm.viewClicked(this);
11851         }
11852     }
11853
11854     /**
11855      * Deletes the range of text [start, end[.
11856      * @hide
11857      */
11858     protected void deleteText_internal(int start, int end) {
11859         ((Editable) mText).delete(start, end);
11860     }
11861
11862     /**
11863      * Replaces the range of text [start, end[ by replacement text
11864      * @hide
11865      */
11866     protected void replaceText_internal(int start, int end, CharSequence text) {
11867         ((Editable) mText).replace(start, end, text);
11868     }
11869
11870     /**
11871      * Sets a span on the specified range of text
11872      * @hide
11873      */
11874     protected void setSpan_internal(Object span, int start, int end, int flags) {
11875         ((Editable) mText).setSpan(span, start, end, flags);
11876     }
11877
11878     /**
11879      * Moves the cursor to the specified offset position in text
11880      * @hide
11881      */
11882     protected void setCursorPosition_internal(int start, int end) {
11883         Selection.setSelection(((Editable) mText), start, end);
11884     }
11885
11886     /**
11887      * An Editor should be created as soon as any of the editable-specific fields (grouped
11888      * inside the Editor object) is assigned to a non-default value.
11889      * This method will create the Editor if needed.
11890      *
11891      * A standard TextView (as well as buttons, checkboxes...) should not qualify and hence will
11892      * have a null Editor, unlike an EditText. Inconsistent in-between states will have an
11893      * Editor for backward compatibility, as soon as one of these fields is assigned.
11894      *
11895      * Also note that for performance reasons, the mEditor is created when needed, but not
11896      * reset when no more edit-specific fields are needed.
11897      */
11898     private void createEditorIfNeeded() {
11899         if (mEditor == null) {
11900             mEditor = new Editor(this);
11901         }
11902     }
11903
11904     /**
11905      * @hide
11906      */
11907     @Override
11908     public CharSequence getIterableTextForAccessibility() {
11909         return mText;
11910     }
11911
11912     private void ensureIterableTextForAccessibilitySelectable() {
11913         if (!(mText instanceof Spannable)) {
11914             setText(mText, BufferType.SPANNABLE);
11915         }
11916     }
11917
11918     /**
11919      * @hide
11920      */
11921     @Override
11922     public TextSegmentIterator getIteratorForGranularity(int granularity) {
11923         switch (granularity) {
11924             case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE: {
11925                 Spannable text = (Spannable) getIterableTextForAccessibility();
11926                 if (!TextUtils.isEmpty(text) && getLayout() != null) {
11927                     AccessibilityIterators.LineTextSegmentIterator iterator =
11928                             AccessibilityIterators.LineTextSegmentIterator.getInstance();
11929                     iterator.initialize(text, getLayout());
11930                     return iterator;
11931                 }
11932             } break;
11933             case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE: {
11934                 Spannable text = (Spannable) getIterableTextForAccessibility();
11935                 if (!TextUtils.isEmpty(text) && getLayout() != null) {
11936                     AccessibilityIterators.PageTextSegmentIterator iterator =
11937                             AccessibilityIterators.PageTextSegmentIterator.getInstance();
11938                     iterator.initialize(this);
11939                     return iterator;
11940                 }
11941             } break;
11942         }
11943         return super.getIteratorForGranularity(granularity);
11944     }
11945
11946     /**
11947      * @hide
11948      */
11949     @Override
11950     public int getAccessibilitySelectionStart() {
11951         return getSelectionStart();
11952     }
11953
11954     /**
11955      * @hide
11956      */
11957     public boolean isAccessibilitySelectionExtendable() {
11958         return true;
11959     }
11960
11961     /**
11962      * @hide
11963      */
11964     @Override
11965     public int getAccessibilitySelectionEnd() {
11966         return getSelectionEnd();
11967     }
11968
11969     /**
11970      * @hide
11971      */
11972     @Override
11973     public void setAccessibilitySelection(int start, int end) {
11974         if (getAccessibilitySelectionStart() == start
11975                 && getAccessibilitySelectionEnd() == end) {
11976             return;
11977         }
11978         CharSequence text = getIterableTextForAccessibility();
11979         if (Math.min(start, end) >= 0 && Math.max(start, end) <= text.length()) {
11980             Selection.setSelection((Spannable) text, start, end);
11981         } else {
11982             Selection.removeSelection((Spannable) text);
11983         }
11984         // Hide all selection controllers used for adjusting selection
11985         // since we are doing so explicitlty by other means and these
11986         // controllers interact with how selection behaves.
11987         if (mEditor != null) {
11988             mEditor.hideCursorAndSpanControllers();
11989             mEditor.stopTextActionMode();
11990         }
11991     }
11992
11993     /** @hide */
11994     @Override
11995     protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) {
11996         super.encodeProperties(stream);
11997
11998         TruncateAt ellipsize = getEllipsize();
11999         stream.addProperty("text:ellipsize", ellipsize == null ? null : ellipsize.name());
12000         stream.addProperty("text:textSize", getTextSize());
12001         stream.addProperty("text:scaledTextSize", getScaledTextSize());
12002         stream.addProperty("text:typefaceStyle", getTypefaceStyle());
12003         stream.addProperty("text:selectionStart", getSelectionStart());
12004         stream.addProperty("text:selectionEnd", getSelectionEnd());
12005         stream.addProperty("text:curTextColor", mCurTextColor);
12006         stream.addProperty("text:text", mText == null ? null : mText.toString());
12007         stream.addProperty("text:gravity", mGravity);
12008     }
12009
12010     /**
12011      * User interface state that is stored by TextView for implementing
12012      * {@link View#onSaveInstanceState}.
12013      */
12014     public static class SavedState extends BaseSavedState {
12015         int selStart = -1;
12016         int selEnd = -1;
12017         CharSequence text;
12018         boolean frozenWithFocus;
12019         CharSequence error;
12020         ParcelableParcel editorState;  // Optional state from Editor.
12021
12022         SavedState(Parcelable superState) {
12023             super(superState);
12024         }
12025
12026         @Override
12027         public void writeToParcel(Parcel out, int flags) {
12028             super.writeToParcel(out, flags);
12029             out.writeInt(selStart);
12030             out.writeInt(selEnd);
12031             out.writeInt(frozenWithFocus ? 1 : 0);
12032             TextUtils.writeToParcel(text, out, flags);
12033
12034             if (error == null) {
12035                 out.writeInt(0);
12036             } else {
12037                 out.writeInt(1);
12038                 TextUtils.writeToParcel(error, out, flags);
12039             }
12040
12041             if (editorState == null) {
12042                 out.writeInt(0);
12043             } else {
12044                 out.writeInt(1);
12045                 editorState.writeToParcel(out, flags);
12046             }
12047         }
12048
12049         @Override
12050         public String toString() {
12051             String str = "TextView.SavedState{"
12052                     + Integer.toHexString(System.identityHashCode(this))
12053                     + " start=" + selStart + " end=" + selEnd;
12054             if (text != null) {
12055                 str += " text=" + text;
12056             }
12057             return str + "}";
12058         }
12059
12060         @SuppressWarnings("hiding")
12061         public static final Parcelable.Creator<SavedState> CREATOR =
12062                 new Parcelable.Creator<SavedState>() {
12063                     public SavedState createFromParcel(Parcel in) {
12064                         return new SavedState(in);
12065                     }
12066
12067                     public SavedState[] newArray(int size) {
12068                         return new SavedState[size];
12069                     }
12070                 };
12071
12072         private SavedState(Parcel in) {
12073             super(in);
12074             selStart = in.readInt();
12075             selEnd = in.readInt();
12076             frozenWithFocus = (in.readInt() != 0);
12077             text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
12078
12079             if (in.readInt() != 0) {
12080                 error = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
12081             }
12082
12083             if (in.readInt() != 0) {
12084                 editorState = ParcelableParcel.CREATOR.createFromParcel(in);
12085             }
12086         }
12087     }
12088
12089     private static class CharWrapper implements CharSequence, GetChars, GraphicsOperations {
12090         private char[] mChars;
12091         private int mStart, mLength;
12092
12093         public CharWrapper(char[] chars, int start, int len) {
12094             mChars = chars;
12095             mStart = start;
12096             mLength = len;
12097         }
12098
12099         /* package */ void set(char[] chars, int start, int len) {
12100             mChars = chars;
12101             mStart = start;
12102             mLength = len;
12103         }
12104
12105         public int length() {
12106             return mLength;
12107         }
12108
12109         public char charAt(int off) {
12110             return mChars[off + mStart];
12111         }
12112
12113         @Override
12114         public String toString() {
12115             return new String(mChars, mStart, mLength);
12116         }
12117
12118         public CharSequence subSequence(int start, int end) {
12119             if (start < 0 || end < 0 || start > mLength || end > mLength) {
12120                 throw new IndexOutOfBoundsException(start + ", " + end);
12121             }
12122
12123             return new String(mChars, start + mStart, end - start);
12124         }
12125
12126         public void getChars(int start, int end, char[] buf, int off) {
12127             if (start < 0 || end < 0 || start > mLength || end > mLength) {
12128                 throw new IndexOutOfBoundsException(start + ", " + end);
12129             }
12130
12131             System.arraycopy(mChars, start + mStart, buf, off, end - start);
12132         }
12133
12134         @Override
12135         public void drawText(BaseCanvas c, int start, int end,
12136                              float x, float y, Paint p) {
12137             c.drawText(mChars, start + mStart, end - start, x, y, p);
12138         }
12139
12140         @Override
12141         public void drawTextRun(BaseCanvas c, int start, int end,
12142                 int contextStart, int contextEnd, float x, float y, boolean isRtl, Paint p) {
12143             int count = end - start;
12144             int contextCount = contextEnd - contextStart;
12145             c.drawTextRun(mChars, start + mStart, count, contextStart + mStart,
12146                     contextCount, x, y, isRtl, p);
12147         }
12148
12149         public float measureText(int start, int end, Paint p) {
12150             return p.measureText(mChars, start + mStart, end - start);
12151         }
12152
12153         public int getTextWidths(int start, int end, float[] widths, Paint p) {
12154             return p.getTextWidths(mChars, start + mStart, end - start, widths);
12155         }
12156
12157         public float getTextRunAdvances(int start, int end, int contextStart,
12158                 int contextEnd, boolean isRtl, float[] advances, int advancesIndex,
12159                 Paint p) {
12160             int count = end - start;
12161             int contextCount = contextEnd - contextStart;
12162             return p.getTextRunAdvances(mChars, start + mStart, count,
12163                     contextStart + mStart, contextCount, isRtl, advances,
12164                     advancesIndex);
12165         }
12166
12167         public int getTextRunCursor(int contextStart, int contextEnd, int dir,
12168                 int offset, int cursorOpt, Paint p) {
12169             int contextCount = contextEnd - contextStart;
12170             return p.getTextRunCursor(mChars, contextStart + mStart,
12171                     contextCount, dir, offset + mStart, cursorOpt);
12172         }
12173     }
12174
12175     private static final class Marquee {
12176         // TODO: Add an option to configure this
12177         private static final float MARQUEE_DELTA_MAX = 0.07f;
12178         private static final int MARQUEE_DELAY = 1200;
12179         private static final int MARQUEE_DP_PER_SECOND = 30;
12180
12181         private static final byte MARQUEE_STOPPED = 0x0;
12182         private static final byte MARQUEE_STARTING = 0x1;
12183         private static final byte MARQUEE_RUNNING = 0x2;
12184
12185         private final WeakReference<TextView> mView;
12186         private final Choreographer mChoreographer;
12187
12188         private byte mStatus = MARQUEE_STOPPED;
12189         private final float mPixelsPerMs;
12190         private float mMaxScroll;
12191         private float mMaxFadeScroll;
12192         private float mGhostStart;
12193         private float mGhostOffset;
12194         private float mFadeStop;
12195         private int mRepeatLimit;
12196
12197         private float mScroll;
12198         private long mLastAnimationMs;
12199
12200         Marquee(TextView v) {
12201             final float density = v.getContext().getResources().getDisplayMetrics().density;
12202             mPixelsPerMs = MARQUEE_DP_PER_SECOND * density / 1000f;
12203             mView = new WeakReference<TextView>(v);
12204             mChoreographer = Choreographer.getInstance();
12205         }
12206
12207         private Choreographer.FrameCallback mTickCallback = new Choreographer.FrameCallback() {
12208             @Override
12209             public void doFrame(long frameTimeNanos) {
12210                 tick();
12211             }
12212         };
12213
12214         private Choreographer.FrameCallback mStartCallback = new Choreographer.FrameCallback() {
12215             @Override
12216             public void doFrame(long frameTimeNanos) {
12217                 mStatus = MARQUEE_RUNNING;
12218                 mLastAnimationMs = mChoreographer.getFrameTime();
12219                 tick();
12220             }
12221         };
12222
12223         private Choreographer.FrameCallback mRestartCallback = new Choreographer.FrameCallback() {
12224             @Override
12225             public void doFrame(long frameTimeNanos) {
12226                 if (mStatus == MARQUEE_RUNNING) {
12227                     if (mRepeatLimit >= 0) {
12228                         mRepeatLimit--;
12229                     }
12230                     start(mRepeatLimit);
12231                 }
12232             }
12233         };
12234
12235         void tick() {
12236             if (mStatus != MARQUEE_RUNNING) {
12237                 return;
12238             }
12239
12240             mChoreographer.removeFrameCallback(mTickCallback);
12241
12242             final TextView textView = mView.get();
12243             if (textView != null && (textView.isFocused() || textView.isSelected())) {
12244                 long currentMs = mChoreographer.getFrameTime();
12245                 long deltaMs = currentMs - mLastAnimationMs;
12246                 mLastAnimationMs = currentMs;
12247                 float deltaPx = deltaMs * mPixelsPerMs;
12248                 mScroll += deltaPx;
12249                 if (mScroll > mMaxScroll) {
12250                     mScroll = mMaxScroll;
12251                     mChoreographer.postFrameCallbackDelayed(mRestartCallback, MARQUEE_DELAY);
12252                 } else {
12253                     mChoreographer.postFrameCallback(mTickCallback);
12254                 }
12255                 textView.invalidate();
12256             }
12257         }
12258
12259         void stop() {
12260             mStatus = MARQUEE_STOPPED;
12261             mChoreographer.removeFrameCallback(mStartCallback);
12262             mChoreographer.removeFrameCallback(mRestartCallback);
12263             mChoreographer.removeFrameCallback(mTickCallback);
12264             resetScroll();
12265         }
12266
12267         private void resetScroll() {
12268             mScroll = 0.0f;
12269             final TextView textView = mView.get();
12270             if (textView != null) textView.invalidate();
12271         }
12272
12273         void start(int repeatLimit) {
12274             if (repeatLimit == 0) {
12275                 stop();
12276                 return;
12277             }
12278             mRepeatLimit = repeatLimit;
12279             final TextView textView = mView.get();
12280             if (textView != null && textView.mLayout != null) {
12281                 mStatus = MARQUEE_STARTING;
12282                 mScroll = 0.0f;
12283                 final int textWidth = textView.getWidth() - textView.getCompoundPaddingLeft()
12284                         - textView.getCompoundPaddingRight();
12285                 final float lineWidth = textView.mLayout.getLineWidth(0);
12286                 final float gap = textWidth / 3.0f;
12287                 mGhostStart = lineWidth - textWidth + gap;
12288                 mMaxScroll = mGhostStart + textWidth;
12289                 mGhostOffset = lineWidth + gap;
12290                 mFadeStop = lineWidth + textWidth / 6.0f;
12291                 mMaxFadeScroll = mGhostStart + lineWidth + lineWidth;
12292
12293                 textView.invalidate();
12294                 mChoreographer.postFrameCallback(mStartCallback);
12295             }
12296         }
12297
12298         float getGhostOffset() {
12299             return mGhostOffset;
12300         }
12301
12302         float getScroll() {
12303             return mScroll;
12304         }
12305
12306         float getMaxFadeScroll() {
12307             return mMaxFadeScroll;
12308         }
12309
12310         boolean shouldDrawLeftFade() {
12311             return mScroll <= mFadeStop;
12312         }
12313
12314         boolean shouldDrawGhost() {
12315             return mStatus == MARQUEE_RUNNING && mScroll > mGhostStart;
12316         }
12317
12318         boolean isRunning() {
12319             return mStatus == MARQUEE_RUNNING;
12320         }
12321
12322         boolean isStopped() {
12323             return mStatus == MARQUEE_STOPPED;
12324         }
12325     }
12326
12327     private class ChangeWatcher implements TextWatcher, SpanWatcher {
12328
12329         private CharSequence mBeforeText;
12330
12331         public void beforeTextChanged(CharSequence buffer, int start,
12332                                       int before, int after) {
12333             if (DEBUG_EXTRACT) {
12334                 Log.v(LOG_TAG, "beforeTextChanged start=" + start
12335                         + " before=" + before + " after=" + after + ": " + buffer);
12336             }
12337
12338             if (AccessibilityManager.getInstance(mContext).isEnabled()
12339                     && !isPasswordInputType(getInputType()) && !hasPasswordTransformationMethod()) {
12340                 mBeforeText = buffer.toString();
12341             }
12342
12343             TextView.this.sendBeforeTextChanged(buffer, start, before, after);
12344         }
12345
12346         public void onTextChanged(CharSequence buffer, int start, int before, int after) {
12347             if (DEBUG_EXTRACT) {
12348                 Log.v(LOG_TAG, "onTextChanged start=" + start
12349                         + " before=" + before + " after=" + after + ": " + buffer);
12350             }
12351             TextView.this.handleTextChanged(buffer, start, before, after);
12352
12353             if (AccessibilityManager.getInstance(mContext).isEnabled()
12354                     && (isFocused() || isSelected() && isShown())) {
12355                 sendAccessibilityEventTypeViewTextChanged(mBeforeText, start, before, after);
12356                 mBeforeText = null;
12357             }
12358         }
12359
12360         public void afterTextChanged(Editable buffer) {
12361             if (DEBUG_EXTRACT) {
12362                 Log.v(LOG_TAG, "afterTextChanged: " + buffer);
12363             }
12364             TextView.this.sendAfterTextChanged(buffer);
12365
12366             if (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0) {
12367                 MetaKeyKeyListener.stopSelecting(TextView.this, buffer);
12368             }
12369         }
12370
12371         public void onSpanChanged(Spannable buf, Object what, int s, int e, int st, int en) {
12372             if (DEBUG_EXTRACT) {
12373                 Log.v(LOG_TAG, "onSpanChanged s=" + s + " e=" + e
12374                         + " st=" + st + " en=" + en + " what=" + what + ": " + buf);
12375             }
12376             TextView.this.spanChange(buf, what, s, st, e, en);
12377         }
12378
12379         public void onSpanAdded(Spannable buf, Object what, int s, int e) {
12380             if (DEBUG_EXTRACT) {
12381                 Log.v(LOG_TAG, "onSpanAdded s=" + s + " e=" + e + " what=" + what + ": " + buf);
12382             }
12383             TextView.this.spanChange(buf, what, -1, s, -1, e);
12384         }
12385
12386         public void onSpanRemoved(Spannable buf, Object what, int s, int e) {
12387             if (DEBUG_EXTRACT) {
12388                 Log.v(LOG_TAG, "onSpanRemoved s=" + s + " e=" + e + " what=" + what + ": " + buf);
12389             }
12390             TextView.this.spanChange(buf, what, s, -1, e, -1);
12391         }
12392     }
12393 }