Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * Copyright (C) 2006 The Android Open Source Project
    *
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
    *      http://www.apache.org/licenses/LICENSE-2.0
    *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.widget;
  
  
  
  import android.os.Bundle;
  import android.os.Parcel;
  import android.util.Log;
  import android.view.View;
 
Displays text to the user and optionally allows them to edit it. A TextView is a complete text editor, however the basic class is configured to not allow editing; see EditText for a subclass that configures the text view for editing.

XML attributes

See TextView Attributes, View Attributes

Attr:
ref android.R.styleable#TextView_text
Attr:
ref android.R.styleable#TextView_bufferType
Attr:
ref android.R.styleable#TextView_hint
Attr:
ref android.R.styleable#TextView_textColor
Attr:
ref android.R.styleable#TextView_textColorHighlight
Attr:
ref android.R.styleable#TextView_textColorHint
Attr:
ref android.R.styleable#TextView_textAppearance
Attr:
ref android.R.styleable#TextView_textColorLink
Attr:
ref android.R.styleable#TextView_textSize
Attr:
ref android.R.styleable#TextView_textScaleX
Attr:
ref android.R.styleable#TextView_typeface
Attr:
ref android.R.styleable#TextView_textStyle
Attr:
ref android.R.styleable#TextView_cursorVisible
Attr:
ref android.R.styleable#TextView_maxLines
Attr:
ref android.R.styleable#TextView_maxHeight
Attr:
ref android.R.styleable#TextView_lines
Attr:
ref android.R.styleable#TextView_height
Attr:
ref android.R.styleable#TextView_minLines
Attr:
ref android.R.styleable#TextView_minHeight
Attr:
ref android.R.styleable#TextView_maxEms
Attr:
ref android.R.styleable#TextView_maxWidth
Attr:
ref android.R.styleable#TextView_ems
Attr:
ref android.R.styleable#TextView_width
Attr:
ref android.R.styleable#TextView_minEms
Attr:
ref android.R.styleable#TextView_minWidth
Attr:
ref android.R.styleable#TextView_gravity
Attr:
ref android.R.styleable#TextView_scrollHorizontally
Attr:
ref android.R.styleable#TextView_password
Attr:
ref android.R.styleable#TextView_singleLine
Attr:
ref android.R.styleable#TextView_selectAllOnFocus
Attr:
ref android.R.styleable#TextView_includeFontPadding
Attr:
ref android.R.styleable#TextView_maxLength
Attr:
ref android.R.styleable#TextView_shadowColor
Attr:
ref android.R.styleable#TextView_shadowDx
Attr:
ref android.R.styleable#TextView_shadowDy
Attr:
ref android.R.styleable#TextView_shadowRadius
Attr:
ref android.R.styleable#TextView_autoLink
Attr:
ref android.R.styleable#TextView_linksClickable
Attr:
ref android.R.styleable#TextView_numeric
Attr:
ref android.R.styleable#TextView_digits
Attr:
ref android.R.styleable#TextView_phoneNumber
Attr:
ref android.R.styleable#TextView_inputMethod
Attr:
ref android.R.styleable#TextView_capitalize
Attr:
ref android.R.styleable#TextView_autoText
Attr:
ref android.R.styleable#TextView_editable
Attr:
ref android.R.styleable#TextView_freezesText
Attr:
ref android.R.styleable#TextView_ellipsize
Attr:
ref android.R.styleable#TextView_drawableTop
Attr:
ref android.R.styleable#TextView_drawableBottom
Attr:
ref android.R.styleable#TextView_drawableRight
Attr:
ref android.R.styleable#TextView_drawableLeft
Attr:
ref android.R.styleable#TextView_drawablePadding
Attr:
ref android.R.styleable#TextView_lineSpacingExtra
Attr:
ref android.R.styleable#TextView_lineSpacingMultiplier
Attr:
ref android.R.styleable#TextView_marqueeRepeatLimit
Attr:
ref android.R.styleable#TextView_inputType
Attr:
ref android.R.styleable#TextView_imeOptions
Attr:
ref android.R.styleable#TextView_privateImeOptions
Attr:
ref android.R.styleable#TextView_imeActionLabel
Attr:
ref android.R.styleable#TextView_imeActionId
Attr:
ref android.R.styleable#TextView_editorExtras
 
 public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
     static final String LOG_TAG = "TextView";
     static final boolean DEBUG_EXTRACT = false;
     
     private static int PRIORITY = 100;
 
     final int[] mTempCoords = new int[2];
     Rect mTempRect;
 
     private ColorStateList mTextColor;
     private int mCurTextColor;
     private ColorStateList mHintTextColor;
     private ColorStateList mLinkTextColor;
     private int mCurHintTextColor;
     private boolean mFreezesText;
     private boolean mFrozenWithFocus;
     private boolean mTemporaryDetach;
     private boolean mDispatchTemporaryDetach;
 
     private boolean mEatTouchRelease = false;
     private boolean mScrolled = false;
 
     private Editable.Factory mEditableFactory = Editable.Factory.getInstance();
     private Spannable.Factory mSpannableFactory = Spannable.Factory.getInstance();
 
     private float mShadowRadiusmShadowDxmShadowDy;
 
     private static final int PREDRAW_NOT_REGISTERED = 0;
     private static final int PREDRAW_PENDING = 1;
     private static final int PREDRAW_DONE = 2;
     private int mPreDrawState = ;
 
     private TextUtils.TruncateAt mEllipsize = null;
 
     // Enum for the "typeface" XML parameter.
     // TODO: How can we get this from the XML instead of hardcoding it here?
     private static final int SANS = 1;
     private static final int SERIF = 2;
     private static final int MONOSPACE = 3;
 
     // Bitfield for the "numeric" XML parameter.
     // TODO: How can we get this from the XML instead of hardcoding it here?
     private static final int SIGNED = 2;
     private static final int DECIMAL = 4;
 
     class Drawables {
         final Rect mCompoundRect = new Rect();
         int mDrawablePadding;
     }
     private Drawables mDrawables;
 
     private CharSequence mError;
     private boolean mErrorWasChanged;
     private ErrorPopup mPopup;
    
This flag is set if the TextView tries to display an error before it is attached to the window (so its position is still unknown). It causes the error to be shown later, when onAttachedToWindow() is called.
 
     private boolean mShowErrorAfterAttach;
 
     private CharWrapper mCharWrapper = null;
 
     private boolean mSelectionMoved = false;
     private boolean mTouchFocusSelected = false;
 
     private Marquee mMarquee;
     private boolean mRestartMarquee;
 
     private int mMarqueeRepeatLimit = 3;
 
     class InputContentType {
         int imeOptions = .;
         String privateImeOptions;
         CharSequence imeActionLabel;
         int imeActionId;
         Bundle extras;
         boolean enterDown;
     }
 
     class InputMethodState {
         Rect mCursorRectInWindow = new Rect();
         RectF mTmpRectF = new RectF();
         float[] mTmpOffset = new float[2];
         final ExtractedText mTmpExtracted = new ExtractedText();
         int mBatchEditNesting;
         boolean mCursorChanged;
         boolean mSelectionModeChanged;
         boolean mContentChanged;
         int mChangedStartmChangedEndmChangedDelta;
     }
 
     int mTextSelectHandleRes;
 
 
     /*
      * Kick-start the font cache for the zygote process (to pay the cost of
      * initializing freetype for our default font only once).
      */
     static {
         Paint p = new Paint();
         p.setAntiAlias(true);
         // We don't care about the result, just the side-effect of measuring.
         p.measureText("H");
     }

    
Interface definition for a callback to be invoked when an action is performed on the editor.
 
     public interface OnEditorActionListener {
        
Called when an action is being performed.

Parameters:
v The view that was clicked.
actionId Identifier of the action. This will be either the identifier you supplied, or EditorInfo.IME_NULL if being called due to the enter key being pressed.
event If triggered by an enter key, this is the event; otherwise, this is null.
Returns:
Return true if you have consumed the action, else false.
 
         boolean onEditorAction(TextView vint actionIdKeyEvent event);
     }
     
     public TextView(Context context) {
         this(contextnull);
     }
 
     public TextView(Context context,
                     AttributeSet attrs) {
         this(contextattrs.....);
     }
 
     @SuppressWarnings("deprecation")
     public TextView(Context context,
                     AttributeSet attrs,
                     int defStyle) {
         super(contextattrsdefStyle);
          = "";
 
          = new TextPaint(.);
                 getResources().getCompatibilityInfo().);
         
         // If we get the paint from the skin, we should set it to left, since
         // the layout always wants it to be left.
         // mTextPaint.setTextAlign(Paint.Align.LEFT);
 
          = new Paint(.);
                 getResources().getCompatibilityInfo().);
 
          = getDefaultMovementMethod();
          = null;
 
         TypedArray a =
             context.obtainStyledAttributes(
                 attrs.....defStyle, 0);
 
         int textColorHighlight = 0;
         ColorStateList textColor = null;
         ColorStateList textColorHint = null;
         ColorStateList textColorLink = null;
         int textSize = 15;
         int typefaceIndex = -1;
         int styleIndex = -1;
 
         /*
          * Look the appearance up without checking first if it exists because
          * almost every TextView has one and it greatly simplifies the logic
          * to be able to parse the appearance first and then let specific tags
          * for this View override it.
          */
         TypedArray appearance = null;
         int ap = a.getResourceId(....., -1);
         if (ap != -1) {
             appearance = context.obtainStyledAttributes(ap,
                                 .....
                                 );
         }
         if (appearance != null) {
             int n = appearance.getIndexCount();
             for (int i = 0; i < ni++) {
                 int attr = appearance.getIndex(i);
 
                 switch (attr) {
                 case .....:
                     textColorHighlight = appearance.getColor(attrtextColorHighlight);
                     break;
 
                 case .....:
                     textColor = appearance.getColorStateList(attr);
                     break;
 
                 case .....:
                     textColorHint = appearance.getColorStateList(attr);
                     break;
 
                 case .....:
                     textColorLink = appearance.getColorStateList(attr);
                     break;
 
                 case .....:
                     textSize = appearance.getDimensionPixelSize(attrtextSize);
                     break;
 
                 case .....:
                     typefaceIndex = appearance.getInt(attr, -1);
                     break;
 
                 case .....:
                     styleIndex = appearance.getInt(attr, -1);
                     break;
                 }
             }
 
             appearance.recycle();
         }
 
         boolean editable = getDefaultEditable();
         CharSequence inputMethod = null;
         int numeric = 0;
         CharSequence digits = null;
         boolean phone = false;
         boolean autotext = false;
         int autocap = -1;
         int buffertype = 0;
         boolean selectallonfocus = false;
         Drawable drawableLeft = nulldrawableTop = nulldrawableRight = null,
             drawableBottom = null;
         int drawablePadding = 0;
         int ellipsize = -1;
         boolean singleLine = false;
         int maxlength = -1;
         CharSequence text = "";
         CharSequence hint = null;
         int shadowcolor = 0;
         float dx = 0, dy = 0, r = 0;
         boolean password = false;
         int inputType = .;
 
         int n = a.getIndexCount();
         for (int i = 0; i < ni++) {
             int attr = a.getIndex(i);
 
             switch (attr) {
             case .....:
                 editable = a.getBoolean(attreditable);
                 break;
 
             case .....:
                 inputMethod = a.getText(attr);
                 break;
 
             case .....:
                 numeric = a.getInt(attrnumeric);
                 break;
 
             case .....:
                 digits = a.getText(attr);
                 break;
 
             case .....:
                 phone = a.getBoolean(attrphone);
                 break;
 
             case .....:
                 autotext = a.getBoolean(attrautotext);
                 break;
 
             case .....:
                 autocap = a.getInt(attrautocap);
                 break;
 
             case .....:
                 buffertype = a.getInt(attrbuffertype);
                 break;
 
             case .....:
                 selectallonfocus = a.getBoolean(attrselectallonfocus);
                 break;
 
             case .....:
                  = a.getInt(attr, 0);
                 break;
 
             case .....:
                  = a.getBoolean(attrtrue);
                 break;
 
             case .....:
                 drawableLeft = a.getDrawable(attr);
                 break;
 
             case .....:
                 drawableTop = a.getDrawable(attr);
                 break;
 
             case .....:
                 drawableRight = a.getDrawable(attr);
                 break;
 
             case .....:
                 drawableBottom = a.getDrawable(attr);
                 break;
 
             case .....:
                 drawablePadding = a.getDimensionPixelSize(attrdrawablePadding);
                 break;
 
             case .....:
                 setMaxLines(a.getInt(attr, -1));
                 break;
 
             case .....:
                 setMaxHeight(a.getDimensionPixelSize(attr, -1));
                 break;
 
             case .....:
                 setLines(a.getInt(attr, -1));
                 break;
 
             case .....:
                 setHeight(a.getDimensionPixelSize(attr, -1));
                 break;
 
             case .....:
                 setMinLines(a.getInt(attr, -1));
                 break;
 
             case .....:
                 setMinHeight(a.getDimensionPixelSize(attr, -1));
                 break;
 
             case .....:
                 setMaxEms(a.getInt(attr, -1));
                 break;
 
             case .....:
                 setMaxWidth(a.getDimensionPixelSize(attr, -1));
                 break;
 
             case .....:
                 setEms(a.getInt(attr, -1));
                 break;
 
             case .....:
                 setWidth(a.getDimensionPixelSize(attr, -1));
                 break;
 
             case .....:
                 setMinEms(a.getInt(attr, -1));
                 break;
 
             case .....:
                 setMinWidth(a.getDimensionPixelSize(attr, -1));
                 break;
 
             case .....:
                 setGravity(a.getInt(attr, -1));
                 break;
 
             case .....:
                 hint = a.getText(attr);
                 break;
 
             case .....:
                 text = a.getText(attr);
                 break;
 
                 if (a.getBoolean(attrfalse)) {
                     setHorizontallyScrolling(true);
                 }
                 break;
 
             case .....:
                 singleLine = a.getBoolean(attrsingleLine);
                 break;
 
             case .....:
                 ellipsize = a.getInt(attrellipsize);
                 break;
 
                 setMarqueeRepeatLimit(a.getInt(attr));
                 break;
 
                 if (!a.getBoolean(attrtrue)) {
                     setIncludeFontPadding(false);
                 }
                 break;
 
             case .....:
                 if (!a.getBoolean(attrtrue)) {
                     setCursorVisible(false);
                 }
                 break;
 
             case .....:
                 maxlength = a.getInt(attr, -1);
                 break;
 
             case .....:
                 setTextScaleX(a.getFloat(attr, 1.0f));
                 break;
 
             case .....:
                  = a.getBoolean(attrfalse);
                 break;
 
             case .....:
                 shadowcolor = a.getInt(attr, 0);
                 break;
 
             case .....:
                 dx = a.getFloat(attr, 0);
                 break;
 
             case .....:
                 dy = a.getFloat(attr, 0);
                 break;
 
             case .....:
                 r = a.getFloat(attr, 0);
                 break;
 
             case .....:
                 setEnabled(a.getBoolean(attrisEnabled()));
                 break;
 
                 textColorHighlight = a.getColor(attrtextColorHighlight);
                 break;
 
             case .....:
                 textColor = a.getColorStateList(attr);
                 break;
 
             case .....:
                 textColorHint = a.getColorStateList(attr);
                 break;
 
             case .....:
                 textColorLink = a.getColorStateList(attr);
                 break;
 
             case .....:
                 textSize = a.getDimensionPixelSize(attrtextSize);
                 break;
 
             case .....:
                 typefaceIndex = a.getInt(attrtypefaceIndex);
                 break;
 
             case .....:
                 styleIndex = a.getInt(attrstyleIndex);
                 break;
 
             case .....:
                 password = a.getBoolean(attrpassword);
                 break;
 
             case .....:
                  = a.getDimensionPixelSize(attr, (int);
                 break;
 
                  = a.getFloat(attr);
                 break;
 
             case .....:
                 inputType = a.getInt(attr);
                 break;
 
             case .....:
                 if ( == null) {
                      = new InputContentType();
                 }
                 . = a.getInt(attr,
                         .);
                 break;
 
             case .....:
                 if ( == null) {
                      = new InputContentType();
                 }
                 . = a.getText(attr);
                 break;
 
             case .....:
                 if ( == null) {
                      = new InputContentType();
                 }
                 . = a.getInt(attr,
                         .);
                 break;
 
             case .....:
                 setPrivateImeOptions(a.getString(attr));
                 break;
 
             case .....:
                 try {
                     setInputExtras(a.getResourceId(attr, 0));
                 } catch (XmlPullParserException e) {
                     Log.w("Failure reading input extras"e);
                 } catch (IOException e) {
                     Log.w("Failure reading input extras"e);
                 }
                 break;
 
                  = a.getResourceId(attr, 0);
                 break;
 
                  = a.getResourceId(attr, 0);
                 break;
 
             case .....:
                  = a.getResourceId(attr, 0);
                 break;
             }
         }
         a.recycle();
 
         BufferType bufferType = .;
 
         if ((inputType & (. | .))
                 == (. | .)) {
             password = true;
         }
 
         if (inputMethod != null) {
             Class<?> c;
 
             try {
                 c = Class.forName(inputMethod.toString());
             } catch (ClassNotFoundException ex) {
                 throw new RuntimeException(ex);
             }
 
             try {
                  = (KeyListenerc.newInstance();
             } catch (InstantiationException ex) {
                 throw new RuntimeException(ex);
             } catch (IllegalAccessException ex) {
                 throw new RuntimeException(ex);
             }
             try {
                  = inputType != .
                         ? inputType
                         : .getInputType();
             } catch (IncompatibleClassChangeError e) {
                  = .;
             }
         } else if (digits != null) {
              = DigitsKeyListener.getInstance(digits.toString());
             // If no input type was specified, we will default to generic
             // text, since we can't tell the IME about the set of digits
             // that was selected.
              = inputType != .
                     ? inputType : .;
         } else if (inputType != .) {
             setInputType(inputTypetrue);
             singleLine = (inputType&(.
                             | .)) !=
                     (.
                             | .);
         } else if (phone) {
              = DialerKeyListener.getInstance();
              = inputType = .;
         } else if (numeric != 0) {
              = DigitsKeyListener.getInstance((numeric & ) != 0,
                                                    (numeric & ) != 0);
             inputType = .;
             if ((numeric & ) != 0) {
                 inputType |= .;
             }
             if ((numeric & ) != 0) {
                 inputType |= .;
             }
              = inputType;
         } else if (autotext || autocap != -1) {
             TextKeyListener.Capitalize cap;
 
             inputType = .;
             if (!singleLine) {
                 inputType |= .;
             }
 
             switch (autocap) {
             case 1:
                 cap = ..;
                 inputType |= .;
                 break;
 
             case 2:
                 cap = ..;
                 inputType |= .;
                 break;
 
             case 3:
                 cap = ..;
                 inputType |= .;
                 break;
 
             default:
                 cap = ..;
                 break;
             }
 
              = TextKeyListener.getInstance(autotextcap);
              = inputType;
         } else if (editable) {
              = TextKeyListener.getInstance();
              = .;
             if (!singleLine) {
                  |= .;
             }
         } else {
              = null;
 
             switch (buffertype) {
                 case 0:
                     bufferType = .;
                     break;
                 case 1:
                     bufferType = .;
                     break;
                 case 2:
                     bufferType = .;
                     break;
             }
         }
 
         if (password && (&.)
                 == .) {
              = ( & ~(.))
                 | .;
         }
 
         if (selectallonfocus) {
              = true;
 
             if (bufferType == .)
                 bufferType = .;
         }
 
             drawableLeftdrawableTopdrawableRightdrawableBottom);
         setCompoundDrawablePadding(drawablePadding);
 
         if (singleLine) {
             setSingleLine();
 
             if ( == null && ellipsize < 0) {
                 ellipsize = 3; // END
             }
         }
 
         switch (ellipsize) {
             case 1:
                 setEllipsize(..);
                 break;
             case 2:
                 setEllipsize(..);
                 break;
             case 3:
                 setEllipsize(..);
                 break;
             case 4:
                 setHorizontalFadingEdgeEnabled(true);
                 setEllipsize(..);
                 break;
         }
 
         setTextColor(textColor != null ? textColor : ColorStateList.valueOf(0xFF000000));
         setHintTextColor(textColorHint);
         setLinkTextColor(textColorLink);
         if (textColorHighlight != 0) {
             setHighlightColor(textColorHighlight);
         }
         setRawTextSize(textSize);
 
         if (password) {
             setTransformationMethod(PasswordTransformationMethod.getInstance());
             typefaceIndex = ;
         } else if ((&(.
                 |.))
                 == (.
                         |.)) {
             typefaceIndex = ;
         }
 
         setTypefaceByIndex(typefaceIndexstyleIndex);
 
         if (shadowcolor != 0) {
             setShadowLayer(rdxdyshadowcolor);
         }
 
         if (maxlength >= 0) {
             setFilters(new InputFilter[] { new InputFilter.LengthFilter(maxlength) });
         } else {
             setFilters();
         }
 
         setText(textbufferType);
         if (hint != nullsetHint(hint);
 
         /*
          * Views are not normally focusable unless specified to be.
          * However, TextViews that have input or movement methods *are*
          * focusable by default.
          */
         a = context.obtainStyledAttributes(attrs,
                                            .....,
                                            defStyle, 0);
 
         boolean focusable =  != null ||  != null;
         boolean clickable = focusable;
         boolean longClickable = focusable;
 
         n = a.getIndexCount();
         for (int i = 0; i < ni++) {
             int attr = a.getIndex(i);
 
             switch (attr) {
             case .....:
                 focusable = a.getBoolean(attrfocusable);
                 break;
 
             case .....:
                 clickable = a.getBoolean(attrclickable);
                 break;
 
             case .....:
                 longClickable = a.getBoolean(attrlongClickable);
                 break;
             }
         }
         a.recycle();
 
         setFocusable(focusable);
         setClickable(clickable);
         setLongClickable(longClickable);
 
         prepareCursorControllers();
     }
 
     private void setTypefaceByIndex(int typefaceIndexint styleIndex) {
         Typeface tf = null;
         switch (typefaceIndex) {
             case :
                 tf = .;
                 break;
 
             case :
                 tf = .;
                 break;
 
             case :
                 tf = .;
                 break;
         }
 
         setTypeface(tfstyleIndex);
     }

    
Sets the typeface and style in which the text should be displayed, and turns on the fake bold and italic bits in the Paint if the Typeface that you provided does not have all the bits in the style that you specified.

Attr:
ref android.R.styleable#TextView_typeface
Attr:
ref android.R.styleable#TextView_textStyle
 
     public void setTypeface(Typeface tfint style) {
         if (style > 0) {
             if (tf == null) {
                 tf = Typeface.defaultFromStyle(style);
             } else {
                 tf = Typeface.create(tfstyle);
             }
 
             setTypeface(tf);
             // now compute what (if any) algorithmic styling is needed
             int typefaceStyle = tf != null ? tf.getStyle() : 0;
             int need = style & ~typefaceStyle;
            .setFakeBoldText((need & .) != 0);
            .setTextSkewX((need & .) != 0 ? -0.25f : 0);
        } else {
            .setFakeBoldText(false);
            .setTextSkewX(0);
            setTypeface(tf);
        }
    }

    
Subclasses override this to specify that they have a KeyListener by default even if not specifically called for in the XML options.
    protected boolean getDefaultEditable() {
        return false;
    }

    
Subclasses override this to specify a default movement method.
        return null;
    }

    
Return the text the TextView is displaying. If setText() was called with an argument of BufferType.SPANNABLE or BufferType.EDITABLE, you can cast the return value from this method to Spannable or Editable, respectively. Note: The content of the return value should not be modified. If you want a modifiable one, you should make your own copy first.
    public CharSequence getText() {
        return ;
    }

    
Returns the length, in characters, of the text managed by this TextView
    public int length() {
        return .length();
    }

    
Return the text the TextView is displaying as an Editable object. If the text is not editable, null is returned.

See also:
getText()
    public Editable getEditableText() {
        return ( instanceof Editable) ? (Editable) : null;
    }

    

Returns:
the height of one standard line in pixels. Note that markup within the text can cause individual lines to be taller or shorter than this height, and the layout may contain additional first- or last-line padding.
    public int getLineHeight() {
        return FastMath.round(.getFontMetricsInt(null) * 
                          + );
    }

    

Returns:
the Layout that is currently being used to display the text. This can be null if the text or width has recently changes.
    public final Layout getLayout() {
        return ;
    }

    

Returns:
the current key listener for this TextView. This will frequently be null for non-EditText TextViews.
    public final KeyListener getKeyListener() {
        return ;
    }

    
Sets the key listener to be used with this TextView. This can be null to disallow user input. Note that this method has significant and subtle interactions with soft keyboards and other input method: see KeyListener.getContentType() for important details. Calling this method will replace the current content type of the text view with the content type returned by the key listener.

Be warned that if you want a TextView with a key listener or movement method not to be focusable, or if you want a TextView without a key listener or movement method to be focusable, you must call android.view.View.setFocusable(boolean) again after calling this to get the focusability back the way you want it.

Attr:
ref android.R.styleable#TextView_numeric
Attr:
ref android.R.styleable#TextView_digits
Attr:
ref android.R.styleable#TextView_phoneNumber
Attr:
ref android.R.styleable#TextView_inputMethod
Attr:
ref android.R.styleable#TextView_capitalize
Attr:
ref android.R.styleable#TextView_autoText
    public void setKeyListener(KeyListener input) {
        setKeyListenerOnly(input);
        if (input != null) {
            try {
                 = .getInputType();
            } catch (IncompatibleClassChangeError e) {
                 = .;
            }
            if ((&.)
                    == .) {
                if () {
                     &= ~.;
                } else {
                     |= .;
                }
            }
        } else {
             = .;
        }
        InputMethodManager imm = InputMethodManager.peekInstance();
        if (imm != nullimm.restartInput(this);
    }
    private void setKeyListenerOnly(KeyListener input) {
         = input;
        if ( != null && !( instanceof Editable))
            setText();
        setFilters((Editable);
    }

    

Returns:
the movement method being used for this TextView. This will frequently be null for non-EditText TextViews.
    public final MovementMethod getMovementMethod() {
        return ;
    }

    
Sets the movement method (arrow key handler) to be used for this TextView. This can be null to disallow using the arrow keys to move the cursor or scroll the view.

Be warned that if you want a TextView with a key listener or movement method not to be focusable, or if you want a TextView without a key listener or movement method to be focusable, you must call android.view.View.setFocusable(boolean) again after calling this to get the focusability back the way you want it.

    public final void setMovementMethod(MovementMethod movement) {
         = movement;
        if ( != null && !( instanceof Spannable))
            setText();
        // SelectionModifierCursorController depends on textCanBeSelected, which depends on mMovement
        prepareCursorControllers();
    }
    private void fixFocusableAndClickableSettings() {
        if (( != null) ||  != null) {
            setFocusable(true);
            setClickable(true);
            setLongClickable(true);
        } else {
            setFocusable(false);
            setClickable(false);
            setLongClickable(false);
        }
    }

    

Returns:
the current transformation method for this TextView. This will frequently be null except for single-line and password fields.
        return ;
    }

    
Sets the transformation that is applied to the text that this TextView is displaying.

Attr:
ref android.R.styleable#TextView_password
Attr:
ref android.R.styleable#TextView_singleLine
    public final void setTransformationMethod(TransformationMethod method) {
        if (method == ) {
            // Avoid the setText() below if the transformation is
            // the same.
            return;
        }
        if ( != null) {
            if ( instanceof Spannable) {
                ((Spannable).removeSpan();
            }
        }
         = method;
        setText();
    }

    
Returns the top padding of the view, plus space for the top Drawable if any.
    public int getCompoundPaddingTop() {
        final Drawables dr = ;
        if (dr == null || dr.mDrawableTop == null) {
            return ;
        } else {
            return  + dr.mDrawablePadding + dr.mDrawableSizeTop;
        }
    }

    
Returns the bottom padding of the view, plus space for the bottom Drawable if any.
    public int getCompoundPaddingBottom() {
        final Drawables dr = ;
        if (dr == null || dr.mDrawableBottom == null) {
            return ;
        } else {
            return  + dr.mDrawablePadding + dr.mDrawableSizeBottom;
        }
    }

    
Returns the left padding of the view, plus space for the left Drawable if any.
    public int getCompoundPaddingLeft() {
        final Drawables dr = ;
        if (dr == null || dr.mDrawableLeft == null) {
            return ;
        } else {
            return  + dr.mDrawablePadding + dr.mDrawableSizeLeft;
        }
    }

    
Returns the right padding of the view, plus space for the right Drawable if any.
    public int getCompoundPaddingRight() {
        final Drawables dr = ;
        if (dr == null || dr.mDrawableRight == null) {
            return ;
        } else {
            return  + dr.mDrawablePadding + dr.mDrawableSizeRight;
        }
    }

    
Returns the extended top padding of the view, including both the top Drawable if any and any extra space to keep more than maxLines of text from showing. It is only valid to call this after measuring.
    public int getExtendedPaddingTop() {
        if ( != ) {
            return getCompoundPaddingTop();
        }
        if (.getLineCount() <= ) {
            return getCompoundPaddingTop();
        }
        int top = getCompoundPaddingTop();
        int bottom = getCompoundPaddingBottom();
        int viewht = getHeight() - top - bottom;
        int layoutht = .getLineTop();
        if (layoutht >= viewht) {
            return top;
        }
        final int gravity =  & .;
        if (gravity == .) {
            return top;
        } else if (gravity == .) {
            return top + viewht - layoutht;
        } else { // (gravity == Gravity.CENTER_VERTICAL)
            return top + (viewht - layoutht) / 2;
        }
    }

    
Returns the extended bottom padding of the view, including both the bottom Drawable if any and any extra space to keep more than maxLines of text from showing. It is only valid to call this after measuring.
    public int getExtendedPaddingBottom() {
        if ( != ) {
            return getCompoundPaddingBottom();
        }
        if (.getLineCount() <= ) {
            return getCompoundPaddingBottom();
        }
        int top = getCompoundPaddingTop();
        int bottom = getCompoundPaddingBottom();
        int viewht = getHeight() - top - bottom;
        int layoutht = .getLineTop();
        if (layoutht >= viewht) {
            return bottom;
        }
        final int gravity =  & .;
        if (gravity == .) {
            return bottom + viewht - layoutht;
        } else if (gravity == .) {
            return bottom;
        } else { // (gravity == Gravity.CENTER_VERTICAL)
            return bottom + (viewht - layoutht) / 2;
        }
    }

    
Returns the total left padding of the view, including the left Drawable if any.
    public int getTotalPaddingLeft() {
        return getCompoundPaddingLeft();
    }

    
Returns the total right padding of the view, including the right Drawable if any.
    public int getTotalPaddingRight() {
        return getCompoundPaddingRight();
    }

    
Returns the total top padding of the view, including the top Drawable if any, the extra space to keep more than maxLines from showing, and the vertical offset for gravity, if any.
    public int getTotalPaddingTop() {
        return getExtendedPaddingTop() + getVerticalOffset(true);
    }

    
Returns the total bottom padding of the view, including the bottom Drawable if any, the extra space to keep more than maxLines from showing, and the vertical offset for gravity, if any.
    public int getTotalPaddingBottom() {
        return getExtendedPaddingBottom() + getBottomVerticalOffset(true);
    }

    
Sets the Drawables (if any) to appear to the left of, above, to the right of, and below the text. Use null if you do not want a Drawable there. The Drawables must already have had android.graphics.drawable.Drawable.setBounds(android.graphics.Rect) called.

Attr:
ref android.R.styleable#TextView_drawableLeft
Attr:
ref android.R.styleable#TextView_drawableTop
Attr:
ref android.R.styleable#TextView_drawableRight
Attr:
ref android.R.styleable#TextView_drawableBottom
    public void setCompoundDrawables(Drawable leftDrawable top,
                                     Drawable rightDrawable bottom) {
        Drawables dr = ;
        final boolean drawables = left != null || top != null
                || right != null || bottom != null;
        if (!drawables) {
            // Clearing drawables...  can we free the data structure?
            if (dr != null) {
                if (dr.mDrawablePadding == 0) {
                     = null;
                } else {
                    // We need to retain the last set padding, so just clear
                    // out all of the fields in the existing structure.
                    if (dr.mDrawableLeft != nulldr.mDrawableLeft.setCallback(null);
                    dr.mDrawableLeft = null;
                    if (dr.mDrawableTop != nulldr.mDrawableTop.setCallback(null);
                    dr.mDrawableTop = null;
                    if (dr.mDrawableRight != nulldr.mDrawableRight.setCallback(null);
                    dr.mDrawableRight = null;
                    if (dr.mDrawableBottom != nulldr.mDrawableBottom.setCallback(null);
                    dr.mDrawableBottom = null;
                    dr.mDrawableSizeLeft = dr.mDrawableHeightLeft = 0;
                    dr.mDrawableSizeRight = dr.mDrawableHeightRight = 0;
                    dr.mDrawableSizeTop = dr.mDrawableWidthTop = 0;
                    dr.mDrawableSizeBottom = dr.mDrawableWidthBottom = 0;
                }
            }
        } else {
            if (dr == null) {
                 = dr = new Drawables();
            }
            if (dr.mDrawableLeft != left && dr.mDrawableLeft != null) {
                dr.mDrawableLeft.setCallback(null);
            }
            dr.mDrawableLeft = left;
            if (dr.mDrawableTop != top && dr.mDrawableTop != null) {
                dr.mDrawableTop.setCallback(null);
            }
            dr.mDrawableTop = top;
            if (dr.mDrawableRight != right && dr.mDrawableRight != null) {
                dr.mDrawableRight.setCallback(null);
            }
            dr.mDrawableRight = right;
            if (dr.mDrawableBottom != bottom && dr.mDrawableBottom != null) {
                dr.mDrawableBottom.setCallback(null);
            }
            dr.mDrawableBottom = bottom;
            final Rect compoundRect = dr.mCompoundRect;
            int[] state;
            state = getDrawableState();
            if (left != null) {
                left.setState(state);
                left.copyBounds(compoundRect);
                left.setCallback(this);
                dr.mDrawableSizeLeft = compoundRect.width();
                dr.mDrawableHeightLeft = compoundRect.height();
            } else {
                dr.mDrawableSizeLeft = dr.mDrawableHeightLeft = 0;
            }
            if (right != null) {
                right.setState(state);
                right.copyBounds(compoundRect);
                right.setCallback(this);
                dr.mDrawableSizeRight = compoundRect.width();
                dr.mDrawableHeightRight = compoundRect.height();
            } else {
                dr.mDrawableSizeRight = dr.mDrawableHeightRight = 0;
            }
            if (top != null) {
                top.setState(state);
                top.copyBounds(compoundRect);
                top.setCallback(this);
                dr.mDrawableSizeTop = compoundRect.height();
                dr.mDrawableWidthTop = compoundRect.width();
            } else {
                dr.mDrawableSizeTop = dr.mDrawableWidthTop = 0;
            }
            if (bottom != null) {
                bottom.setState(state);
                bottom.copyBounds(compoundRect);
                bottom.setCallback(this);
                dr.mDrawableSizeBottom = compoundRect.height();
                dr.mDrawableWidthBottom = compoundRect.width();
            } else {
                dr.mDrawableSizeBottom = dr.mDrawableWidthBottom = 0;
            }
        }
        invalidate();
        requestLayout();
    }

    
Sets the Drawables (if any) to appear to the left of, above, to the right of, and below the text. Use 0 if you do not want a Drawable there. The Drawables' bounds will be set to their intrinsic bounds.

Parameters:
left Resource identifier of the left Drawable.
top Resource identifier of the top Drawable.
right Resource identifier of the right Drawable.
bottom Resource identifier of the bottom Drawable.
Attr:
ref android.R.styleable#TextView_drawableLeft
Attr:
ref android.R.styleable#TextView_drawableTop
Attr:
ref android.R.styleable#TextView_drawableRight
Attr:
ref android.R.styleable#TextView_drawableBottom
    public void setCompoundDrawablesWithIntrinsicBounds(int leftint topint rightint bottom) {
        final Resources resources = getContext().getResources();
        setCompoundDrawablesWithIntrinsicBounds(left != 0 ? resources.getDrawable(left) : null,
                top != 0 ? resources.getDrawable(top) : null,
                right != 0 ? resources.getDrawable(right) : null,
                bottom != 0 ? resources.getDrawable(bottom) : null);
    }

    
Sets the Drawables (if any) to appear to the left of, above, to the right of, and below the text. Use null if you do not want a Drawable there. The Drawables' bounds will be set to their intrinsic bounds.

Attr:
ref android.R.styleable#TextView_drawableLeft
Attr:
ref android.R.styleable#TextView_drawableTop
Attr:
ref android.R.styleable#TextView_drawableRight
Attr:
ref android.R.styleable#TextView_drawableBottom
            Drawable rightDrawable bottom) {
        if (left != null) {
            left.setBounds(0, 0, left.getIntrinsicWidth(), left.getIntrinsicHeight());
        }
        if (right != null) {
            right.setBounds(0, 0, right.getIntrinsicWidth(), right.getIntrinsicHeight());
        }
        if (top != null) {
            top.setBounds(0, 0, top.getIntrinsicWidth(), top.getIntrinsicHeight());
        }
        if (bottom != null) {
            bottom.setBounds(0, 0, bottom.getIntrinsicWidth(), bottom.getIntrinsicHeight());
        }
        setCompoundDrawables(lefttoprightbottom);
    }

    
Returns drawables for the left, top, right, and bottom borders.
    public Drawable[] getCompoundDrawables() {
        final Drawables dr = ;
        if (dr != null) {
            return new Drawable[] {
                dr.mDrawableLeftdr.mDrawableTopdr.mDrawableRightdr.mDrawableBottom
            };
        } else {
            return new Drawable[] { nullnullnullnull };
        }
    }

    
Sets the size of the padding between the compound drawables and the text.

Attr:
ref android.R.styleable#TextView_drawablePadding
    public void setCompoundDrawablePadding(int pad) {
        Drawables dr = ;
        if (pad == 0) {
            if (dr != null) {
                dr.mDrawablePadding = pad;
            }
        } else {
            if (dr == null) {
                 = dr = new Drawables();
            }
            dr.mDrawablePadding = pad;
        }
        invalidate();
        requestLayout();
    }

    
Returns the padding between the compound drawables and the text.
    public int getCompoundDrawablePadding() {
        final Drawables dr = ;
        return dr != null ? dr.mDrawablePadding : 0;
    }
    @Override
    public void setPadding(int leftint topint rightint bottom) {
        if (left !=  ||
            right !=  ||
            top !=  ||
            bottom != ) {
            nullLayouts();
        }
        // the super call will requestLayout()
        super.setPadding(lefttoprightbottom);
        invalidate();
    }

    
Gets the autolink mask of the text. See android.text.util.Linkify.ALL and peers for possible values.

Attr:
ref android.R.styleable#TextView_autoLink
    public final int getAutoLinkMask() {
        return ;
    }

    
Sets the text color, size, style, hint color, and highlight color from the specified TextAppearance resource.
    public void setTextAppearance(Context contextint resid) {
        TypedArray appearance =
            context.obtainStyledAttributes(resid,
                                           .....);
        int color;
        ColorStateList colors;
        int ts;
        color = appearance.getColor(....., 0);
        if (color != 0) {
            setHighlightColor(color);
        }
        colors = appearance.getColorStateList(.....
                                              );
        if (colors != null) {
            setTextColor(colors);
        }
        ts = appearance.getDimensionPixelSize(.....
                                              , 0);
        if (ts != 0) {
            setRawTextSize(ts);
        }
        colors = appearance.getColorStateList(.....
                                              );
        if (colors != null) {
            setHintTextColor(colors);
        }
        colors = appearance.getColorStateList(.....
                                              );
        if (colors != null) {
            setLinkTextColor(colors);
        }
        int typefaceIndexstyleIndex;
        typefaceIndex = appearance.getInt(.....
                                          , -1);
        styleIndex = appearance.getInt(.....
                                       , -1);
        setTypefaceByIndex(typefaceIndexstyleIndex);
        appearance.recycle();
    }

    

Returns:
the size (in pixels) of the default text size in this TextView.
    public float getTextSize() {
        return .getTextSize();
    }

    
Set the default text size to the given value, interpreted as "scaled pixel" units. This size is adjusted based on the current density and user font size preference.

Parameters:
size The scaled pixel size.
Attr:
ref android.R.styleable#TextView_textSize
    public void setTextSize(float size) {
        setTextSize(.size);
    }

    
Set the default text size to a given unit and value. See android.util.TypedValue for the possible dimension units.

Parameters:
unit The desired dimension unit.
size The desired size in the given units.
Attr:
ref android.R.styleable#TextView_textSize
    public void setTextSize(int unitfloat size) {
        Context c = getContext();
        Resources r;
        if (c == null)
            r = Resources.getSystem();
        else
            r = c.getResources();
        setRawTextSize(TypedValue.applyDimension(
            unitsizer.getDisplayMetrics()));
    }
    private void setRawTextSize(float size) {
        if (size != .getTextSize()) {
            .setTextSize(size);
            if ( != null) {
                nullLayouts();
                requestLayout();
                invalidate();
            }
        }
    }

    

Returns:
the extent by which text is currently being stretched horizontally. This will usually be 1.
    public float getTextScaleX() {
        return .getTextScaleX();
    }

    
Sets the extent by which text should be stretched horizontally.

Attr:
ref android.R.styleable#TextView_textScaleX
    public void setTextScaleX(float size) {
        if (size != .getTextScaleX()) {
             = true;
            .setTextScaleX(size);
            if ( != null) {
                nullLayouts();
                requestLayout();
                invalidate();
            }
        }
    }

    
Sets the typeface and style in which the text should be displayed. Note that not all Typeface families actually have bold and italic variants, so you may need to use setTypeface(android.graphics.Typeface,int) to get the appearance that you actually want.

Attr:
ref android.R.styleable#TextView_typeface
Attr:
ref android.R.styleable#TextView_textStyle
    public void setTypeface(Typeface tf) {
        if (.getTypeface() != tf) {
            .setTypeface(tf);
            if ( != null) {
                nullLayouts();
                requestLayout();
                invalidate();
            }
        }
    }

    

Returns:
the current typeface and style in which the text is being displayed.
    public Typeface getTypeface() {
        return .getTypeface();
    }

    
Sets the text color for all the states (normal, selected, focused) to be this color.

Attr:
ref android.R.styleable#TextView_textColor
    public void setTextColor(int color) {
         = ColorStateList.valueOf(color);
        updateTextColors();
    }

    
Sets the text color.

Attr:
ref android.R.styleable#TextView_textColor
    public void setTextColor(ColorStateList colors) {
        if (colors == null) {
            throw new NullPointerException();
        }
         = colors;
        updateTextColors();
    }

    
Return the set of text colors.

Returns:
Returns the set of text colors.
    public final ColorStateList getTextColors() {
        return ;
    }

    

Return the current color selected for normal text.

Returns:
Returns the current text color.
    public final int getCurrentTextColor() {
        return ;
    }

    
Sets the color used to display the selection highlight.

Attr:
ref android.R.styleable#TextView_textColorHighlight
    public void setHighlightColor(int color) {
        if ( != color) {
             = color;
            invalidate();
        }
    }

    
Gives the text a shadow of the specified radius and color, the specified distance from its normal position.

Attr:
ref android.R.styleable#TextView_shadowColor
Attr:
ref android.R.styleable#TextView_shadowDx
Attr:
ref android.R.styleable#TextView_shadowDy
Attr:
ref android.R.styleable#TextView_shadowRadius
    public void setShadowLayer(float radiusfloat dxfloat dyint color) {
        .setShadowLayer(radiusdxdycolor);
         = radius;
         = dx;
         = dy;
        invalidate();
    }

    

Returns:
the base paint used for the text. Please use this only to consult the Paint's properties and not to change them.
    public TextPaint getPaint() {
        return ;
    }

    
Sets the autolink mask of the text. See android.text.util.Linkify.ALL and peers for possible values.

Attr:
ref android.R.styleable#TextView_autoLink
    public final void setAutoLinkMask(int mask) {
         = mask;
    }

    
Sets whether the movement method will automatically be set to android.text.method.LinkMovementMethod if setAutoLinkMask(int) has been set to nonzero and links are detected in setText(java.lang.CharSequence). The default is true.

Attr:
ref android.R.styleable#TextView_linksClickable
    public final void setLinksClickable(boolean whether) {
         = whether;
    }

    
Returns whether the movement method will automatically be set to android.text.method.LinkMovementMethod if setAutoLinkMask(int) has been set to nonzero and links are detected in setText(java.lang.CharSequence). The default is true.

Attr:
ref android.R.styleable#TextView_linksClickable
    public final boolean getLinksClickable() {
        return ;
    }

    
Returns the list of URLSpans attached to the text (by android.text.util.Linkify or otherwise) if any. You can call android.text.style.URLSpan.getURL() on them to find where they link to or use android.text.Spanned.getSpanStart(java.lang.Object) and android.text.Spanned.getSpanEnd(java.lang.Object) to find the region of the text they are attached to.
    public URLSpan[] getUrls() {
        if ( instanceof Spanned) {
            return ((Spanned).getSpans(0, .length(), URLSpan.class);
        } else {
            return new URLSpan[0];
        }
    }

    
Sets the color of the hint text.

Attr:
ref android.R.styleable#TextView_textColorHint
    public final void setHintTextColor(int color) {
         = ColorStateList.valueOf(color);
        updateTextColors();
    }

    
Sets the color of the hint text.

Attr:
ref android.R.styleable#TextView_textColorHint
    public final void setHintTextColor(ColorStateList colors) {
         = colors;
        updateTextColors();
    }

    

Return the color used to paint the hint text.

Returns:
Returns the list of hint text colors.
    public final ColorStateList getHintTextColors() {
        return ;
    }

    

Return the current color selected to paint the hint text.

Returns:
Returns the current hint text color.
    public final int getCurrentHintTextColor() {
        return  != null ?  : ;
    }

    
Sets the color of links in the text.

Attr:
ref android.R.styleable#TextView_textColorLink
    public final void setLinkTextColor(int color) {
         = ColorStateList.valueOf(color);
        updateTextColors();
    }

    
Sets the color of links in the text.

Attr:
ref android.R.styleable#TextView_textColorLink
    public final void setLinkTextColor(ColorStateList colors) {
         = colors;
        updateTextColors();
    }

    

Returns the color used to paint links in the text.

Returns:
Returns the list of link text colors.
    public final ColorStateList getLinkTextColors() {
        return ;
    }

    
Sets the horizontal alignment of the text and the vertical gravity that will be used when there is extra space in the TextView beyond what is required for the text itself.

See also:
android.view.Gravity
Attr:
ref android.R.styleable#TextView_gravity
    public void setGravity(int gravity) {
        if ((gravity & .) == 0) {
            gravity |= .;
        }
        if ((gravity & .) == 0) {
            gravity |= .;
        }
        boolean newLayout = false;
        if ((gravity & .) !=
            ( & .)) {
            newLayout = true;
        }
        if (gravity != ) {
            invalidate();
        }
         = gravity;
        if ( != null && newLayout) {
            // XXX this is heavy-handed because no actual content changes.
            int want = .getWidth();
            int hintWant =  == null ? 0 : .getWidth();
            makeNewLayout(wanthintWant,
                           -  - getCompoundPaddingLeft() -
                          getCompoundPaddingRight(), true);
        }
    }

    
Returns the horizontal and vertical alignment of this TextView.

See also:
android.view.Gravity
Attr:
ref android.R.styleable#TextView_gravity
    public int getGravity() {
        return ;
    }

    

Returns:
the flags on the Paint being used to display the text.
See also:
android.graphics.Paint.getFlags()
    public int getPaintFlags() {
        return .getFlags();
    }

    
Sets flags on the Paint being used to display the text and reflows the text if they are different from the old flags.

    public void setPaintFlags(int flags) {
        if (.getFlags() != flags) {
            .setFlags(flags);
            if ( != null) {
                nullLayouts();
                requestLayout();
                invalidate();
            }
        }
    }

    
Sets whether the text should be allowed to be wider than the View is. If false, it will be wrapped to the width of the View.

Attr:
ref android.R.styleable#TextView_scrollHorizontally
    public void setHorizontallyScrolling(boolean whether) {
         = whether;
        if ( != null) {
            nullLayouts();
            requestLayout();
            invalidate();
        }
    }

    
Makes the TextView at least this many lines tall

Attr:
ref android.R.styleable#TextView_minLines
    public void setMinLines(int minlines) {
         = minlines;
         = ;
        requestLayout();
        invalidate();
    }

    
Makes the TextView at least this many pixels tall

Attr:
ref android.R.styleable#TextView_minHeight
    public void setMinHeight(int minHeight) {
         = minHeight;
         = ;
        requestLayout();
        invalidate();
    }

    
Makes the TextView at most this many lines tall

Attr:
ref android.R.styleable#TextView_maxLines
    public void setMaxLines(int maxlines) {
         = maxlines;
         = ;
        requestLayout();
        invalidate();
    }

    
Makes the TextView at most this many pixels tall

Attr:
ref android.R.styleable#TextView_maxHeight
    public void setMaxHeight(int maxHeight) {
         = maxHeight;
         = ;
        requestLayout();
        invalidate();
    }

    
Makes the TextView exactly this many lines tall

Attr:
ref android.R.styleable#TextView_lines
    public void setLines(int lines) {
         =  = lines;
         =  = ;
        requestLayout();
        invalidate();
    }

    
Makes the TextView exactly this many pixels tall. You could do the same thing by specifying this number in the LayoutParams.

Attr:
ref android.R.styleable#TextView_height
    public void setHeight(int pixels) {
         =  = pixels;
         =  = ;
        requestLayout();
        invalidate();
    }

    
Makes the TextView at least this many ems wide

Attr:
ref android.R.styleable#TextView_minEms
    public void setMinEms(int minems) {
         = minems;
         = ;
        requestLayout();
        invalidate();
    }

    
Makes the TextView at least this many pixels wide

Attr:
ref android.R.styleable#TextView_minWidth
    public void setMinWidth(int minpixels) {
         = minpixels;
         = ;
        requestLayout();
        invalidate();
    }

    
Makes the TextView at most this many ems wide

Attr:
ref android.R.styleable#TextView_maxEms
    public void setMaxEms(int maxems) {
         = maxems;
         = ;
        requestLayout();
        invalidate();
    }

    
Makes the TextView at most this many pixels wide

Attr:
ref android.R.styleable#TextView_maxWidth
    public void setMaxWidth(int maxpixels) {
         = maxpixels;
         = ;
        requestLayout();
        invalidate();
    }

    
Makes the TextView exactly this many ems wide

Attr:
ref android.R.styleable#TextView_ems
    public void setEms(int ems) {
         =  = ems;
         =  = ;
        requestLayout();
        invalidate();
    }

    
Makes the TextView exactly this many pixels wide. You could do the same thing by specifying this number in the LayoutParams.

Attr:
ref android.R.styleable#TextView_width
    public void setWidth(int pixels) {
         =  = pixels;
         =  = ;
        requestLayout();
        invalidate();
    }


    
Sets line spacing for this TextView. Each line will have its height multiplied by mult and have add added to it.

Attr:
ref android.R.styleable#TextView_lineSpacingExtra
Attr:
ref android.R.styleable#TextView_lineSpacingMultiplier
    public void setLineSpacing(float addfloat mult) {
         = mult;
         = add;
        if ( != null) {
            nullLayouts();
            requestLayout();
            invalidate();
        }
    }

    
Convenience method: Append the specified text to the TextView's display buffer, upgrading it to BufferType.EDITABLE if it was not already editable.
    public final void append(CharSequence text) {
        append(text, 0, text.length());
    }

    
Convenience method: Append the specified text slice to the TextView's display buffer, upgrading it to BufferType.EDITABLE if it was not already editable.
    public void append(CharSequence textint startint end) {
        if (!( instanceof Editable)) {
            setText(.);
        }
        ((Editable).append(textstartend);
    }
    private void updateTextColors() {
        boolean inval = false;
        int color = .getColorForState(getDrawableState(), 0);
        if (color != ) {
             = color;
            inval = true;
        }
        if ( != null) {
            color = .getColorForState(getDrawableState(), 0);
            if (color != .) {
                . = color;
                inval = true;
            }
        }
        if ( != null) {
            color = .getColorForState(getDrawableState(), 0);
            if (color !=  && .length() == 0) {
                 = color;
                inval = true;
            }
        }
        if (inval) {
            invalidate();
        }
    }
    @Override
    protected void drawableStateChanged() {
        super.drawableStateChanged();
        if ( != null && .isStateful()
                || ( != null && .isStateful())
                || ( != null && .isStateful())) {
            updateTextColors();
        }
        final Drawables dr = ;
        if (dr != null) {
            int[] state = getDrawableState();
            if (dr.mDrawableTop != null && dr.mDrawableTop.isStateful()) {
                dr.mDrawableTop.setState(state);
            }
            if (dr.mDrawableBottom != null && dr.mDrawableBottom.isStateful()) {
                dr.mDrawableBottom.setState(state);
            }
            if (dr.mDrawableLeft != null && dr.mDrawableLeft.isStateful()) {
                dr.mDrawableLeft.setState(state);
            }
            if (dr.mDrawableRight != null && dr.mDrawableRight.isStateful()) {
                dr.mDrawableRight.setState(state);
            }
        }
    }

    
User interface state that is stored by TextView for implementing android.view.View.onSaveInstanceState().
    public static class SavedState extends BaseSavedState {
        int selStart;
        int selEnd;
        CharSequence text;
        boolean frozenWithFocus;
        CharSequence error;
        SavedState(Parcelable superState) {
            super(superState);
        }
        @Override
        public void writeToParcel(Parcel outint flags) {
            super.writeToParcel(outflags);
            out.writeInt();
            out.writeInt();
            out.writeInt( ? 1 : 0);
            TextUtils.writeToParcel(outflags);
            if ( == null) {
                out.writeInt(0);
            } else {
                out.writeInt(1);
                TextUtils.writeToParcel(outflags);
            }
        }
        @Override
        public String toString() {
            String str = "TextView.SavedState{"
                    + Integer.toHexString(System.identityHashCode(this))
                    + " start=" +  + " end=" + ;
            if ( != null) {
                str += " text=" + ;
            }
            return str + "}";
        }
        @SuppressWarnings("hiding")
        public static final Parcelable.Creator<SavedStateCREATOR
                = new Parcelable.Creator<SavedState>() {
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }
            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
        private SavedState(Parcel in) {
            super(in);
             = in.readInt();
             = in.readInt();
             = (in.readInt() != 0);
             = ..createFromParcel(in);
            if (in.readInt() != 0) {
                 = ..createFromParcel(in);
            }
        }
    }
    @Override
    public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        // Save state if we are forced to
        boolean save = ;
        int start = 0;
        int end = 0;
        if ( != null) {
            start = getSelectionStart();
            end = getSelectionEnd();
            if (start >= 0 || end >= 0) {
                // Or save state if there is a selection
                save = true;
            }
        }
        if (save) {
            SavedState ss = new SavedState(superState);
            // XXX Should also save the current scroll position!
            ss.selStart = start;
            ss.selEnd = end;
            if ( instanceof Spanned) {
                /*
                 * Calling setText() strips off any ChangeWatchers;
                 * strip them now to avoid leaking references.
                 * But do it to a copy so that if there are any
                 * further changes to the text of this view, it
                 * won't get into an inconsistent state.
                 */
                Spannable sp = new SpannableString();
                for (ChangeWatcher cw :
                     sp.getSpans(0, sp.length(), ChangeWatcher.class)) {
                    sp.removeSpan(cw);
                }
                ss.text = sp;
            } else {
                ss.text = .toString();
            }
            if (isFocused() && start >= 0 && end >= 0) {
                ss.frozenWithFocus = true;
            }
            ss.error = ;
            return ss;
        }
        return superState;
    }
    @Override
    public void onRestoreInstanceState(Parcelable state) {
        if (!(state instanceof SavedState)) {
            super.onRestoreInstanceState(state);
            return;
        }
        SavedState ss = (SavedState)state;
        super.onRestoreInstanceState(ss.getSuperState());
        // XXX restore buffer type too, as well as lots of other stuff
        if (ss.text != null) {
            setText(ss.text);
        }
        if (ss.selStart >= 0 && ss.selEnd >= 0) {
            if ( instanceof Spannable) {
                int len = .length();
                if (ss.selStart > len || ss.selEnd > len) {
                    String restored = "";
                    if (ss.text != null) {
                        restored = "(restored) ";
                    }
                    Log.e("Saved cursor position " + ss.selStart +
                          "/" + ss.selEnd + " out of range for " + restored +
                          "text " + );
                } else {
                    Selection.setSelection((Spannabless.selStart,
                                           ss.selEnd);
                    if (ss.frozenWithFocus) {
                         = true;
                    }
                }
            }
        }
        if (ss.error != null) {
            final CharSequence error = ss.error;
            // Display the error later, after the first layout pass
            post(new Runnable() {
                public void run() {
                    setError(error);
                }
            });
        }
    }

    
Control whether this text view saves its entire text contents when freezing to an icicle, in addition to dynamic state such as cursor position. By default this is false, not saving the text. Set to true if the text in the text view is not being saved somewhere else in persistent storage (such as in a content provider) so that if the view is later thawed the user will not lose their data.

Parameters:
freezesText Controls whether a frozen icicle should include the entire text data: true to include it, false to not.
Attr:
ref android.R.styleable#TextView_freezesText
    public void setFreezesText(boolean freezesText) {
         = freezesText;
    }

    
Return whether this text view is including its entire text contents in frozen icicles.

Returns:
Returns true if text is included, false if it isn't.
See also:
setFreezesText(boolean)
    public boolean getFreezesText() {
        return ;
    }
    ///////////////////////////////////////////////////////////////////////////

    
Sets the Factory used to create new Editables.
    public final void setEditableFactory(Editable.Factory factory) {
         = factory;
        setText();
    }

    
Sets the Factory used to create new Spannables.
    public final void setSpannableFactory(Spannable.Factory factory) {
         = factory;
        setText();
    }

    
Sets the string value of the TextView. TextView does not accept HTML-like formatting, which you can do with text strings in XML resource files. To style your strings, attach android.text.style.* objects to a SpannableString, or see the Available Resource Types documentation for an example of setting formatted text in the XML resource file.

Attr:
ref android.R.styleable#TextView_text
    public final void setText(CharSequence text) {
        setText(text);
    }

    
Like setText(java.lang.CharSequence), except that the cursor position (if any) is retained in the new text.

Parameters:
text The new text to place in the text view.
See also:
setText(java.lang.CharSequence)
    public final void setTextKeepState(CharSequence text) {
        setTextKeepState(text);
    }

    
Sets the text that this TextView is to display (see setText(java.lang.CharSequence)) and also sets whether it is stored in a styleable/spannable buffer and whether it is editable.

Attr:
ref android.R.styleable#TextView_text
Attr:
ref android.R.styleable#TextView_bufferType
    public void setText(CharSequence textBufferType type) {
        setText(texttypetrue, 0);
        if ( != null) {
            . = null;
        }
    }
    private void setText(CharSequence textBufferType type,
                         boolean notifyBeforeint oldlen) {
        if (text == null) {
            text = "";
        }
        if (!.setTextScaleX(1.0f);
        if (text instanceof Spanned &&
            ((Spannedtext).getSpanStart(..) >= 0) {
            setHorizontalFadingEdgeEnabled(true);
            setEllipsize(..);
        }
        int n = .;
        for (int i = 0; i < ni++) {
            CharSequence out = [i].filter(text, 0, text.length(),
                                                  , 0, 0);
            if (out != null) {
                text = out;
            }
        }
        if (notifyBefore) {
            if ( != null) {
                oldlen = .length();
                sendBeforeTextChanged(, 0, oldlentext.length());
            } else {
                sendBeforeTextChanged("", 0, 0, text.length());
            }
        }
        boolean needEditableForNotification = false;
        if ( != null && .size() != 0) {
            needEditableForNotification = true;
        }
        if (type == . ||  != null ||
            needEditableForNotification) {
            Editable t = .newEditable(text);
            text = t;
            setFilters(t);
            InputMethodManager imm = InputMethodManager.peekInstance();
            if (imm != nullimm.restartInput(this);
        } else if (type == . ||  != null) {
            text = .newSpannable(text);
        } else if (!(text instanceof CharWrapper)) {
            text = TextUtils.stringOrSpannedString(text);
        }
        if ( != 0) {
            Spannable s2;
            if (type == . || text instanceof Spannable) {
                s2 = (Spannabletext;
            } else {
                s2 = .newSpannable(text);
            }
            if (Linkify.addLinks(s2)) {
                text = s2;
                type = (type == .) ? . : .;
                /*
                 * We must go ahead and set the text before changing the
                 * movement method, because setMovementMethod() may call
                 * setText() again to try to upgrade the buffer type.
                 */
                 = text;
                if () {
                    setMovementMethod(LinkMovementMethod.getInstance());
                }
            }
        }
         = type;
         = text;
        if ( == null)
             = text;
        else
             = .getTransformation(textthis);
        final int textLength = text.length();
        if (text instanceof Spannable) {
            Spannable sp = (Spannabletext;
            // Remove any ChangeWatchers that might have come
            // from other TextViews.
            final ChangeWatcher[] watchers = sp.getSpans(0, sp.length(), ChangeWatcher.class);
            final int count = watchers.length;
            for (int i = 0; i < counti++)
                sp.removeSpan(watchers[i]);
            if ( == null)
                 = new ChangeWatcher();
            sp.setSpan(, 0, textLength. |
                       ( << .));
            if ( != null) {
                sp.setSpan(, 0, textLength.);
            }
            if ( != null) {
                sp.setSpan(, 0, textLength.);
            }
            if ( != null) {
                .initialize(this, (Spannabletext);
                /*
                 * Initializing the movement method will have set the
                 * selection, so reset mSelectionMoved to keep that from
                 * interfering with the normal on-focus selection-setting.
                 */
                 = false;
            }
        }
        if ( != null) {
            checkForRelayout();
        }
        sendOnTextChanged(text, 0, oldlentextLength);
        onTextChanged(text, 0, oldlentextLength);
        if (needEditableForNotification) {
            sendAfterTextChanged((Editabletext);
        }
        // SelectionModifierCursorController depends on textCanBeSelected, which depends on text
        prepareCursorControllers();
    }

    
Sets the TextView to display the specified slice of the specified char array. You must promise that you will not change the contents of the array except for right before another call to setText(), since the TextView has no way to know that the text has changed and that it needs to invalidate and re-layout.
    public final void setText(char[] textint startint len) {
        int oldlen = 0;
        if (start < 0 || len < 0 || start + len > text.length) {
            throw new IndexOutOfBoundsException(start + ", " + len);
        }
        /*
         * We must do the before-notification here ourselves because if
         * the old text is a CharWrapper we destroy it before calling
         * into the normal path.
         */
        if ( != null) {
            oldlen = .length();
            sendBeforeTextChanged(, 0, oldlenlen);
        } else {
            sendBeforeTextChanged("", 0, 0, len);
        }
        if ( == null) {
             = new CharWrapper(textstartlen);
        } else {
            .set(textstartlen);
        }
        setText(falseoldlen);
    }
    private static class CharWrapper
            implements CharSequenceGetCharsGraphicsOperations {
        private char[] mChars;
        private int mStartmLength;
        public CharWrapper(char[] charsint startint len) {
             = chars;
             = start;
             = len;
        }
        /* package */ void set(char[] charsint startint len) {
             = chars;
             = start;
             = len;
        }
        public int length() {
            return ;
        }
        public char charAt(int off) {
            return [off + ];
        }
        @Override
        public String toString() {
            return new String();
        }
        public CharSequence subSequence(int startint end) {
            if (start < 0 || end < 0 || start >  || end > ) {
                throw new IndexOutOfBoundsException(start + ", " + end);
            }
            return new String(start + end - start);
        }
        public void getChars(int startint endchar[] bufint off) {
            if (start < 0 || end < 0 || start >  || end > ) {
                throw new IndexOutOfBoundsException(start + ", " + end);
            }
            System.arraycopy(start + bufoffend - start);
        }
        public void drawText(Canvas cint startint end,
                             float xfloat yPaint p) {
            c.drawText(start + end - startxyp);
        }
        public float measureText(int startint endPaint p) {
            return p.measureText(start + end - start);
        }
        public int getTextWidths(int startint endfloat[] widthsPaint p) {
            return p.getTextWidths(start + end - startwidths);
        }
    }

    
    public final void setTextKeepState(CharSequence textBufferType type) {
        int start = getSelectionStart();
        int end = getSelectionEnd();
        int len = text.length();
        setText(texttype);
        if (start >= 0 || end >= 0) {
            if ( instanceof Spannable) {
                Selection.setSelection((Spannable,
                                       Math.max(0, Math.min(startlen)),
                                       Math.max(0, Math.min(endlen)));
            }
        }
    }
    public final void setText(int resid) {
        setText(getContext().getResources().getText(resid));
    }
    public final void setText(int residBufferType type) {
        setText(getContext().getResources().getText(resid), type);
    }

    
Sets the text to be displayed when the text of the TextView is empty. Null means to use the normal empty text. The hint does not currently participate in determining the size of the view.

Attr:
ref android.R.styleable#TextView_hint
    public final void setHint(CharSequence hint) {
         = TextUtils.stringOrSpannedString(hint);
        if ( != null) {
            checkForRelayout();
        }
        if (.length() == 0) {
            invalidate();
        }
    }

    
Sets the text to be displayed when the text of the TextView is empty, from a resource.

Attr:
ref android.R.styleable#TextView_hint
    public final void setHint(int resid) {
        setHint(getContext().getResources().getText(resid));
    }

    
Returns the hint that is displayed when the text of the TextView is empty.

Attr:
ref android.R.styleable#TextView_hint
    public CharSequence getHint() {
        return ;
    }

    
Set the type of the content with a constant as defined for android.view.inputmethod.EditorInfo.inputType. This will take care of changing the key listener, by calling setKeyListener(android.text.method.KeyListener), to match the given content type. If the given content type is android.text.InputType.TYPE_NULL then a soft keyboard will not be displayed for this text view.

See also:
getInputType()
setRawInputType(int)
android.text.InputType
Attr:
ref android.R.styleable#TextView_inputType
    public void setInputType(int type) {
        final boolean wasPassword = isPasswordInputType();
        final boolean wasVisiblePassword = isVisiblePasswordInputType();
        setInputType(typefalse);
        final boolean isPassword = isPasswordInputType(type);
        final boolean isVisiblePassword = isVisiblePasswordInputType(type);
        boolean forceUpdate = false;
        if (isPassword) {
            setTransformationMethod(PasswordTransformationMethod.getInstance());
            setTypefaceByIndex(, 0);
        } else if (isVisiblePassword) {
            if ( == PasswordTransformationMethod.getInstance()) {
                forceUpdate = true;
            }
            setTypefaceByIndex(, 0);
        } else if (wasPassword || wasVisiblePassword) {
            // not in password mode, clean up typeface and transformation
            setTypefaceByIndex(-1, -1);
            if ( == PasswordTransformationMethod.getInstance()) {
                forceUpdate = true;
            }
        }
        
        boolean multiLine = (type&(.
                        | .)) ==
                (.
                        | .);
        
        // We need to update the single line mode if it has changed or we
        // were previously in password mode.
        if ( == multiLine || forceUpdate) {
            // Change single line mode, but only change the transformation if
            // we are not in password mode.
            applySingleLine(!multiLine, !isPassword);
        }
        
        InputMethodManager imm = InputMethodManager.peekInstance();
        if (imm != nullimm.restartInput(this);
    }

    
It would be better to rely on the input type for everything. A password inputType should have a password transformation. We should hence use isPasswordInputType instead of this method. We should: - Call setInputType in setKeyListener instead of changing the input type directly (which would install the correct transformation). - Refuse the installation of a non-password transformation in setTransformation if the input type is password. However, this is like this for legacy reasons and we cannot break existing apps. This method is useful since it matches what the user can see (obfuscated text or not).

Returns:
true if the current transformation method is of the password type.
    private boolean hasPasswordTransformationMethod() {
        return  instanceof PasswordTransformationMethod;
    }
    private boolean isPasswordInputType(int inputType) {
        final int variation = inputType & (.
                | .);
        return variation
                == (.
                        | .);
    }
    private boolean isVisiblePasswordInputType(int inputType) {
        final int variation = inputType & (.
                | .);
        return variation
                == (.
                        | .);
    }

    
Directly change the content type integer of the text view, without modifying any other state.

See also:
setInputType(int)
android.text.InputType
Attr:
ref android.R.styleable#TextView_inputType
    public void setRawInputType(int type) {
         = type;
    }
    private void setInputType(int typeboolean direct) {
        final int cls = type & .;
        KeyListener input;
        if (cls == .) {
            boolean autotext = (type & .) != 0;
            TextKeyListener.Capitalize cap;
            if ((type & .) != 0) {
                cap = ..;
            } else if ((type & .) != 0) {
                cap = ..;
            } else if ((type & .) != 0) {
                cap = ..;
            } else {
                cap = ..;
            }
            input = TextKeyListener.getInstance(autotextcap);
        } else if (cls == .) {
            input = DigitsKeyListener.getInstance(
                    (type & .) != 0,
                    (type & .) != 0);
        } else if (cls == .) {
            switch (type & .) {
                case .:
                    input = DateKeyListener.getInstance();
                    break;
                case .:
                    input = TimeKeyListener.getInstance();
                    break;
                default:
                    input = DateTimeKeyListener.getInstance();
                    break;
            }
        } else if (cls == .) {
            input = DialerKeyListener.getInstance();
        } else {
            input = TextKeyListener.getInstance();
        }
        setRawInputType(type);
        if (direct = input;
        else {
            setKeyListenerOnly(input);
        }
    }

    
Get the type of the content.

    public int getInputType() {
        return ;
    }

    
Change the editor type integer associated with the text view, which will be reported to an IME with android.view.inputmethod.EditorInfo.imeOptions when it has focus.

See also:
getImeOptions()
android.view.inputmethod.EditorInfo
Attr:
ref android.R.styleable#TextView_imeOptions
    public void setImeOptions(int imeOptions) {
        if ( == null) {
             = new InputContentType();
        }
        . = imeOptions;
    }

    
Get the type of the IME editor.

    public int getImeOptions() {
        return  != null
                ? . : .;
    }

    
Change the custom IME action associated with the text view, which will be reported to an IME with android.view.inputmethod.EditorInfo.actionLabel and android.view.inputmethod.EditorInfo.actionId when it has focus.

See also:
getImeActionLabel()
getImeActionId()
android.view.inputmethod.EditorInfo
Attr:
ref android.R.styleable#TextView_imeActionLabel
Attr:
ref android.R.styleable#TextView_imeActionId
    public void setImeActionLabel(CharSequence labelint actionId) {
        if ( == null) {
             = new InputContentType();
        }
        . = label;
        . = actionId;
    }

    
    public CharSequence getImeActionLabel() {
        return  != null
                ? . : null;
    }

    
    public int getImeActionId() {
        return  != null
                ? . : 0;
    }

    
Set a special listener to be called when an action is performed on the text view. This will be called when the enter key is pressed, or when an action supplied to the IME is selected by the user. Setting this means that the normal hard key event will not insert a newline into the text view, even if it is multi-line; holding down the ALT modifier will, however, allow the user to insert a newline character.
        if ( == null) {
             = new InputContentType();
        }
    }
    
    
Called when an attached input method calls InputConnection.performEditorAction() for this text view. The default implementation will call your action listener supplied to setOnEditorActionListener(android.widget.TextView.OnEditorActionListener), or perform a standard operation for EditorInfo.IME_ACTION_NEXT or EditorInfo.IME_ACTION_DONE.

For backwards compatibility, if no IME options have been set and the text view would not normally advance focus on enter, then the NEXT and DONE actions received here will be turned into an enter key down/up pair to go through the normal key handling.

Parameters:
actionCode The code of the action being performed.
See also:
setOnEditorActionListener(android.widget.TextView.OnEditorActionListener)
    public void onEditorAction(int actionCode) {
        final InputContentType ict = ;
        if (ict != null) {
            if (ict.onEditorActionListener != null) {
                if (ict.onEditorActionListener.onEditorAction(this,
                        actionCodenull)) {
                    return;
                }
            }
            
            // This is the handling for some default action.
            // Note that for backwards compatibility we don't do this
            // default handling if explicit ime options have not been given,
            // instead turning this into the normal enter key codes that an
            // app may be expecting.
            if (actionCode == .) {
                View v = focusSearch();
                if (v != null) {
                    if (!v.requestFocus()) {
                        throw new IllegalStateException("focus search returned a view " +
                                "that wasn't able to take focus!");
                    }
                }
                return;
                
            } else if (actionCode == .) {
                InputMethodManager imm = InputMethodManager.peekInstance();
                if (imm != null) {
                    imm.hideSoftInputFromWindow(getWindowToken(), 0);
                }
                return;
            }
        }
        
        Handler h = getHandler();
        if (h != null) {
            long eventTime = SystemClock.uptimeMillis();
                    new KeyEvent(eventTimeeventTime,
                    .., 0, 0, 0, 0,
                    . | .
                    | .)));
                    new KeyEvent(SystemClock.uptimeMillis(), eventTime,
                    .., 0, 0, 0, 0,
                    . | .
                    | .)));
        }
    }
    
    
Set the private content type of the text, which is the EditorInfo.privateImeOptions field that will be filled in when creating an input connection.

See also:
getPrivateImeOptions()
android.view.inputmethod.EditorInfo.privateImeOptions
Attr:
ref android.R.styleable#TextView_privateImeOptions
    public void setPrivateImeOptions(String type) {
        if ( == null = new InputContentType();
        . = type;
    }

    
    public String getPrivateImeOptions() {
        return  != null
                ? . : null;
    }

    
Set the extra input data of the text, which is the TextBoxAttribute.extras Bundle that will be filled in when creating an input connection. The given integer is the resource ID of an XML resource holding an <input-extras> XML tree.

See also:
getInputExtras(boolean)
android.view.inputmethod.EditorInfo.extras
Attr:
ref android.R.styleable#TextView_editorExtras
    public void setInputExtras(int xmlResId)
            throws XmlPullParserExceptionIOException {
        XmlResourceParser parser = getResources().getXml(xmlResId);
        if ( == null = new InputContentType();
        . = new Bundle();
    }

    
Retrieve the input extras currently associated with the text view, which can be viewed as well as modified.

Parameters:
create If true, the extras will be created if they don't already exist. Otherwise, null will be returned if none have been created.
See also:
setInputExtras(int)
android.view.inputmethod.EditorInfo.extras
Attr:
ref android.R.styleable#TextView_editorExtras
    public Bundle getInputExtras(boolean create) {
        if ( == null) {
            if (!createreturn null;
             = new InputContentType();
        }
        if (. == null) {
            if (!createreturn null;
            . = new Bundle();
        }
        return .;
    }

    
Returns the error message that was set to be displayed with setError(java.lang.CharSequence), or null if no error was set or if it the error was cleared by the widget after user input.
    public CharSequence getError() {
        return ;
    }

    
Sets the right-hand compound drawable of the TextView to the "error" icon and sets an error message that will be displayed in a popup when the TextView has focus. The icon and error message will be reset to null when any key events cause changes to the TextView's text. If the error is null, the error message and icon will be cleared.
    public void setError(CharSequence error) {
        if (error == null) {
            setError(nullnull);
        } else {
            Drawable dr = getContext().getResources().
                getDrawable(.....
                            );
            dr.setBounds(0, 0, dr.getIntrinsicWidth(), dr.getIntrinsicHeight());
            setError(errordr);
        }
    }

    
Sets the right-hand compound drawable of the TextView to the specified icon and sets an error message that will be displayed in a popup when the TextView has focus. The icon and error message will be reset to null when any key events cause changes to the TextView's text. The drawable must already have had android.graphics.drawable.Drawable.setBounds(android.graphics.Rect) set on it. If the error is null, the error message will be cleared (and you should provide a null icon as well).
    public void setError(CharSequence errorDrawable icon) {
        error = TextUtils.stringOrSpannedString(error);
         = error;
         = true;
        final Drawables dr = ;
        if (dr != null) {
            setCompoundDrawables(dr.mDrawableLeftdr.mDrawableTop,
                                 icondr.mDrawableBottom);
        } else {
            setCompoundDrawables(nullnulliconnull);
        }
        if (error == null) {
            if ( != null) {
                if (.isShowing()) {
                    .dismiss();
                }
                 = null;
            }
        } else {
            if (isFocused()) {
                showError();
            }
        }
    }
    private void showError() {
        if (getWindowToken() == null) {
             = true;
            return;
        }
        if ( == null) {
            LayoutInflater inflater = LayoutInflater.from(getContext());
            final TextView err = (TextViewinflater.inflate(.....,
                    null);
            final float scale = getResources().getDisplayMetrics().;
             = new ErrorPopup(err, (int) (200 * scale + 0.5f),
                    (int) (50 * scale + 0.5f));
            .setFocusable(false);
            // The user is entering text, so the input method is needed.  We
            // don't want the popup to be displayed on top of it.
        }
        TextView tv = (TextView.getContentView();
        chooseSize(tv);
        tv.setText();
        .showAsDropDown(thisgetErrorX(), getErrorY());
    }
    private static class ErrorPopup extends PopupWindow {
        private boolean mAbove = false;
        private final TextView mView;
        ErrorPopup(TextView vint widthint height) {
            super(vwidthheight);
             = v;
        }
        void fixDirection(boolean above) {
             = above;
            if (above) {
            } else {
            }
        }
        @Override
        public void update(int xint yint wint hboolean force) {
            super.update(xywhforce);
            boolean above = isAboveAnchor();
            if (above != ) {
                fixDirection(above);
            }
        }
    }

    
Returns the Y offset to make the pointy top of the error point at the middle of the error icon.
    private int getErrorX() {
        /*
         * The "25" is the distance between the point and the right edge
         * of the background
         */
        final float scale = getResources().getDisplayMetrics().;
        final Drawables dr = ;
        return getWidth() - .getWidth()
                - getPaddingRight()
                - (dr != null ? dr.mDrawableSizeRight : 0) / 2 + (int) (25 * scale + 0.5f);
    }

    
Returns the Y offset to make the pointy top of the error point at the bottom of the error icon.
    private int getErrorY() {
        /*
         * Compound, not extended, because the icon is not clipped
         * if the text height is smaller.
         */
        int vspace =  -  -
                     getCompoundPaddingBottom() - getCompoundPaddingTop();
        final Drawables dr = ;
        int icontop = getCompoundPaddingTop()
                + (vspace - (dr != null ? dr.mDrawableHeightRight : 0)) / 2;
        /*
         * The "2" is the distance between the point and the top edge
         * of the background.
         */
        return icontop + (dr != null ? dr.mDrawableHeightRight : 0)
                - getHeight() - 2;
    }
    private void hideError() {
        if ( != null) {
            if (.isShowing()) {
                .dismiss();
            }
        }
         = false;
    }
    private void chooseSize(PopupWindow popCharSequence textTextView tv) {
        int wid = tv.getPaddingLeft() + tv.getPaddingRight();
        int ht = tv.getPaddingTop() + tv.getPaddingBottom();
        /*
         * Figure out how big the text would be if we laid it out to the
         * full width of this view minus the border.
         */
        int cap = getWidth() - wid;
        if (cap < 0) {
            cap = 200; // We must not be measured yet -- setFrame() will fix it.
        }
        Layout l = new StaticLayout(texttv.getPaint(), cap,
                                    .., 1, 0, true);
        float max = 0;
        for (int i = 0; i < l.getLineCount(); i++) {
            max = Math.max(maxl.getLineWidth(i));
        }
        /*
         * Now set the popup size to be big enough for the text plus the border.
         */
        pop.setWidth(wid + (int) Math.ceil(max));
        pop.setHeight(ht + l.getHeight());
    }
    @Override
    protected boolean setFrame(int lint tint rint b) {
        boolean result = super.setFrame(ltrb);
        if ( != null) {
            TextView tv = (TextView.getContentView();
            chooseSize(tv);
            .update(thisgetErrorX(), getErrorY(),
                          .getWidth(), .getHeight());
        }
        restartMarqueeIfNeeded();
        return result;
    }
    private void restartMarqueeIfNeeded() {
        if ( &&  == ..) {
             = false;
            startMarquee();
        }
    }

    
Sets the list of input filters that will be used if the buffer is Editable. Has no effect otherwise.

Attr:
ref android.R.styleable#TextView_maxLength
    public void setFilters(InputFilter[] filters) {
        if (filters == null) {
            throw new IllegalArgumentException();
        }
         = filters;
        if ( instanceof Editable) {
            setFilters((Editablefilters);
        }
    }

    
Sets the list of input filters on the specified Editable, and includes mInput in the list if it is an InputFilter.
    private void setFilters(Editable eInputFilter[] filters) {
        if ( instanceof InputFilter) {
            InputFilter[] nf = new InputFilter[filters.length + 1];
            System.arraycopy(filters, 0, nf, 0, filters.length);
            nf[filters.length] = (InputFilter;
            e.setFilters(nf);
        } else {
            e.setFilters(filters);
        }
    }

    
Returns the current list of input filters.
    public InputFilter[] getFilters() {
        return ;
    }
    /////////////////////////////////////////////////////////////////////////
    private int getVerticalOffset(boolean forceNormal) {
        int voffset = 0;
        final int gravity =  & .;
        Layout l = ;
        if (!forceNormal && .length() == 0 &&  != null) {
            l = ;
        }
        if (gravity != .) {
            int boxht;
            if (l == ) {
                boxht = getMeasuredHeight() - getCompoundPaddingTop() -
                        getCompoundPaddingBottom();
            } else {
                boxht = getMeasuredHeight() - getExtendedPaddingTop() -
                        getExtendedPaddingBottom();
            }
            int textht = l.getHeight();
            if (textht < boxht) {
                if (gravity == .)
                    voffset = boxht - textht;
                else // (gravity == Gravity.CENTER_VERTICAL)
                    voffset = (boxht - textht) >> 1;
            }
        }
        return voffset;
    }
    private int getBottomVerticalOffset(boolean forceNormal) {
        int voffset = 0;
        final int gravity =  & .;
        Layout l = ;
        if (!forceNormal && .length() == 0 &&  != null) {
            l = ;
        }
        if (gravity != .) {
            int boxht;
            if (l == ) {
                boxht = getMeasuredHeight() - getCompoundPaddingTop() -
                        getCompoundPaddingBottom();
            } else {
                boxht = getMeasuredHeight() - getExtendedPaddingTop() -
                        getExtendedPaddingBottom();
            }
            int textht = l.getHeight();
            if (textht < boxht) {
                if (gravity == .)
                    voffset = boxht - textht;
                else // (gravity == Gravity.CENTER_VERTICAL)
                    voffset = (boxht - textht) >> 1;
            }
        }
        return voffset;
    }
    private void invalidateCursorPath() {
        if () {
            invalidateCursor();
        } else {
            synchronized () {
                /*
                 * The reason for this concern about the thickness of the
                 * cursor and doing the floor/ceil on the coordinates is that
                 * some EditTexts (notably textfields in the Browser) have
                 * anti-aliased text where not all the characters are
                 * necessarily at integer-multiple locations.  This should
                 * make sure the entire cursor gets invalidated instead of
                 * sometimes missing half a pixel.
                 */
                float thick = FloatMath.ceil(.getStrokeWidth());
                if (thick < 1.0f) {
                    thick = 1.0f;
                }
                thick /= 2;
                .computeBounds(false);
                int left = getCompoundPaddingLeft();
                int top = getExtendedPaddingTop() + getVerticalOffset(true);
                invalidate((int) FloatMath.floor(left + . - thick),
                           (int) FloatMath.floor(top + . - thick),
                           (int) FloatMath.ceil(left + . + thick),
                           (int) FloatMath.ceil(top + . + thick));
            }
        }
    }
    private void invalidateCursor() {
        int where = getSelectionEnd();
        invalidateCursor(wherewherewhere);
    }
    private void invalidateCursor(int aint bint c) {
        if ( == null) {
            invalidate();
        } else {
            if (a >= 0 || b >= 0 || c >= 0) {
                int first = Math.min(Math.min(ab), c);
                int last = Math.max(Math.max(ab), c);
                int line = .getLineForOffset(first);
                int top = .getLineTop(line);
                // This is ridiculous, but the descent from the line above
                // can hang down into the line we really want to redraw,
                // so we have to invalidate part of the line above to make
                // sure everything that needs to be redrawn really is.
                // (But not the whole line above, because that would cause
                // the same problem with the descenders on the line above it!)
                if (line > 0) {
                    top -= .getLineDescent(line - 1);
                }
                int line2;
                if (first == last)
                    line2 = line;
                else
                    line2 = .getLineForOffset(last);
                int bottom = .getLineTop(line2 + 1);
                int voffset = getVerticalOffset(true);
                int left = getCompoundPaddingLeft() + ;
                invalidate(lefttop + voffset + getExtendedPaddingTop(),
                           left + getWidth() - getCompoundPaddingLeft() -
                           getCompoundPaddingRight(),
                           bottom + voffset + getExtendedPaddingTop());
            }
        }
    }
    private void registerForPreDraw() {
        final ViewTreeObserver observer = getViewTreeObserver();
        if (observer == null) {
            return;
        }
        if ( == ) {
            observer.addOnPreDrawListener(this);
             = ;
        } else if ( == ) {
             = ;
        }
        // else state is PREDRAW_PENDING, so keep waiting.
    }

    
    public boolean onPreDraw() {
        if ( != ) {
            return true;
        }
        if ( == null) {
            assumeLayout();
        }
        boolean changed = false;
        SelectionModifierCursorController selectionController = null;
        if ( != null) {
            selectionController = (SelectionModifierCursorController)
                ;
        }
        if ( != null) {
            /* This code also provides auto-scrolling when a cursor is moved using a
             * CursorController (insertion point or selection limits).
             * For selection, ensure start or end is visible depending on controller's state.
             */
            int curs = getSelectionEnd();
            if (selectionController != null && selectionController.isSelectionStartDragged()) {
                curs = getSelectionStart();
            }
            /*
             * TODO: This should really only keep the end in view if
             * it already was before the text changed.  I'm not sure
             * of a good way to tell from here if it was.
             */
            if (curs < 0 &&
                  ( & .) == .) {
                curs = .length();
            }
            if (curs >= 0) {
                changed = bringPointIntoView(curs);
            }
        } else {
            changed = bringTextIntoView();
        }
        // This has to be checked here since:
        // - onFocusChanged cannot start it when focus is given to a view with selected text (after
        //   a screen rotation) since layout is not yet initialized at that point.
        // - ExtractEditText does not call onFocus when it is displayed. Fixing this issue would
        //   allow to test for hasSelection in onFocusChanged, which would trigger a
        //   startTextSelectionMode here. TODO
        if (this instanceof ExtractEditText && selectionController != null && hasSelection()) {
            startTextSelectionMode();
        }
         = ;
        return !changed;
    }
    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
         = false;
        
        if () {
            showError();
             = false;
        }
        final ViewTreeObserver observer = getViewTreeObserver();
        if (observer != null) {
            if ( != null) {
                observer.addOnTouchModeChangeListener();
            }
            if ( != null) {
                observer.addOnTouchModeChangeListener();
            }
        }
    }
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        final ViewTreeObserver observer = getViewTreeObserver();
        if (observer != null) {
            if ( != ) {
                observer.removeOnPreDrawListener(this);
                 = ;
            }
            if ( != null) {
                observer.removeOnTouchModeChangeListener();
            }
            if ( != null) {
            }
        }
        if ( != null) {
            hideError();
        }
        hideControllers();
    }
    @Override
    protected boolean isPaddingOffsetRequired() {
        return  != 0 ||  != null;
    }
    @Override
    protected int getLeftPaddingOffset() {
        return getCompoundPaddingLeft() -  +
                (int) Math.min(0,  - );
    }
    @Override
    protected int getTopPaddingOffset() {
        return (int) Math.min(0,  - );
    }
    @Override
    protected int getBottomPaddingOffset() {
        return (int) Math.max(0,  + );
    }
    @Override
    protected int getRightPaddingOffset() {
        return -(getCompoundPaddingRight() - ) +
                (int) Math.max(0,  + );
    }
    @Override
    protected boolean verifyDrawable(Drawable who) {
        final boolean verified = super.verifyDrawable(who);
        if (!verified &&  != null) {
            return who == . || who == . ||
                    who == . || who == .;
        }
        return verified;
    }
    @Override
    public void invalidateDrawable(Drawable drawable) {
        if (verifyDrawable(drawable)) {
            final Rect dirty = drawable.getBounds();
            int scrollX = ;
            int scrollY = ;
            // IMPORTANT: The coordinates below are based on the coordinates computed
            // for each compound drawable in onDraw(). Make sure to update each section
            // accordingly.
            final TextView.Drawables drawables = ;
            if (drawables != null) {
                if (drawable == drawables.mDrawableLeft) {
                    final int compoundPaddingTop = getCompoundPaddingTop();
                    final int compoundPaddingBottom = getCompoundPaddingBottom();
                    final int vspace =  -  - compoundPaddingBottom - compoundPaddingTop;
                    scrollX += ;
                    scrollY += compoundPaddingTop + (vspace - drawables.mDrawableHeightLeft) / 2;
                } else if (drawable == drawables.mDrawableRight) {
                    final int compoundPaddingTop = getCompoundPaddingTop();
                    final int compoundPaddingBottom = getCompoundPaddingBottom();
                    final int vspace =  -  - compoundPaddingBottom - compoundPaddingTop;
                    scrollX += ( -  -  - drawables.mDrawableSizeRight);
                    scrollY += compoundPaddingTop + (vspace - drawables.mDrawableHeightRight) / 2;
                } else if (drawable == drawables.mDrawableTop) {
                    final int compoundPaddingLeft = getCompoundPaddingLeft();
                    final int compoundPaddingRight = getCompoundPaddingRight();
                    final int hspace =  -  - compoundPaddingRight - compoundPaddingLeft;
                    scrollX += compoundPaddingLeft + (hspace - drawables.mDrawableWidthTop) / 2;
                    scrollY += ;
                } else if (drawable == drawables.mDrawableBottom) {
                    final int compoundPaddingLeft = getCompoundPaddingLeft();
                    final int compoundPaddingRight = getCompoundPaddingRight();
                    final int hspace =  -  - compoundPaddingRight - compoundPaddingLeft;
                    scrollX += compoundPaddingLeft + (hspace - drawables.mDrawableWidthBottom) / 2;
                    scrollY += ( -  -  - drawables.mDrawableSizeBottom);
                }
            }
            invalidate(dirty.left + scrollXdirty.top + scrollY,
                    dirty.right + scrollXdirty.bottom + scrollY);
        }
    }
    @Override
    protected void onDraw(Canvas canvas) {
        restartMarqueeIfNeeded();
        // Draw the background for this view
        super.onDraw(canvas);
        final int compoundPaddingLeft = getCompoundPaddingLeft();
        final int compoundPaddingTop = getCompoundPaddingTop();
        final int compoundPaddingRight = getCompoundPaddingRight();
        final int compoundPaddingBottom = getCompoundPaddingBottom();
        final int scrollX = ;
        final int scrollY = ;
        final int right = ;
        final int left = ;
        final int bottom = ;
        final int top = ;
        final Drawables dr = ;
        if (dr != null) {
            /*
             * Compound, not extended, because the icon is not clipped
             * if the text height is smaller.
             */
            int vspace = bottom - top - compoundPaddingBottom - compoundPaddingTop;
            int hspace = right - left - compoundPaddingRight - compoundPaddingLeft;
            // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
            // Make sure to update invalidateDrawable() when changing this code.
            if (dr.mDrawableLeft != null) {
                canvas.save();
                canvas.translate(scrollX + ,
                                 scrollY + compoundPaddingTop +
                                 (vspace - dr.mDrawableHeightLeft) / 2);
                dr.mDrawableLeft.draw(canvas);
                canvas.restore();
            }
            // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
            // Make sure to update invalidateDrawable() when changing this code.
            if (dr.mDrawableRight != null) {
                canvas.save();
                canvas.translate(scrollX + right - left -  - dr.mDrawableSizeRight,
                         scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightRight) / 2);
                dr.mDrawableRight.draw(canvas);
                canvas.restore();
            }
            // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
            // Make sure to update invalidateDrawable() when changing this code.
            if (dr.mDrawableTop != null) {
                canvas.save();
                canvas.translate(scrollX + compoundPaddingLeft + (hspace - dr.mDrawableWidthTop) / 2,
                        scrollY + );
                dr.mDrawableTop.draw(canvas);
                canvas.restore();
            }
            // IMPORTANT: The coordinates computed are also used in invalidateDrawable()
            // Make sure to update invalidateDrawable() when changing this code.
            if (dr.mDrawableBottom != null) {
                canvas.save();
                canvas.translate(scrollX + compoundPaddingLeft +
                        (hspace - dr.mDrawableWidthBottom) / 2,
                         scrollY + bottom - top -  - dr.mDrawableSizeBottom);
                dr.mDrawableBottom.draw(canvas);
                canvas.restore();
            }
        }
        if ( == ) {
            final ViewTreeObserver observer = getViewTreeObserver();
            if (observer != null) {
                observer.removeOnPreDrawListener(this);
                 = ;
            }
        }
        int color = ;
        if ( == null) {
            assumeLayout();
        }
        Layout layout = ;
        int cursorcolor = color;
        if ( != null && .length() == 0) {
            if ( != null) {
                color = ;
            }
            layout = ;
        }
        .setColor(color);
        canvas.save();
        /*  Would be faster if we didn't have to do this. Can we chop the
            (displayable) text so that we don't need to do this ever?
        */
        int extendedPaddingTop = getExtendedPaddingTop();
        int extendedPaddingBottom = getExtendedPaddingBottom();
        float clipLeft = compoundPaddingLeft + scrollX;
        float clipTop = extendedPaddingTop + scrollY;
        float clipRight = right - left - compoundPaddingRight + scrollX;
        float clipBottom = bottom - top - extendedPaddingBottom + scrollY;
        if ( != 0) {
            clipLeft += Math.min(0,  - );
            clipRight += Math.max(0,  + );
            clipTop += Math.min(0,  - );
            clipBottom += Math.max(0,  + );
        }
        canvas.clipRect(clipLeftclipTopclipRightclipBottom);
        int voffsetText = 0;
        int voffsetCursor = 0;
        // translate in by our padding
        {
            /* shortcircuit calling getVerticaOffset() */
            if (( & .) != .) {
                voffsetText = getVerticalOffset(false);
                voffsetCursor = getVerticalOffset(true);
            }
            canvas.translate(compoundPaddingLeftextendedPaddingTop + voffsetText);
        }
        if ( == ..) {
            if (! && getLineCount() == 1 && canMarquee() &&
                    ( & .) != .) {
                canvas.translate(.getLineRight(0) - ( -  -
                        getCompoundPaddingLeft() - getCompoundPaddingRight()), 0.0f);
            }
            if ( != null && .isRunning()) {
                canvas.translate(-., 0.0f);
            }
        }
        Path highlight = null;
        int selStart = -1, selEnd = -1;
        //  If there is no movement method, then there can be no selection.
        //  Check that first and attempt to skip everything having to do with
        //  the cursor.
        //  XXX This is not strictly true -- a program could set the
        //  selection manually if it really wanted to.
        if ( != null && (isFocused() || isPressed())) {
            selStart = getSelectionStart();
            selEnd = getSelectionEnd();
            if ( && selStart >= 0 && isEnabled()) {
                if ( == null)
                     = new Path();
                if (selStart == selEnd) {
                    if ((SystemClock.uptimeMillis() - ) % (2 * ) < ) {
                        if () {
                            .reset();
                            .getCursorPath(selStart);
                             = false;
                        }
                        // XXX should pass to skin instead of drawing directly
                        .setColor(cursorcolor);
                        .setStyle(..);
                        highlight = ;
                    }
                } else {
                    if () {
                        .reset();
                        .getSelectionPath(selStartselEnd);
                         = false;
                    }
                    // XXX should pass to skin instead of drawing directly
                    .setColor();
                    .setStyle(..);
                    highlight = ;
                }
            }
        }
        /*  Comment out until we decide what to do about animations
        boolean isLinearTextOn = false;
        if (currentTransformation != null) {
            isLinearTextOn = mTextPaint.isLinearTextOn();
            Matrix m = currentTransformation.getMatrix();
            if (!m.isIdentity()) {
                // mTextPaint.setLinearTextOn(true);
            }
        }
        */
        final InputMethodState ims = ;
        if (ims != null && ims.mBatchEditNesting == 0) {
            InputMethodManager imm = InputMethodManager.peekInstance();
            if (imm != null) {
                if (imm.isActive(this)) {
                    boolean reported = false;
                    if (ims.mContentChanged || ims.mSelectionModeChanged) {
                        // We are in extract mode and the content has changed
                        // in some way... just report complete new text to the
                        // input method.
                        reported = reportExtractedText();
                    }
                    if (!reported && highlight != null) {
                        int candStart = -1;
                        int candEnd = -1;
                        if ( instanceof Spannable) {
                            Spannable sp = (Spannable);
                            candStart = EditableInputConnection.getComposingSpanStart(sp);
                            candEnd = EditableInputConnection.getComposingSpanEnd(sp);
                        }
                        imm.updateSelection(thisselStartselEndcandStartcandEnd);
                    }
                }
                
                if (imm.isWatchingCursor(this) && highlight != null) {
                    highlight.computeBounds(ims.mTmpRectFtrue);
                    ims.mTmpOffset[0] = ims.mTmpOffset[1] = 0;
    
                    canvas.getMatrix().mapPoints(ims.mTmpOffset);
                    ims.mTmpRectF.offset(ims.mTmpOffset[0], ims.mTmpOffset[1]);
    
                    ims.mTmpRectF.offset(0, voffsetCursor - voffsetText);
    
                    ims.mCursorRectInWindow.set((int)(ims.mTmpRectF.left + 0.5),
                            (int)(ims.mTmpRectF.top + 0.5),
                            (int)(ims.mTmpRectF.right + 0.5),
                            (int)(ims.mTmpRectF.bottom + 0.5));
    
                    imm.updateCursor(this,
                            ims.mCursorRectInWindow.leftims.mCursorRectInWindow.top,
                            ims.mCursorRectInWindow.rightims.mCursorRectInWindow.bottom);
                }
            }
        }
        layout.draw(canvashighlightvoffsetCursor - voffsetText);
        if ( != null && .shouldDrawGhost()) {
            canvas.translate((int.getGhostOffset(), 0.0f);
            layout.draw(canvashighlightvoffsetCursor - voffsetText);
        }
        /*  Comment out until we decide what to do about animations
        if (currentTransformation != null) {
            mTextPaint.setLinearTextOn(isLinearTextOn);
        }
        */
        canvas.restore();
    }

    
Update the positions of the CursorControllers. Needed by WebTextView, which does not draw.

Hide:
    protected void updateCursorControllerPositions() {
        if ( != null &&
                .isShowing()) {
        }
        if ( != null &&
                .isShowing()) {
        }
    }
    @Override
    public void getFocusedRect(Rect r) {
        if ( == null) {
            super.getFocusedRect(r);
            return;
        }
        int sel = getSelectionEnd();
        if (sel < 0) {
            super.getFocusedRect(r);
            return;
        }
        int line = .getLineForOffset(sel);
        r.top = .getLineTop(line);
        r.bottom = .getLineBottom(line);
        r.left = (int.getPrimaryHorizontal(sel);
        r.right = r.left + 1;
        // Adjust for padding and gravity.
        int paddingLeft = getCompoundPaddingLeft();
        int paddingTop = getExtendedPaddingTop();
        if (( & .) != .) {
            paddingTop += getVerticalOffset(false);
        }
        r.offset(paddingLeftpaddingTop);
    }

    
Return the number of lines of text, or 0 if the internal Layout has not been built.
    public int getLineCount() {
        return  != null ? .getLineCount() : 0;
    }

    
Return the baseline for the specified line (0...getLineCount() - 1) If bounds is not null, return the top, left, right, bottom extents of the specified line in it. If the internal Layout has not been built, return 0 and set bounds to (0, 0, 0, 0)

Parameters:
line which line to examine (0..getLineCount() - 1)
bounds Optional. If not null, it returns the extent of the line
Returns:
the Y-coordinate of the baseline
    public int getLineBounds(int lineRect bounds) {
        if ( == null) {
            if (bounds != null) {
                bounds.set(0, 0, 0, 0);
            }
            return 0;
        }
        else {
            int baseline = .getLineBounds(linebounds);
            int voffset = getExtendedPaddingTop();
            if (( & .) != .) {
                voffset += getVerticalOffset(true);
            }
            if (bounds != null) {
                bounds.offset(getCompoundPaddingLeft(), voffset);
            }
            return baseline + voffset;
        }
    }
    @Override
    public int getBaseline() {
        if ( == null) {
            return super.getBaseline();
        }
        int voffset = 0;
        if (( & .) != .) {
            voffset = getVerticalOffset(true);
        }
        return getExtendedPaddingTop() + voffset + .getLineBaseline(0);
    }
    @Override
    public boolean onKeyDown(int keyCodeKeyEvent event) {
        int which = doKeyDown(keyCodeeventnull);
        if (which == 0) {
            // Go through default dispatching.
            return super.onKeyDown(keyCodeevent);
        }
        return true;
    }
    @Override
    public boolean onKeyMultiple(int keyCodeint repeatCountKeyEvent event) {
        KeyEvent down = KeyEvent.changeAction(event.);
        int which = doKeyDown(keyCodedownevent);
        if (which == 0) {
            // Go through default dispatching.
            return super.onKeyMultiple(keyCoderepeatCountevent);
        }
        if (which == -1) {
            // Consumed the whole thing.
            return true;
        }
        repeatCount--;
        
        // We are going to dispatch the remaining events to either the input
        // or movement method.  To do this, we will just send a repeated stream
        // of down and up events until we have done the complete repeatCount.
        // It would be nice if those interfaces had an onKeyMultiple() method,
        // but adding that is a more complicated change.
        KeyEvent up = KeyEvent.changeAction(event.);
        if (which == 1) {
            .onKeyUp(this, (Editable)keyCodeup);
            while (--repeatCount > 0) {
                .onKeyDown(this, (Editable)keyCodedown);
                .onKeyUp(this, (Editable)keyCodeup);
            }
            if ( != null && !) {
                setError(nullnull);
            }
        } else if (which == 2) {
            .onKeyUp(this, (Spannable)keyCodeup);
            while (--repeatCount > 0) {
                .onKeyDown(this, (Spannable)keyCodedown);
                .onKeyUp(this, (Spannable)keyCodeup);
            }
        }
        return true;
    }

    
Returns true if pressing ENTER in this field advances focus instead of inserting the character. This is true mostly in single-line fields, but also in mail addresses and subjects which will display on multiple lines but where it doesn't make sense to insert newlines.
    private boolean shouldAdvanceFocusOnEnter() {
        if ( == null) {
            return false;
        }
        if () {
            return true;
        }
            int variation =  & .;
            if (variation == . ||
                variation == .) {
                return true;
            }
        }
        return false;
    }
    private int doKeyDown(int keyCodeKeyEvent eventKeyEvent otherEvent) {
        if (!isEnabled()) {
            return 0;
        }
        switch (keyCode) {
            case .:
                 = true;
                // If ALT modifier is held, then we always insert a
                // newline character.
                if ((event.getMetaState()&.) == 0) {
                    
                    // When mInputContentType is set, we know that we are
                    // running in a "modern" cupcake environment, so don't need
                    // to worry about the application trying to capture
                    // enter key events.
                    if ( != null) {
                        
                        // If there is an action listener, given them a
                        // chance to consume the event.
                        if (. != null &&
                                ..onEditorAction(
                                this.event)) {
                            . = true;
                            // We are consuming the enter key for them.
                            return -1;
                        }
                    }
                    
                    // If our editor should move focus when enter is pressed, or
                    // this is a generated event from an IME action button, then
                    // don't let it be inserted into the text.
                    if ((event.getFlags()&.) != 0
                            || shouldAdvanceFocusOnEnter()) {
                        return -1;
                    }
                }
                break;
                
            case .:
                 = true;
                if (shouldAdvanceFocusOnEnter()) {
                    return 0;
                }
                break;
                // Has to be done on key down (and not on key up) to correctly be intercepted.
            case .:
                if () {
                    stopTextSelectionMode();
                    return -1;
                }
                break;
        }
        if ( != null) {
            /*
             * Keep track of what the error was before doing the input
             * so that if an input filter changed the error, we leave
             * that error showing.  Otherwise, we take down whatever
             * error was showing when the user types something.
             */
             = false;
            boolean doDown = true;
            if (otherEvent != null) {
                try {
                    beginBatchEdit();
                    boolean handled = .onKeyOther(this, (Editable,
                            otherEvent);
                    if ( != null && !) {
                        setError(nullnull);
                    }
                    doDown = false;
                    if (handled) {
                        return -1;
                    }
                } catch (AbstractMethodError e) {
                    // onKeyOther was added after 1.0, so if it isn't
                    // implemented we need to try to dispatch as a regular down.
                } finally {
                    endBatchEdit();
                }
            }
            
            if (doDown) {
                beginBatchEdit();
                if (.onKeyDown(this, (EditablekeyCodeevent)) {
                    endBatchEdit();
                    if ( != null && !) {
                        setError(nullnull);
                    }
                    return 1;
                }
                endBatchEdit();
            }
        }
        // bug 650865: sometimes we get a key event before a layout.
        // don't try to move around if we don't know the layout.
        if ( != null &&  != null) {
            boolean doDown = true;
            if (otherEvent != null) {
                try {
                    boolean handled = .onKeyOther(this, (Spannable,
                            otherEvent);
                    doDown = false;
                    if (handled) {
                        return -1;
                    }
                } catch (AbstractMethodError e) {
                    // onKeyOther was added after 1.0, so if it isn't
                    // implemented we need to try to dispatch as a regular down.
                }
            }
            if (doDown) {
                if (.onKeyDown(this, (Spannable)keyCodeevent))
                    return 2;
            }
        }
        return 0;
    }
    @Override
    public boolean onKeyUp(int keyCodeKeyEvent event) {
        if (!isEnabled()) {
            return super.onKeyUp(keyCodeevent);
        }
        hideControllers();
        stopTextSelectionMode();
        switch (keyCode) {
            case .:
                 = false;
                /*
                 * If there is a click listener, just call through to
                 * super, which will invoke it.
                 *
                 * If there isn't a click listener, try to show the soft
                 * input method.  (It will also
                 * call performClick(), but that won't do anything in
                 * this case.)
                 */
                if ( == null) {
                    if ( != null &&  instanceof Editable
                            &&  != null && onCheckIsTextEditor()) {
                        InputMethodManager imm = (InputMethodManager)
                                getContext().getSystemService(.);
                        imm.showSoftInput(this, 0);
                    }
                }
                return super.onKeyUp(keyCodeevent);
                
            case .:
                 = false;
                if ( != null
                        && . != null
                        && .) {
                    . = false;
                    if (..onEditorAction(
                            this.event)) {
                        return true;
                    }
                }
                
                if ((event.getFlags()&.) != 0
                        || shouldAdvanceFocusOnEnter()) {
                    /*
                     * If there is a click listener, just call through to
                     * super, which will invoke it.
                     *
                     * If there isn't a click listener, try to advance focus,
                     * but still call through to super, which will reset the
                     * pressed state and longpress state.  (It will also
                     * call performClick(), but that won't do anything in
                     * this case.)
                     */
                    if ( == null) {
                        View v = focusSearch();
                        if (v != null) {
                            if (!v.requestFocus()) {
                                throw new IllegalStateException("focus search returned a view " +
                                        "that wasn't able to take focus!");
                            }
                            /*
                             * Return true because we handled the key; super
                             * will return false because there was no click
                             * listener.
                             */
                            super.onKeyUp(keyCodeevent);
                            return true;
                        } else if ((event.getFlags()
                                & .) != 0) {
                            // No target for next focus, but make sure the IME
                            // if this came from it.
                            InputMethodManager imm = InputMethodManager.peekInstance();
                            if (imm != null) {
                                imm.hideSoftInputFromWindow(getWindowToken(), 0);
                            }
                        }
                    }
                    return super.onKeyUp(keyCodeevent);
                }
                break;
        }
        if ( != null)
            if (.onKeyUp(this, (EditablekeyCodeevent))
                return true;
        if ( != null &&  != null)
            if (.onKeyUp(this, (SpannablekeyCodeevent))
                return true;
        return super.onKeyUp(keyCodeevent);
    }
    @Override public boolean onCheckIsTextEditor() {
        return  != .;
    }
    
        if (onCheckIsTextEditor()) {
            if ( == null) {
                 = new InputMethodState();
            }
            outAttrs.inputType = ;
            if ( != null) {
                outAttrs.imeOptions = .;
                outAttrs.privateImeOptions = .;
                outAttrs.actionLabel = .;
                outAttrs.actionId = .;
                outAttrs.extras = .;
            } else {
                outAttrs.imeOptions = .;
            }
            if ((outAttrs.imeOptions&.)
                    == .) {
                if (focusSearch() != null) {
                    // An action has not been set, but the enter key will move to
                    // the next focus, so set the action to that.
                    outAttrs.imeOptions |= .;
                } else {
                    // An action has not been set, and there is no focus to move
                    // to, so let's just supply a "done" action.
                    outAttrs.imeOptions |= .;
                }
                if (!shouldAdvanceFocusOnEnter()) {
                    outAttrs.imeOptions |= .;
                }
            }
            if ((outAttrs.inputType & (.
                    | .))
                    == (.
                            | .)) {
                // Multi-line text editors should always show an enter key.
                outAttrs.imeOptions |= .;
            }
            outAttrs.hintText = ;
            if ( instanceof Editable) {
                InputConnection ic = new EditableInputConnection(this);
                outAttrs.initialSelStart = getSelectionStart();
                outAttrs.initialSelEnd = getSelectionEnd();
                outAttrs.initialCapsMode = ic.getCursorCapsMode();
                return ic;
            }
        }
        return null;
    }

    
If this TextView contains editable content, extract a portion of it based on the information in request in to outText.

Returns:
Returns true if the text was successfully extracted, else false.
    public boolean extractText(ExtractedTextRequest request,
            ExtractedText outText) {
        return extractTextInternal(request,
                outText);
    }
    
    static final int EXTRACT_NOTHING = -2;
    static final int EXTRACT_UNKNOWN = -1;
    
            int partialStartOffsetint partialEndOffsetint delta,
            ExtractedText outText) {
        final CharSequence content = ;
        if (content != null) {
            if (partialStartOffset != ) {
                final int N = content.length();
                if (partialStartOffset < 0) {
                    outText.partialStartOffset = outText.partialEndOffset = -1;
                    partialStartOffset = 0;
                    partialEndOffset = N;
                } else {
                    // Adjust offsets to ensure we contain full spans.
                    if (content instanceof Spanned) {
                        Spanned spanned = (Spanned)content;
                        Object[] spans = spanned.getSpans(partialStartOffset,
                                partialEndOffsetParcelableSpan.class);
                        int i = spans.length;
                        while (i > 0) {
                            i--;
                            int j = spanned.getSpanStart(spans[i]);
                            if (j < partialStartOffsetpartialStartOffset = j;
                            j = spanned.getSpanEnd(spans[i]);
                            if (j > partialEndOffsetpartialEndOffset = j;
                        }
                    }
                    outText.partialStartOffset = partialStartOffset;
                    outText.partialEndOffset = partialEndOffset;
                    // Now use the delta to determine the actual amount of text
                    // we need.
                    partialEndOffset += delta;
                    if (partialStartOffset > N) {
                        partialStartOffset = N;
                    } else if (partialStartOffset < 0) {
                        partialStartOffset = 0;
                    }
                    if (partialEndOffset > N) {
                        partialEndOffset = N;
                    } else if (partialEndOffset < 0) {
                        partialEndOffset = 0;
                    }
                }
                if ((request.flags&.) != 0) {
                    outText.text = content.subSequence(partialStartOffset,
                            partialEndOffset);
                } else {
                    outText.text = TextUtils.substring(contentpartialStartOffset,
                            partialEndOffset);
                }
            } else {
                outText.partialStartOffset = 0;
                outText.partialEndOffset = 0;
                outText.text = "";
            }
            outText.flags = 0;
            if (MetaKeyKeyListener.getMetaState(.) != 0) {
                outText.flags |= .;
            }
            if () {
                outText.flags |= .;
            }
            outText.startOffset = 0;
            outText.selectionStart = getSelectionStart();
            outText.selectionEnd = getSelectionEnd();
            return true;
        }
        return false;
    }
    
    boolean reportExtractedText() {
        final InputMethodState ims = ;
        if (ims != null) {
            final boolean contentChanged = ims.mContentChanged;
            if (contentChanged || ims.mSelectionModeChanged) {
                ims.mContentChanged = false;
                ims.mSelectionModeChanged = false;
                final ExtractedTextRequest req = .;
                if (req != null) {
                    InputMethodManager imm = InputMethodManager.peekInstance();
                    if (imm != null) {
                        if () Log.v("Retrieving extracted start="
                                + ims.mChangedStart + " end=" + ims.mChangedEnd
                                + " delta=" + ims.mChangedDelta);
                        if (ims.mChangedStart < 0 && !contentChanged) {
                            ims.mChangedStart = ;
                        }
                        if (extractTextInternal(reqims.mChangedStartims.mChangedEnd,
                                ims.mChangedDeltaims.mTmpExtracted)) {
                            if () Log.v("Reporting extracted start="
                                    + ims.mTmpExtracted.partialStartOffset
                                    + " end=" + ims.mTmpExtracted.partialEndOffset
                                    + ": " + ims.mTmpExtracted.text);
                            imm.updateExtractedText(thisreq.token,
                                    .);
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }
    
    
This is used to remove all style-impacting spans from text before new extracted text is being replaced into it, so that we don't have any lingering spans applied during the replace.
    static void removeParcelableSpans(Spannable spannableint startint end) {
        Object[] spans = spannable.getSpans(startendParcelableSpan.class);
        int i = spans.length;
        while (i > 0) {
            i--;
            spannable.removeSpan(spans[i]);
        }
    }
    
    
Apply to this text view the given extracted text, as previously returned by extractText(android.view.inputmethod.ExtractedTextRequest,android.view.inputmethod.ExtractedText).
    public void setExtractedText(ExtractedText text) {
        Editable content = getEditableText();
        if (text.text != null) {
            if (content == null) {
                setText(text.text..);
            } else if (text.partialStartOffset < 0) {
                removeParcelableSpans(content, 0, content.length());
                content.replace(0, content.length(), text.text);
            } else {
                final int N = content.length();
                int start = text.partialStartOffset;
                if (start > Nstart = N;
                int end = text.partialEndOffset;
                if (end > Nend = N;
                removeParcelableSpans(contentstartend);
                content.replace(startendtext.text);
            }
        }
        
        // Now set the selection position...  make sure it is in range, to
        // avoid crashes.  If this is a partial update, it is possible that
        // the underlying text may have changed, causing us problems here.
        // Also we just don't want to trust clients to do the right thing.
        Spannable sp = (Spannable)getText();
        final int N = sp.length();
        int start = text.selectionStart;
        if (start < 0) start = 0;
        else if (start > Nstart = N;
        int end = text.selectionEnd;
        if (end < 0) end = 0;
        else if (end > Nend = N;
        Selection.setSelection(spstartend);
        
        // Finally, update the selection mode.
        if ((text.flags&.) != 0) {
            MetaKeyKeyListener.startSelecting(thissp);
        } else {
            MetaKeyKeyListener.stopSelecting(thissp);
        }
    }
    
    

Hide:
    public void setExtracting(ExtractedTextRequest req) {
        if ( != null) {
            . = req;
        }
        hideControllers();
    }
    
    
Called by the framework in response to a text completion from the current input method, provided by it calling InputConnection.commitCompletion(). The default implementation does nothing; text views that are supporting auto-completion should override this to do their desired behavior.

Parameters:
text The auto complete text the user has selected.
    public void onCommitCompletion(CompletionInfo text) {
    }
    public void beginBatchEdit() {
         = true;
        final InputMethodState ims = ;
        if (ims != null) {
            int nesting = ++ims.mBatchEditNesting;
            if (nesting == 1) {
                ims.mCursorChanged = false;
                ims.mChangedDelta = 0;
                if (ims.mContentChanged) {
                    // We already have a pending change from somewhere else,
                    // so turn this into a full update.
                    ims.mChangedStart = 0;
                    ims.mChangedEnd = .length();
                } else {
                    ims.mChangedStart = ;
                    ims.mChangedEnd = ;
                    ims.mContentChanged = false;
                }
                onBeginBatchEdit();
            }
        }
    }
    
    public void endBatchEdit() {
         = false;
        final InputMethodState ims = ;
        if (ims != null) {
            int nesting = --ims.mBatchEditNesting;
            if (nesting == 0) {
                finishBatchEdit(ims);
            }
        }
    }
    
    void ensureEndedBatchEdit() {
        final InputMethodState ims = ;
        if (ims != null && ims.mBatchEditNesting != 0) {
            ims.mBatchEditNesting = 0;
            finishBatchEdit(ims);
        }
    }
    
    void finishBatchEdit(final InputMethodState ims) {
        onEndBatchEdit();
        
        if (ims.mContentChanged || ims.mSelectionModeChanged) {
            updateAfterEdit();
            reportExtractedText();
        } else if (ims.mCursorChanged) {
            // Cheezy way to get us to report the current cursor location.
            invalidateCursor();
        }
    }
    
    void updateAfterEdit() {
        invalidate();
        int curs = getSelectionStart();
        if (curs >= 0 || ( & .) ==
                             .) {
            registerForPreDraw();
        }
        if (curs >= 0) {
             = true;
            if (isFocused()) {
                 = SystemClock.uptimeMillis();
                makeBlink();
            }
        }
        
        checkForResize();
    }
    
    
Called by the framework in response to a request to begin a batch of edit operations through a call to link beginBatchEdit().
    public void onBeginBatchEdit() {
    }
    
    
Called by the framework in response to a request to end a batch of edit operations through a call to link endBatchEdit().
    public void onEndBatchEdit() {
    }
    
    
Called by the framework in response to a private command from the current method, provided by it calling InputConnection.performPrivateCommand().

Parameters:
action The action name of the command.
data Any additional data for the command. This may be null.
Returns:
Return true if you handled the command, else false.
    public boolean onPrivateIMECommand(String actionBundle data) {
        return false;
    }
    private void nullLayouts() {
        if ( instanceof BoringLayout &&  == null) {
             = (BoringLayout;
        }
        if ( instanceof BoringLayout &&  == null) {
             = (BoringLayout;
        }
         =  = null;
    }

    
Make a new Layout based on the already-measured size of the view, on the assumption that it was measured correctly at some point.
    private void assumeLayout() {
        int width =  -  - getCompoundPaddingLeft() - getCompoundPaddingRight();
        if (width < 1) {
            width = 0;
        }
        int physicalWidth = width;
        if () {
            width = ;
        }
        makeNewLayout(widthphysicalWidth,
                      physicalWidthfalse);
    }

    
The width passed in is now the desired layout width, not the full view width with padding.
    protected void makeNewLayout(int wint hintWidth,
                                 BoringLayout.Metrics boring,
                                 BoringLayout.Metrics hintBoring,
                                 int ellipsisWidthboolean bringIntoView) {
        stopMarquee();
         = true;
        if (w < 0) {
            w = 0;
        }
        if (hintWidth < 0) {
            hintWidth = 0;
        }
        Layout.Alignment alignment;
        switch ( & .) {
            case .:
                alignment = ..;
                break;
            case .:
                alignment = ..;
                break;
            default:
                alignment = ..;
        }
        boolean shouldEllipsize =  != null &&  == null;
        if ( instanceof Spannable) {
             = new DynamicLayout(w,
                    alignment,
                     == null ?  : null,
                    ellipsisWidth);
        } else {
            if (boring == ) {
                boring = BoringLayout.isBoring(,
                                               );
                if (boring != null) {
                     = boring;
                }
            }
            if (boring != null) {
                if (boring.width <= w &&
                    ( == null || boring.width <= ellipsisWidth)) {
                    if ( != null) {
                         = .
                                replaceOrMake(,
                                walignment,
                                boring);
                    } else {
                         = BoringLayout.make(,
                                walignment,
                                boring);
                    }
                     = (BoringLayout;
                } else if (shouldEllipsize && boring.width <= w) {
                    if ( != null) {
                         = .
                                replaceOrMake(,
                                walignment,
                                boring,
                                ellipsisWidth);
                    } else {
                         = BoringLayout.make(,
                                walignment,
                                boring,
                                ellipsisWidth);
                    }
                } else if (shouldEllipsize) {
                     = new StaticLayout(,
                                0, .length(),
                                walignment,
                                ,
                                ellipsisWidth);
                } else {
                     = new StaticLayout(,
                            walignment,
                            );
                }
            } else if (shouldEllipsize) {
                 = new StaticLayout(,
                            0, .length(),
                            walignment,
                            ,
                            ellipsisWidth);
            } else {
                 = new StaticLayout(,
                        walignment,
                        );
            }
        }
        shouldEllipsize =  != null;
         = null;
        if ( != null) {
            if (shouldEllipsizehintWidth = w;
            if (hintBoring == ) {
                hintBoring = BoringLayout.isBoring(,
                                                   );
                if (hintBoring != null) {
                     = hintBoring;
                }
            }
            if (hintBoring != null) {
                if (hintBoring.width <= hintWidth &&
                    (!shouldEllipsize || hintBoring.width <= ellipsisWidth)) {
                    if ( != null) {
                         = .
                                replaceOrMake(,
                                hintWidthalignment,
                                hintBoring);
                    } else {
                         = BoringLayout.make(,
                                hintWidthalignment,
                                hintBoring);
                    }
                     = (BoringLayout;
                } else if (shouldEllipsize && hintBoring.width <= hintWidth) {
                    if ( != null) {
                         = .
                                replaceOrMake(,
                                hintWidthalignment,
                                hintBoring,
                                ellipsisWidth);
                    } else {
                         = BoringLayout.make(,
                                hintWidthalignment,
                                hintBoring,
                                ellipsisWidth);
                    }
                } else if (shouldEllipsize) {
                     = new StaticLayout(,
                                0, .length(),
                                hintWidthalignment,
                                ,
                                ellipsisWidth);
                } else {
                     = new StaticLayout(,
                            hintWidthalignment,
                            );
                }
            } else if (shouldEllipsize) {
                 = new StaticLayout(,
                            0, .length(),
                            hintWidthalignment,
                            ,
                            ellipsisWidth);
            } else {
                 = new StaticLayout(,
                        hintWidthalignment,
                        );
            }
        }
        if (bringIntoView) {
            registerForPreDraw();
        }
        if ( == ..) {
            if (!compressText(ellipsisWidth)) {
                final int height = .;
                // If the size of the view does not depend on the size of the text, try to
                // start the marquee immediately
                if (height != . && height != .) {
                    startMarquee();
                } else {
                    // Defer the start of the marquee until we know our width (see setFrame())
                     = true;
                }
            }
        }
        // CursorControllers need a non-null mLayout
        prepareCursorControllers();
    }
    private boolean compressText(float width) {
        // Only compress the text if it hasn't been compressed by the previous pass
        if (width > 0.0f &&  != null && getLineCount() == 1 && ! &&
                .getTextScaleX() == 1.0f) {
            final float textWidth = .getLineWidth(0);
            final float overflow = (textWidth + 1.0f - width) / width;
            if (overflow > 0.0f && overflow <= .) {
                .setTextScaleX(1.0f - overflow - 0.005f);
                post(new Runnable() {
                    public void run() {
                        requestLayout();
                    }
                });
                return true;
            }
        }
        return false;
    }
    private static int desired(Layout layout) {
        int n = layout.getLineCount();
        CharSequence text = layout.getText();
        float max = 0;
        // if any line was wrapped, we can't use it.
        // but it's ok for the last line not to have a newline
        for (int i = 0; i < n - 1; i++) {
            if (text.charAt(layout.getLineEnd(i) - 1) != '\n')
                return -1;
        }
        for (int i = 0; i < ni++) {
            max = Math.max(maxlayout.getLineWidth(i));
        }
        return (int) FloatMath.ceil(max);
    }

    
Set whether the TextView includes extra top and bottom padding to make room for accents that go above the normal ascent and descent. The default is true.

Attr:
ref android.R.styleable#TextView_includeFontPadding
    public void setIncludeFontPadding(boolean includepad) {
         = includepad;
        if ( != null) {
            nullLayouts();
            requestLayout();
            invalidate();
        }
    }
    private static final BoringLayout.Metrics UNKNOWN_BORING = new BoringLayout.Metrics();
    @Override
    protected void onMeasure(int widthMeasureSpecint heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int width;
        int height;
        BoringLayout.Metrics boring = ;
        BoringLayout.Metrics hintBoring = ;
        int des = -1;
        boolean fromexisting = false;
        if (widthMode == .) {
            // Parent has told us how big to be. So be it.
            width = widthSize;
        } else {
            if ( != null &&  == null) {
                des = desired();
            }
            if (des < 0) {
                boring = BoringLayout.isBoring();
                if (boring != null) {
                     = boring;
                }
            } else {
                fromexisting = true;
            }
            if (boring == null || boring == ) {
                if (des < 0) {
                    des = (int) FloatMath.ceil(Layout.getDesiredWidth());
                }
                width = des;
            } else {
                width = boring.width;
            }
            final Drawables dr = ;
            if (dr != null) {
                width = Math.max(widthdr.mDrawableWidthTop);
                width = Math.max(widthdr.mDrawableWidthBottom);
            }
            if ( != null) {
                int hintDes = -1;
                int hintWidth;
                if ( != null &&  == null) {
                    hintDes = desired();
                }
                if (hintDes < 0) {
                    hintBoring = BoringLayout.isBoring();
                    if (hintBoring != null) {
                         = hintBoring;
                    }
                }
                if (hintBoring == null || hintBoring == ) {
                    if (hintDes < 0) {
                        hintDes = (int) FloatMath.ceil(
                                Layout.getDesiredWidth());
                    }
                    hintWidth = hintDes;
                } else {
                    hintWidth = hintBoring.width;
                }
                if (hintWidth > width) {
                    width = hintWidth;
                }
            }
            width += getCompoundPaddingLeft() + getCompoundPaddingRight();
            if ( == ) {
                width = Math.min(width * getLineHeight());
            } else {
                width = Math.min(width);
            }
            if ( == ) {
                width = Math.max(width * getLineHeight());
            } else {
                width = Math.max(width);
            }
            // Check against our minimum width
            width = Math.max(widthgetSuggestedMinimumWidth());
            if (widthMode == .) {
                width = Math.min(widthSizewidth);
            }
        }
        int want = width - getCompoundPaddingLeft() - getCompoundPaddingRight();
        int unpaddedWidth = want;
        int hintWant = want;
        if ()
            want = ;
        int hintWidth =  == null ? hintWant : .getWidth();
        if ( == null) {
            makeNewLayout(wanthintWantboringhintBoring,
                          width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false);
        } else if ((.getWidth() != want) || (hintWidth != hintWant) ||
                   (.getEllipsizedWidth() !=
                        width - getCompoundPaddingLeft() - getCompoundPaddingRight())) {
            if ( == null &&  == null &&
                    want > .getWidth() &&
                    ( instanceof BoringLayout ||
                            (fromexisting && des >= 0 && des <= want))) {
                .increaseWidthTo(want);
            } else {
                makeNewLayout(wanthintWantboringhintBoring,
                              width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false);
            }
        } else {
            // Width has not changed.
        }
        if (heightMode == .) {
            // Parent has told us how big to be. So be it.
            height = heightSize;
             = -1;
        } else {
            int desired = getDesiredHeight();
            height = desired;
             = desired;
            if (heightMode == .) {
                height = Math.min(desiredheightSize);
            }
        }
        int unpaddedHeight = height - getCompoundPaddingTop() - getCompoundPaddingBottom();
        if ( ==  && .getLineCount() > ) {
            unpaddedHeight = Math.min(unpaddedHeight.getLineTop());
        }
        /*
         * We didn't let makeNewLayout() register to bring the cursor into view,
         * so do it here if there is any possibility that it is needed.
         */
        if ( != null ||
            .getWidth() > unpaddedWidth ||
            .getHeight() > unpaddedHeight) {
            registerForPreDraw();
        } else {
            scrollTo(0, 0);
        }
        setMeasuredDimension(widthheight);
    }
    private int getDesiredHeight() {
        return Math.max(
                getDesiredHeight(true),
                getDesiredHeight( != null));
    }
    private int getDesiredHeight(Layout layoutboolean cap) {
        if (layout == null) {
            return 0;
        }
        int linecount = layout.getLineCount();
        int pad = getCompoundPaddingTop() + getCompoundPaddingBottom();
        int desired = layout.getLineTop(linecount);
        final Drawables dr = ;
        if (dr != null) {
            desired = Math.max(desireddr.mDrawableHeightLeft);
            desired = Math.max(desireddr.mDrawableHeightRight);
        }
        desired += pad;
        if ( == ) {
            /*
             * Don't cap the hint to a certain number of lines.
             * (Do cap it, though, if we have a maximum pixel height.)
             */
            if (cap) {
                if (linecount > ) {
                    desired = layout.getLineTop() +
                              layout.getBottomPadding();
                    if (dr != null) {
                        desired = Math.max(desireddr.mDrawableHeightLeft);
                        desired = Math.max(desireddr.mDrawableHeightRight);
                    }
                    desired += pad;
                    linecount = ;
                }
            }
        } else {
            desired = Math.min(desired);
        }
        if ( == ) {
            if (linecount < ) {
                desired += getLineHeight() * ( - linecount);
            }
        } else {
            desired = Math.max(desired);
        }
        // Check against our minimum height
        desired = Math.max(desiredgetSuggestedMinimumHeight());
        return desired;
    }

    
Check whether a change to the existing text layout requires a new view layout.
    private void checkForResize() {
        boolean sizeChanged = false;
        if ( != null) {
            // Check if our width changed
            if (. == .) {
                sizeChanged = true;
                invalidate();
            }
            // Check if our height changed
            if (. == .) {
                int desiredHeight = getDesiredHeight();
                if (desiredHeight != this.getHeight()) {
                    sizeChanged = true;
                }
            } else if (. == .) {
                if ( >= 0) {
                    int desiredHeight = getDesiredHeight();
                    if (desiredHeight != ) {
                        sizeChanged = true;
                    }
                }
            }
        }
        if (sizeChanged) {
            requestLayout();
            // caller will have already invalidated
        }
    }

    
Check whether entirely new text requires a new view layout or merely a new text layout.
    private void checkForRelayout() {
        // If we have a fixed width, we can just swap in a new text layout
        // if the text height stays the same or if the view height is fixed.
        if ((. != . ||
                ( ==  &&  == )) &&
                ( == null ||  != null) &&
                ( -  - getCompoundPaddingLeft() - getCompoundPaddingRight() > 0)) {
            // Static width, so try making a new text layout.
            int oldht = .getHeight();
            int want = .getWidth();
            int hintWant =  == null ? 0 : .getWidth();
            /*
             * No need to bring the text into view, since the size is not
             * changing (unless we do the requestLayout(), in which case it
             * will happen at measure).
             */
            makeNewLayout(wanthintWant,
                           -  - getCompoundPaddingLeft() - getCompoundPaddingRight(),
                          false);
            if ( != ..) {
                // In a fixed-height view, so use our new text layout.
                if (. != . &&
                    . != .) {
                    invalidate();
                    return;
                }
    
                // Dynamic height, but height has stayed the same,
                // so use our new text layout.
                if (.getHeight() == oldht &&
                    ( == null || .getHeight() == oldht)) {
                    invalidate();
                    return;
                }
            }
            // We lose: the height has changed and we have a dynamic height.
            // Request a new view layout using our new text layout.
            requestLayout();
            invalidate();
        } else {
            // Dynamic width, so we have no choice but to request a new
            // view layout with a new text layout.
            nullLayouts();
            requestLayout();
            invalidate();
        }
    }

    
Returns true if anything changed.
    private boolean bringTextIntoView() {
        int line = 0;
        if (( & .) == .) {
            line = .getLineCount() - 1;
        }
        Layout.Alignment a = .getParagraphAlignment(line);
        int dir = .getParagraphDirection(line);
        int hspace =  -  - getCompoundPaddingLeft() - getCompoundPaddingRight();
        int vspace =  -  - getExtendedPaddingTop() - getExtendedPaddingBottom();
        int ht = .getHeight();
        int scrollxscrolly;
        if (a == ..) {
            /*
             * Keep centered if possible, or, if it is too wide to fit,
             * keep leading edge in view.
             */
            int left = (int) FloatMath.floor(.getLineLeft(line));
            int right = (int) FloatMath.ceil(.getLineRight(line));
            if (right - left < hspace) {
                scrollx = (right + left) / 2 - hspace / 2;
            } else {
                if (dir < 0) {
                    scrollx = right - hspace;
                } else {
                    scrollx = left;
                }
            }
        } else if (a == ..) {
            /*
             * Keep leading edge in view.
             */
            if (dir < 0) {
                int right = (int) FloatMath.ceil(.getLineRight(line));
                scrollx = right - hspace;
            } else {
                scrollx = (int) FloatMath.floor(.getLineLeft(line));
            }
        } else /* a == Layout.Alignment.ALIGN_OPPOSITE */ {
            /*
             * Keep trailing edge in view.
             */
            if (dir < 0) {
                scrollx = (int) FloatMath.floor(.getLineLeft(line));
            } else {
                int right = (int) FloatMath.ceil(.getLineRight(line));
                scrollx = right - hspace;
            }
        }
        if (ht < vspace) {
            scrolly = 0;
        } else {
            if (( & .) == .) {
                scrolly = ht - vspace;
            } else {
                scrolly = 0;
            }
        }
        if (scrollx !=  || scrolly != ) {
            scrollTo(scrollxscrolly);
            return true;
        } else {
            return false;
        }
    }

    
Move the point, specified by the offset, into the view if it is needed. This has to be called after layout. Returns true if anything changed.
    public boolean bringPointIntoView(int offset) {
        boolean changed = false;
        int line = .getLineForOffset(offset);
        // FIXME: Is it okay to truncate this, or should we round?
        final int x = (int).getPrimaryHorizontal(offset);
        final int top = .getLineTop(line);
        final int bottom = .getLineTop(line + 1);
        int left = (int) FloatMath.floor(.getLineLeft(line));
        int right = (int) FloatMath.ceil(.getLineRight(line));
        int ht = .getHeight();
        int grav;
        switch (.getParagraphAlignment(line)) {
            case :
                grav = 1;
                break;
            case :
                grav = -1;
                break;
            default:
                grav = 0;
        }
        grav *= .getParagraphDirection(line);
        int hspace =  -  - getCompoundPaddingLeft() - getCompoundPaddingRight();
        int vspace =  -  - getExtendedPaddingTop() - getExtendedPaddingBottom();
        int hslack = (bottom - top) / 2;
        int vslack = hslack;
        if (vslack > vspace / 4)
            vslack = vspace / 4;
        if (hslack > hspace / 4)
            hslack = hspace / 4;
        int hs = ;
        int vs = ;
        if (top - vs < vslack)
            vs = top - vslack;
        if (bottom - vs > vspace - vslack)
            vs = bottom - (vspace - vslack);
        if (ht - vs < vspace)
            vs = ht - vspace;
        if (0 - vs > 0)
            vs = 0;
        if (grav != 0) {
            if (x - hs < hslack) {
                hs = x - hslack;
            }
            if (x - hs > hspace - hslack) {
                hs = x - (hspace - hslack);
            }
        }
        if (grav < 0) {
            if (left - hs > 0)
                hs = left;
            if (right - hs < hspace)
                hs = right - hspace;
        } else if (grav > 0) {
            if (right - hs < hspace)
                hs = right - hspace;
            if (left - hs > 0)
                hs = left;
        } else /* grav == 0 */ {
            if (right - left <= hspace) {
                /*
                 * If the entire text fits, center it exactly.
                 */
                hs = left - (hspace - (right - left)) / 2;
            } else if (x > right - hslack) {
                /*
                 * If we are near the right edge, keep the right edge
                 * at the edge of the view.
                 */
                hs = right - hspace;
            } else if (x < left + hslack) {
                /*
                 * If we are near the left edge, keep the left edge
                 * at the edge of the view.
                 */
                hs = left;
            } else if (left > hs) {
                /*
                 * Is there whitespace visible at the left?  Fix it if so.
                 */
                hs = left;
            } else if (right < hs + hspace) {
                /*
                 * Is there whitespace visible at the right?  Fix it if so.
                 */
                hs = right - hspace;
            } else {
                /*
                 * Otherwise, float as needed.
                 */
                if (x - hs < hslack) {
                    hs = x - hslack;
                }
                if (x - hs > hspace - hslack) {
                    hs = x - (hspace - hslack);
                }
            }
        }
        if (hs !=  || vs != ) {
            if ( == null) {
                scrollTo(hsvs);
            } else {
                long duration = AnimationUtils.currentAnimationTimeMillis() - ;
                int dx = hs - ;
                int dy = vs - ;
                if (duration > ) {
                    .startScroll(dxdy);
                    awakenScrollBars(.getDuration());
                    invalidate();
                } else {
                    if (!.isFinished()) {
                        .abortAnimation();
                    }
                    scrollBy(dxdy);
                }
                 = AnimationUtils.currentAnimationTimeMillis();
            }
            changed = true;
        }
        if (isFocused()) {
            // This offsets because getInterestingRect() is in terms of
            // viewport coordinates, but requestRectangleOnScreen()
            // is in terms of content coordinates.
            Rect r = new Rect(xtopx + 1, bottom);
            getInterestingRect(rline);
            r.offset();
            if (requestRectangleOnScreen(r)) {
                changed = true;
            }
        }
        return changed;
    }

    
Move the cursor, if needed, so that it is at an offset that is visible to the user. This will not move the cursor if it represents more than one character (a selection range). This will only work if the TextView contains spannable text; otherwise it will do nothing.

Returns:
True if the cursor was actually moved, false otherwise.
    public boolean moveCursorToVisibleOffset() {
        if (!( instanceof Spannable)) {
            return false;
        }
        int start = getSelectionStart();
        int end = getSelectionEnd();
        if (start != end) {
            return false;
        }
        
        // First: make sure the line is visible on screen:
        
        int line = .getLineForOffset(start);
        final int top = .getLineTop(line);
        final int bottom = .getLineTop(line + 1);
        final int vspace =  -  - getExtendedPaddingTop() - getExtendedPaddingBottom();
        int vslack = (bottom - top) / 2;
        if (vslack > vspace / 4)
            vslack = vspace / 4;
        final int vs = ;
        if (top < (vs+vslack)) {
            line = .getLineForVertical(vs+vslack+(bottom-top));
        } else if (bottom > (vspace+vs-vslack)) {
            line = .getLineForVertical(vspace+vs-vslack-(bottom-top));
        }
        
        // Next: make sure the character is visible on screen:
        
        final int hspace =  -  - getCompoundPaddingLeft() - getCompoundPaddingRight();
        final int hs = ;
        final int leftChar = .getOffsetForHorizontal(linehs);
        final int rightChar = .getOffsetForHorizontal(linehspace+hs);
        
        int newStart = start;
        if (newStart < leftChar) {
            newStart = leftChar;
        } else if (newStart > rightChar) {
            newStart = rightChar;
        }
        
        if (newStart != start) {
            Selection.setSelection((Spannable)newStart);
            return true;
        }
        
        return false;
    }
    @Override
    public void computeScroll() {
        if ( != null) {
            if (.computeScrollOffset()) {
                 = .getCurrX();
                 = .getCurrY();
                postInvalidate();  // So we draw again
            }
        }
    }
    private void getInterestingRect(Rect rint line) {
        // Rectangle can can be expanded on first and last line to take
        // padding into account.
        // TODO Take left/right padding into account too?
        if (line == 0) r.top -= getExtendedPaddingTop();
        if (line == .getLineCount() - 1) r.bottom += getExtendedPaddingBottom();
    }
        final int horizontalOffset = viewportToContentHorizontalOffset();
        r.left += horizontalOffset;
        r.right += horizontalOffset;
        final int verticalOffset = viewportToContentVerticalOffset();
        r.top += verticalOffset;
        r.bottom += verticalOffset;
    }
    private int viewportToContentHorizontalOffset() {
        return getCompoundPaddingLeft() - ;
    }
    private int viewportToContentVerticalOffset() {
        int offset = getExtendedPaddingTop() - ;
        if (( & .) != .) {
            offset += getVerticalOffset(false);
        }
        return offset;
    }
    @Override
    public void debug(int depth) {
        super.debug(depth);
        String output = debugIndent(depth);
        output += "frame={" +  + ", " +  + ", " + 
                + ", " +  + "} scroll={" +  + ", " + 
                + "} ";
        if ( != null) {
            output += "mText=\"" +  + "\" ";
            if ( != null) {
                output += "mLayout width=" + .getWidth()
                        + " height=" + .getHeight();
            }
        } else {
            output += "mText=NULL";
        }
        Log.d(output);
    }

    
    @ViewDebug.ExportedProperty(category = "text")
    public int getSelectionStart() {
        return Selection.getSelectionStart(getText());
    }

    
    @ViewDebug.ExportedProperty(category = "text")
    public int getSelectionEnd() {
        return Selection.getSelectionEnd(getText());
    }

    
Return true iff there is a selection inside this text view.
    public boolean hasSelection() {
        final int selectionStart = getSelectionStart();
        final int selectionEnd = getSelectionEnd();
        return selectionStart >= 0 && selectionStart != selectionEnd;
    }

    
Sets the properties of this field (lines, horizontally scrolling, transformation method) to be for a single-line input.

Attr:
ref android.R.styleable#TextView_singleLine
    public void setSingleLine() {
        setSingleLine(true);
    }

    
If true, sets the properties of this field (lines, horizontally scrolling, transformation method) to be for a single-line input; if false, restores these to the default conditions. Note that calling this with false restores default conditions, not necessarily those that were in effect prior to calling it with true.

Attr:
ref android.R.styleable#TextView_singleLine
    public void setSingleLine(boolean singleLine) {
                == .) {
            if (singleLine) {
                 &= ~.;
            } else {
                 |= .;
            }
        }
        applySingleLine(singleLinetrue);
    }
    private void applySingleLine(boolean singleLineboolean applyTransformation) {
         = singleLine;
        if (singleLine) {
            setLines(1);
            setHorizontallyScrolling(true);
            if (applyTransformation) {
                setTransformationMethod(SingleLineTransformationMethod.
                                        getInstance());
            }
        } else {
            setMaxLines(.);
            setHorizontallyScrolling(false);
            if (applyTransformation) {
                setTransformationMethod(null);
            }
        }
    }
    
    
Causes words in the text that are longer than the view is wide to be ellipsized instead of broken in the middle. You may also want to setSingleLine() or setHorizontallyScrolling(boolean) to constrain the text to a single line. Use null to turn off ellipsizing.

Attr:
ref android.R.styleable#TextView_ellipsize
    public void setEllipsize(TextUtils.TruncateAt where) {
         = where;
        if ( != null) {
            nullLayouts();
            requestLayout();
            invalidate();
        }
    }

    
Sets how many times to repeat the marquee animation. Only applied if the TextView has marquee enabled. Set to -1 to repeat indefinitely.

Attr:
ref android.R.styleable#TextView_marqueeRepeatLimit
    public void setMarqueeRepeatLimit(int marqueeLimit) {
         = marqueeLimit;
    }

    
Returns where, if anywhere, words that are longer than the view is wide should be ellipsized.
    public TextUtils.TruncateAt getEllipsize() {
        return ;
    }

    
Set the TextView so that when it takes focus, all the text is selected.

Attr:
ref android.R.styleable#TextView_selectAllOnFocus
    public void setSelectAllOnFocus(boolean selectAllOnFocus) {
         = selectAllOnFocus;
        if (selectAllOnFocus && !( instanceof Spannable)) {
            setText(.);
        }
    }

    
Set whether the cursor is visible. The default is true.

Attr:
ref android.R.styleable#TextView_cursorVisible
    public void setCursorVisible(boolean visible) {
         = visible;
        invalidate();
        if (visible) {
            makeBlink();
        } else if ( != null) {
            .removeCallbacks();
        }
        // InsertionPointCursorController depends on mCursorVisible
        prepareCursorControllers();
    }
    private boolean canMarquee() {
        int width = ( -  - getCompoundPaddingLeft() - getCompoundPaddingRight());
        return width > 0 && .getLineWidth(0) > width;
    }
    private void startMarquee() {
        // Do not ellipsize EditText
        if ( != nullreturn;
            return;
        }
        if (( == null || .isStopped()) && (isFocused() || isSelected()) &&
                getLineCount() == 1 && canMarquee()) {
            if ( == null = new Marquee(this);
            .start();
        }
    }
    private void stopMarquee() {
        if ( != null && !.isStopped()) {
            .stop();
        }
    }
    private void startStopMarquee(boolean start) {
        if ( == ..) {
            if (start) {
                startMarquee();
            } else {
                stopMarquee();
            }
        }
    }
    private static final class Marquee extends Handler {
        // TODO: Add an option to configure this
        private static final float MARQUEE_DELTA_MAX = 0.07f;
        private static final int MARQUEE_DELAY = 1200;
        private static final int MARQUEE_RESTART_DELAY = 1200;
        private static final int MARQUEE_RESOLUTION = 1000 / 30;
        private static final int MARQUEE_PIXELS_PER_SECOND = 30;
        private static final byte MARQUEE_STOPPED = 0x0;
        private static final byte MARQUEE_STARTING = 0x1;
        private static final byte MARQUEE_RUNNING = 0x2;
        private static final int MESSAGE_START = 0x1;
        private static final int MESSAGE_TICK = 0x2;
        private static final int MESSAGE_RESTART = 0x3;
        private final WeakReference<TextViewmView;
        private byte mStatus = ;
        private final float mScrollUnit;
        private float mMaxScroll;
        float mMaxFadeScroll;
        private float mGhostStart;
        private float mGhostOffset;
        private float mFadeStop;
        private int mRepeatLimit;
        float mScroll;
        Marquee(TextView v) {
            final float density = v.getContext().getResources().getDisplayMetrics().;
             = ( * density) / ;
             = new WeakReference<TextView>(v);
        }
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case :
                     = ;
                    tick();
                    break;
                case :
                    tick();
                    break;
                case :
                    if ( == ) {
                        if ( >= 0) {
                            --;
                        }
                        start();
                    }
                    break;
            }
        }
        void tick() {
            if ( != ) {
                return;
            }
            removeMessages();
            final TextView textView = .get();
            if (textView != null && (textView.isFocused() || textView.isSelected())) {
                 += ;
                if ( > ) {
                     = ;
                    sendEmptyMessageDelayed();
                } else {
                    sendEmptyMessageDelayed();
                }
                textView.invalidate();
            }
        }
        void stop() {
             = ;
            removeMessages();
            removeMessages();
            removeMessages();
            resetScroll();
        }
        private void resetScroll() {
             = 0.0f;
            final TextView textView = .get();
            if (textView != nulltextView.invalidate();
        }
        void start(int repeatLimit) {
            if (repeatLimit == 0) {
                stop();
                return;
            }
             = repeatLimit;
            final TextView textView = .get();
            if (textView != null && textView.mLayout != null) {
                 = ;
                 = 0.0f;
                final int textWidth = textView.getWidth() - textView.getCompoundPaddingLeft() -
                        textView.getCompoundPaddingRight();
                final float lineWidth = textView.mLayout.getLineWidth(0);
                final float gap = textWidth / 3.0f;
                 = lineWidth - textWidth + gap;
                 =  + textWidth;
                 = lineWidth + gap;
                 = lineWidth + textWidth / 6.0f;
                 =  + lineWidth + lineWidth;
                textView.invalidate();
                sendEmptyMessageDelayed();
            }
        }
        float getGhostOffset() {
            return ;
        }
        boolean shouldDrawLeftFade() {
            return  <= ;
        }
        boolean shouldDrawGhost() {
            return  ==  &&  > ;
        }
        boolean isRunning() {
            return  == ;
        }
        boolean isStopped() {
            return  == ;
        }
    }

    
This method is called when the text is changed, in case any subclasses would like to know.

Parameters:
text The text the TextView is displaying.
start The offset of the start of the range of the text that was modified.
before The offset of the former end of the range of the text that was modified. If text was simply inserted, this will be the same as start. If text was replaced with new text or deleted, the length of the old text was before-start.
after The offset of the end of the range of the text that was modified. If text was simply deleted, this will be the same as start. If text was replaced with new text or inserted, the length of the new text is after-start.
    protected void onTextChanged(CharSequence text,
                                 int startint beforeint after) {
    }

    
This method is called when the selection has changed, in case any subclasses would like to know.

Parameters:
selStart The new selection start location.
selEnd The new selection end location.
    protected void onSelectionChanged(int selStartint selEnd) {
    }
    
    
Adds a TextWatcher to the list of those whose methods are called whenever this TextView's text changes.

In 1.0, the android.text.TextWatcher.afterTextChanged(android.text.Editable) method was erroneously not called after setText(java.lang.CharSequence) calls. Now, doing setText(java.lang.CharSequence) if there are any text changed listeners forces the buffer type to Editable if it would not otherwise be and does call this method.

    public void addTextChangedListener(TextWatcher watcher) {
        if ( == null) {
             = new ArrayList<TextWatcher>();
        }
        .add(watcher);
    }

    
Removes the specified TextWatcher from the list of those whose methods are called whenever this TextView's text changes.
    public void removeTextChangedListener(TextWatcher watcher) {
        if ( != null) {
            int i = .indexOf(watcher);
            if (i >= 0) {
                .remove(i);
            }
        }
    }
    private void sendBeforeTextChanged(CharSequence textint startint before,
                                   int after) {
        if ( != null) {
            final ArrayList<TextWatcherlist = ;
            final int count = list.size();
            for (int i = 0; i < counti++) {
                list.get(i).beforeTextChanged(textstartbeforeafter);
            }
        }
    }

    
Not private so it can be called from an inner class without going through a thunk.
    void sendOnTextChanged(CharSequence textint startint before,
                                   int after) {
        if ( != null) {
            final ArrayList<TextWatcherlist = ;
            final int count = list.size();
            for (int i = 0; i < counti++) {
                list.get(i).onTextChanged(textstartbeforeafter);
            }
        }
    }

    
Not private so it can be called from an inner class without going through a thunk.
    void sendAfterTextChanged(Editable text) {
        if ( != null) {
            final ArrayList<TextWatcherlist = ;
            final int count = list.size();
            for (int i = 0; i < counti++) {
                list.get(i).afterTextChanged(text);
            }
        }
    }

    
Not private so it can be called from an inner class without going through a thunk.
    void handleTextChanged(CharSequence bufferint start,
            int beforeint after) {
        final InputMethodState ims = ;
        if (ims == null || ims.mBatchEditNesting == 0) {
            updateAfterEdit();
        }
        if (ims != null) {
            ims.mContentChanged = true;
            if (ims.mChangedStart < 0) {
                ims.mChangedStart = start;
                ims.mChangedEnd = start+before;
            } else {
                if (ims.mChangedStart > startims.mChangedStart = start;
                if (ims.mChangedEnd < (start+before)) ims.mChangedEnd = start+before;
            }
            ims.mChangedDelta += after-before;
        }
        
        sendOnTextChanged(bufferstartbeforeafter);
        onTextChanged(bufferstartbeforeafter);
        // Hide the controller if the amount of content changed
        if (before != after) {
            hideControllers();
        }
    }
    
    
Not private so it can be called from an inner class without going through a thunk.
    void spanChange(Spanned bufObject whatint oldStartint newStart,
            int oldEndint newEnd) {
        // XXX Make the start and end move together if this ends up
        // spending too much time invalidating.
        boolean selChanged = false;
        int newSelStart=-1, newSelEnd=-1;
        
        final InputMethodState ims = ;
        
        if (what == .) {
             = true;
            selChanged = true;
            newSelEnd = newStart;
            if (!isFocused()) {
                 = true;
            }
            if (oldStart >= 0 || newStart >= 0) {
                invalidateCursor(Selection.getSelectionStart(buf), oldStartnewStart);
                registerForPreDraw();
                if (isFocused()) {
                     = SystemClock.uptimeMillis();
                    makeBlink();
                }
            }
        }
        if (what == .) {
             = true;
            selChanged = true;
            newSelStart = newStart;
            if (!isFocused()) {
                 = true;
            }
            if (oldStart >= 0 || newStart >= 0) {
                int end = Selection.getSelectionEnd(buf);
                invalidateCursor(endoldStartnewStart);
            }
        }
        if (selChanged) {
            if ((buf.getSpanFlags(what)&.) == 0) {
                if (newSelStart < 0) {
                    newSelStart = Selection.getSelectionStart(buf);
                }
                if (newSelEnd < 0) {
                    newSelEnd = Selection.getSelectionEnd(buf);
                }
                onSelectionChanged(newSelStartnewSelEnd);
            }
        }
        
        if (what instanceof UpdateAppearance ||
            what instanceof ParagraphStyle) {
            if (ims == null || ims.mBatchEditNesting == 0) {
                invalidate();
                 = true;
                checkForResize();
            } else {
                ims.mContentChanged = true;
            }
        }
        if (MetaKeyKeyListener.isMetaTracker(bufwhat)) {
             = true;
            if (ims != null && MetaKeyKeyListener.isSelectingMetaTracker(bufwhat)) {
                ims.mSelectionModeChanged = true;
            }
            if (Selection.getSelectionStart(buf) >= 0) {
                if (ims == null || ims.mBatchEditNesting == 0) {
                    invalidateCursor();
                } else {
                    ims.mCursorChanged = true;
                }
            }
        }
        
        if (what instanceof ParcelableSpan) {
            // If this is a span that can be sent to a remote process,
            // the current extract editor would be interested in it.
            if (ims != null && ims.mExtracting != null) {
                if (ims.mBatchEditNesting != 0) {
                    if (oldStart >= 0) {
                        if (ims.mChangedStart > oldStart) {
                            ims.mChangedStart = oldStart;
                        }
                        if (ims.mChangedStart > oldEnd) {
                            ims.mChangedStart = oldEnd;
                        }
                    }
                    if (newStart >= 0) {
                        if (ims.mChangedStart > newStart) {
                            ims.mChangedStart = newStart;
                        }
                        if (ims.mChangedStart > newEnd) {
                            ims.mChangedStart = newEnd;
                        }
                    }
                } else {
                    if () Log.v("Span change outside of batch: "
                            + oldStart + "-" + oldEnd + ","
                            + newStart + "-" + newEnd + what);
                    ims.mContentChanged = true;
                }
            }
        }
    }
    private class ChangeWatcher
    implements TextWatcherSpanWatcher {
        private CharSequence mBeforeText;
        public void beforeTextChanged(CharSequence bufferint start,
                                      int beforeint after) {
            if () Log.v("beforeTextChanged start=" + start
                    + " before=" + before + " after=" + after + ": " + buffer);
            if (AccessibilityManager.getInstance().isEnabled()
                    && !isPasswordInputType()) {
                 = buffer.toString();
            }
            TextView.this.sendBeforeTextChanged(bufferstartbeforeafter);
        }
        public void onTextChanged(CharSequence bufferint start,
                                  int beforeint after) {
            if () Log.v("onTextChanged start=" + start
                    + " before=" + before + " after=" + after + ": " + buffer);
            TextView.this.handleTextChanged(bufferstartbeforeafter);
            if (AccessibilityManager.getInstance().isEnabled() &&
                    (isFocused() || isSelected() &&
                    isShown())) {
                sendAccessibilityEventTypeViewTextChanged(startbeforeafter);
                 = null;
            }
        }
        public void afterTextChanged(Editable buffer) {
            if () Log.v("afterTextChanged: " + buffer);
            TextView.this.sendAfterTextChanged(buffer);
            if (MetaKeyKeyListener.getMetaState(buffer,
                                 .) != 0) {
                MetaKeyKeyListener.stopSelecting(TextView.thisbuffer);
            }
        }
        public void onSpanChanged(Spannable buf,
                                  Object whatint sint eint stint en) {
            if () Log.v("onSpanChanged s=" + s + " e=" + e
                    + " st=" + st + " en=" + en + " what=" + what + ": " + buf);
            TextView.this.spanChange(bufwhatssteen);
        }
        public void onSpanAdded(Spannable bufObject whatint sint e) {
            if () Log.v("onSpanAdded s=" + s + " e=" + e
                    + " what=" + what + ": " + buf);
            TextView.this.spanChange(bufwhat, -1, s, -1, e);
        }
        public void onSpanRemoved(Spannable bufObject whatint sint e) {
            if () Log.v("onSpanRemoved s=" + s + " e=" + e
                    + " what=" + what + ": " + buf);
            TextView.this.spanChange(bufwhats, -1, e, -1);
        }
    }
    private void makeBlink() {
        if (!) {
            if ( != null) {
                .removeCallbacks();
            }
            return;
        }
        if ( == null)
             = new Blink(this);
        .postAtTime( + );
    }

    

Hide:
    @Override
    public void dispatchFinishTemporaryDetach() {
         = true;
        super.dispatchFinishTemporaryDetach();
         = false;
    }
    @Override
    public void onStartTemporaryDetach() {
        super.onStartTemporaryDetach();
        // Only track when onStartTemporaryDetach() is called directly,
        // usually because this instance is an editable field in a list
        if (! = true;
    }
    
    @Override
    public void onFinishTemporaryDetach() {
        super.onFinishTemporaryDetach();
        // Only track when onStartTemporaryDetach() is called directly,
        // usually because this instance is an editable field in a list
        if (! = false;
    }
    
    @Override
    protected void onFocusChanged(boolean focusedint directionRect previouslyFocusedRect) {
        if () {
            // If we are temporarily in the detach state, then do nothing.
            super.onFocusChanged(focuseddirectionpreviouslyFocusedRect);
            return;
        }
        
         = SystemClock.uptimeMillis();
        ensureEndedBatchEdit();
        if (focused) {
            int selStart = getSelectionStart();
            int selEnd = getSelectionEnd();
            if (! || (selStart < 0 || selEnd < 0)) {
                // If a tap was used to give focus to that view, move cursor at tap position.
                // Has to be done before onTakeFocus, which can be overloaded.
                final int lastTapPosition = getLastTapPosition();
                if (lastTapPosition >= 0) {
                    Selection.setSelection((SpannablelastTapPosition);
                }
                if ( != null) {
                    .onTakeFocus(this, (Spannabledirection);
                }
                if () {
                    Selection.setSelection((Spannable, 0, .length());
                }
                // The DecorView does not have focus when the 'Done' ExtractEditText button is
                // pressed. Since it is the ViewRoot's mView, it requests focus before
                // ExtractEditText clears focus, which gives focus to the ExtractEditText.
                // This special case ensure that we keep current selection in that case.
                // It would be better to know why the DecorView does not have focus at that time.
                if (((this instanceof ExtractEditText) || ) &&
                        selStart >= 0 && selEnd >= 0) {
                    /*
                     * Someone intentionally set the selection, so let them
                     * do whatever it is that they wanted to do instead of
                     * the default on-focus behavior.  We reset the selection
                     * here instead of just skipping the onTakeFocus() call
                     * because some movement methods do something other than
                     * just setting the selection in theirs and we still
                     * need to go through that path.
                     */
                    Selection.setSelection((SpannableselStartselEnd);
                }
                 = true;
            }
             = false;
             = false;
            if ( instanceof Spannable) {
                Spannable sp = (Spannable;
                MetaKeyKeyListener.resetMetaState(sp);
            }
            makeBlink();
            if ( != null) {
                showError();
            }
        } else {
            if ( != null) {
                hideError();
            }
            // Don't leave us in the middle of a batch edit.
            onEndBatchEdit();
            hideInsertionPointCursorController();
            if (this instanceof ExtractEditText) {
                // terminateTextSelectionMode would remove selection, which we want to keep when
                // ExtractEditText goes out of focus.
                 = false;
            } else {
                terminateTextSelectionMode();
            }
            if ( != null) {
            }
        }
        startStopMarquee(focused);
        if ( != null) {
            .onFocusChanged(thisfocuseddirectionpreviouslyFocusedRect);
        }
        super.onFocusChanged(focuseddirectionpreviouslyFocusedRect);
    }
    private int getLastTapPosition() {
        if ( != null) {
            int lastTapPosition = ((SelectionModifierCursorController)
                    ).getMinTouchOffset();
            if (lastTapPosition >= 0) {
                // Safety check, should not be possible.
                if (lastTapPosition > .length()) {
                    Log.e("Invalid tap focus position (" + lastTapPosition + " vs "
                            + .length() + ")");
                    lastTapPosition = .length();
                }
                return lastTapPosition;
            }
        }
        return -1;
    }
    @Override
    public void onWindowFocusChanged(boolean hasWindowFocus) {
        super.onWindowFocusChanged(hasWindowFocus);
        if (hasWindowFocus) {
            if ( != null) {
                .uncancel();
                if (isFocused()) {
                     = SystemClock.uptimeMillis();
                    makeBlink();
                }
            }
        } else {
            if ( != null) {
                .cancel();
            }
            // Don't leave us in the middle of a batch edit.
            onEndBatchEdit();
            if ( != null) {
                . = false;
            }
            hideControllers();
        }
        startStopMarquee(hasWindowFocus);
    }
    @Override
    protected void onVisibilityChanged(View changedViewint visibility) {
        super.onVisibilityChanged(changedViewvisibility);
        if (visibility != ) {
            hideControllers();
        }
    }

    
Use BaseInputConnection.removeComposingSpans() to remove any IME composing state from this text view.
    public void clearComposingText() {
        if ( instanceof Spannable) {
            BaseInputConnection.removeComposingSpans((Spannable));
        }
    }
    
    @Override
    public void setSelected(boolean selected) {
        boolean wasSelected = isSelected();
        super.setSelected(selected);
        if (selected != wasSelected &&  == ..) {
            if (selected) {
                startMarquee();
            } else {
                stopMarquee();
            }
        }
    }
    private void onTapUpEvent(int prevStartint prevEnd) {
        final int start = getSelectionStart();
        final int end = getSelectionEnd();
        if (start == end) {
            if (start >= prevStart && start < prevEnd) {
                // Restore previous selection
                Selection.setSelection((Spannable)prevStartprevEnd);
                if (hasSelectionController() && !getSelectionController().isShowing()) {
                    // If the anchors aren't showing, revive them.
                    getSelectionController().show();
                } else {
                    // Tapping inside the selection displays the cut/copy/paste context menu
                    // as long as the anchors are already showing.
                    showContextMenu();
                }
                return;
            } else {
                // Tapping outside stops selection mode, if any
                stopTextSelectionMode();
                if (hasInsertionController() && .length() > 0) {
                    getInsertionController().show();
                }
            }
        } else if (hasSelection() && hasSelectionController()) {
            getSelectionController().show();
        }
    }
    class CommitSelectionReceiver extends ResultReceiver {
        private final int mPrevStartmPrevEnd;
        
        public CommitSelectionReceiver(int prevStartint prevEnd) {
            super(getHandler());
             = prevStart;
             = prevEnd;
        }
        
        @Override
        protected void onReceiveResult(int resultCodeBundle resultData) {
            // If this tap was actually used to show the IMM, leave cursor or selection unchanged
            // by restoring its previous position.
            if (resultCode == .) {
                final int len = .length();
                int start = Math.min(len);
                int end = Math.min(len);
                Selection.setSelection((Spannable)startend);
                if (hasSelection()) {
                    startTextSelectionMode();
                }
            }
        }
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final int action = event.getActionMasked();
        if (action == .) {
            if (hasInsertionController()) {
                getInsertionController().onTouchEvent(event);
            }
            if (hasSelectionController()) {
                getSelectionController().onTouchEvent(event);
            }
            // Reset this state; it will be re-set if super.onTouchEvent
            // causes focus to move to the view.
             = false;
             = false;
        }
        final boolean superResult = super.onTouchEvent(event);
        /*
         * Don't handle the release after a long press, because it will
         * move the selection away from whatever the menu action was
         * trying to affect.
         */
        if ( && action == .) {
             = false;
            return superResult;
        }
        if (( != null || onCheckIsTextEditor()) &&  instanceof Spannable &&  != null) {
            if (hasInsertionController()) {
                getInsertionController().onTouchEvent(event);
            }
            if (hasSelectionController()) {
                getSelectionController().onTouchEvent(event);
            }
            boolean handled = false;
            // Save previous selection, in case this event is used to show the IME.
            int oldSelStart = getSelectionStart();
            int oldSelEnd = getSelectionEnd();
            final int oldScrollX = ;
            final int oldScrollY = ;
            
            if ( != null) {
                handled |= .onTouchEvent(this, (Spannableevent);
            }
            if (isTextEditable()) {
                if ( != oldScrollX ||  != oldScrollY) {
                    // Hide insertion anchor while scrolling. Leave selection.
                    hideInsertionPointCursorController();
                    if ( != null &&
                            .isShowing()) {
                        .updatePosition();
                    }
                }
                if (action == . && isFocused() && !) {
                    InputMethodManager imm = (InputMethodManager)
                          getContext().getSystemService(.);
                    CommitSelectionReceiver csr = null;
                    if (getSelectionStart() != oldSelStart || getSelectionEnd() != oldSelEnd ||
                            didTouchFocusSelect()) {
                        csr = new CommitSelectionReceiver(oldSelStartoldSelEnd);
                    }
                    handled |= imm.showSoftInput(this, 0, csr) && (csr != null);
                    // Cannot be done by CommitSelectionReceiver, which might not always be called,
                    // for instance when dealing with an ExtractEditText.
                    onTapUpEvent(oldSelStartoldSelEnd);
                }
            }
            if (handled) {
                return true;
            }
        }
        return superResult;
    }
    private void prepareCursorControllers() {
        boolean windowSupportsHandles = false;
        ViewGroup.LayoutParams params = getRootView().getLayoutParams();
        if (params instanceof WindowManager.LayoutParams) {
            WindowManager.LayoutParams windowParams = (WindowManager.LayoutParamsparams;
            windowSupportsHandles = windowParams.type < ..
                    || windowParams.type > ..;
        }
        // TODO Add an extra android:cursorController flag to disable the controller?
         = windowSupportsHandles &&  &&  != null;
         = windowSupportsHandles && textCanBeSelected() &&
                 != null;
        if (!) {
             = null;
        }
        if (!) {
            // Stop selection mode if the controller becomes unavailable.
            stopTextSelectionMode();
             = null;
        }
    }

    

Returns:
True iff this TextView contains a text that can be edited.
    private boolean isTextEditable() {
        return  instanceof Editable && onCheckIsTextEditor();
    }

    
Returns true, only while processing a touch gesture, if the initial touch down event caused focus to move to the text view and as a result its selection changed. Only valid while processing the touch gesture of interest.
    public boolean didTouchFocusSelect() {
        return ;
    }
    
    @Override
    public void cancelLongPress() {
        super.cancelLongPress();
         = true;
    }
    
    @Override
    public boolean onTrackballEvent(MotionEvent event) {
        if ( != null &&  instanceof Spannable &&
             != null) {
            if (.onTrackballEvent(this, (Spannableevent)) {
                return true;
            }
        }
        return super.onTrackballEvent(event);
    }
    public void setScroller(Scroller s) {
         = s;
    }
    private static class Blink extends Handler implements Runnable {
        private final WeakReference<TextViewmView;
        private boolean mCancelled;
        public Blink(TextView v) {
             = new WeakReference<TextView>(v);
        }
        public void run() {
            if () {
                return;
            }
            removeCallbacks(Blink.this);
            TextView tv = .get();
            if (tv != null && tv.isFocused()) {
                int st = tv.getSelectionStart();
                int en = tv.getSelectionEnd();
                if (st == en && st >= 0 && en >= 0) {
                    if (tv.mLayout != null) {
                        tv.invalidateCursorPath();
                    }
                    postAtTime(this, SystemClock.uptimeMillis() + );
                }
            }
        }
        void cancel() {
            if (!) {
                removeCallbacks(Blink.this);
                 = true;
            }
        }
        void uncancel() {
             = false;
        }
    }
    @Override
    protected float getLeftFadingEdgeStrength() {
        if ( == ..) {
            if ( != null && !.isStopped()) {
                final Marquee marquee = ;
                if (marquee.shouldDrawLeftFade()) {
                    return marquee.mScroll / getHorizontalFadingEdgeLength();
                } else {
                    return 0.0f;
                }
            } else if (getLineCount() == 1) {
                switch ( & .) {
                    case .:
                        return 0.0f;
                    case .:
                        return (.getLineRight(0) - ( - ) -
                                getCompoundPaddingLeft() - getCompoundPaddingRight() -
                                .getLineLeft(0)) / getHorizontalFadingEdgeLength();
                    case .:
                        return 0.0f;
                }
            }
        }
        return super.getLeftFadingEdgeStrength();
    }
    @Override
    protected float getRightFadingEdgeStrength() {
        if ( == ..) {
            if ( != null && !.isStopped()) {
                final Marquee marquee = ;
                return (marquee.mMaxFadeScroll - marquee.mScroll) / getHorizontalFadingEdgeLength();
            } else if (getLineCount() == 1) {
                switch ( & .) {
                    case .:
                        final int textWidth = ( - ) - getCompoundPaddingLeft() -
                                getCompoundPaddingRight();
                        final float lineWidth = .getLineWidth(0);
                        return (lineWidth - textWidth) / getHorizontalFadingEdgeLength();
                    case .:
                        return 0.0f;
                    case .:
                        return (.getLineWidth(0) - (( - ) -
                                getCompoundPaddingLeft() - getCompoundPaddingRight())) /
                                getHorizontalFadingEdgeLength();
                }
            }
        }
        return super.getRightFadingEdgeStrength();
    }
    @Override
    protected int computeHorizontalScrollRange() {
        if ( != null)
            return .getWidth();
        return super.computeHorizontalScrollRange();
    }
    @Override
    protected int computeVerticalScrollRange() {
        if ( != null)
            return .getHeight();
        return super.computeVerticalScrollRange();
    }
    @Override
    protected int computeVerticalScrollExtent() {
        return getHeight() - getCompoundPaddingTop() - getCompoundPaddingBottom();
    }
    
    public enum BufferType {
        NORMAL, SPANNABLE, EDITABLE,
    }

    
Returns the TextView_textColor attribute from the Resources.StyledAttributes, if set, or the TextAppearance_textColor from the TextView_textAppearance attribute, if TextView_textColor was not set directly.
    public static ColorStateList getTextColors(Context contextTypedArray attrs) {
        ColorStateList colors;
        colors = attrs.getColorStateList(.....
                                         );
        if (colors == null) {
            int ap = attrs.getResourceId(.....
                                         , -1);
            if (ap != -1) {
                TypedArray appearance;
                appearance = context.obtainStyledAttributes(ap,
                                            .....);
                colors = appearance.getColorStateList(.....
                                                  );
                appearance.recycle();
            }
        }
        return colors;
    }

    
Returns the default color from the TextView_textColor attribute from the AttributeSet, if set, or the default color from the TextAppearance_textColor from the TextView_textAppearance attribute, if TextView_textColor was not set directly.
    public static int getTextColor(Context context,
                                   TypedArray attrs,
                                   int def) {
        ColorStateList colors = getTextColors(contextattrs);
        if (colors == null) {
            return def;
        } else {
            return colors.getDefaultColor();
        }
    }
    @Override
    public boolean onKeyShortcut(int keyCodeKeyEvent event) {
        switch (keyCode) {
        case .:
            if (canSelectText()) {
                return onTextContextMenuItem();
            }
            break;
        case .:
            if (canCut()) {
                return onTextContextMenuItem();
            }
            break;
        case .:
            if (canCopy()) {
                return onTextContextMenuItem();
            }
            break;
        case .:
            if (canPaste()) {
                return onTextContextMenuItem();
            }
            break;
        }
        return super.onKeyShortcut(keyCodeevent);
    }
    private boolean canSelectText() {
        return textCanBeSelected() && .length() != 0;
    }
    private boolean textCanBeSelected() {
        // prepareCursorController() relies on this method.
        // If you change this condition, make sure prepareCursorController is called anywhere
        // the value of this condition might be changed.
        return ( instanceof Spannable &&
                 != null &&
                .canSelectArbitrarily());
    }
    private boolean canCut() {
        if (hasPasswordTransformationMethod()) {
            return false;
        }
        if (.length() > 0 && hasSelection()) {
            if ( instanceof Editable &&  != null) {
                return true;
            }
        }
        return false;
    }
    private boolean canCopy() {
        if (hasPasswordTransformationMethod()) {
            return false;
        }
        if (.length() > 0 && hasSelection()) {
            return true;
        }
        return false;
    }
    private boolean canPaste() {
        return ( instanceof Editable &&
                 != null &&
                getSelectionStart() >= 0 &&
                getSelectionEnd() >= 0 &&
                hasText());
    }

    
Returns the offsets delimiting the 'word' located at position offset.

Parameters:
offset An offset in the text.
Returns:
The offsets for the start and end of the word located at offset. The two ints offsets are packed in a long, with the starting offset shifted by 32 bits. Returns a negative value if no valid word was found.
    private long getWordLimitsAt(int offset) {
        /*
         * Quick return if the input type is one where adding words
         * to the dictionary doesn't make any sense.
         */
        int klass =  & .;
        if (klass == . ||
            klass == . ||
            klass == .) {
            return -1;
        }
        int variation =  & .;
        if (variation == . ||
            variation == . ||
            variation == . ||
            variation == . ||
            variation == .) {
            return -1;
        }
        int len = .length();
        int end = Math.min(offsetlen);
        if (end < 0) {
            return -1;
        }
        int start = end;
        for (; start > 0; start--) {
            char c = .charAt(start - 1);
            int type = Character.getType(c);
            if (c != '\'' &&
                type != . &&
                type != . &&
                type != . &&
                type != . &&
                type != .) {
                break;
            }
        }
        for (; end < len