/*
* 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.text.Layout;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import com.android.internal.util.FastMath;
import com.intel.mpt.annotation.MayloonStubAnnotation;
import android.view.MotionEvent;
import android.view.ViewTreeObserver;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.text.BoringLayout;
import android.text.DynamicLayout;
import android.text.Editable;
import android.text.GetChars;
import android.text.GraphicsOperations;
import android.text.Html;
import android.text.InputFilter;
import android.text.InputType;
import android.text.Selection;
import android.text.SpanWatcher;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.SpannedString;
import android.text.StaticLayout;
import android.text.Styled;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.KeyListener;
import android.text.method.MovementMethod;
import android.text.method.SingleLineTransformationMethod;
import android.text.method.TransformationMethod;
import android.text.style.URLSpan;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewParent;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.EditorInfo;
/**
* 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 {@link EditText} for a subclass that configures the text view
* for editing.
*/
public class TextView extends View {
static final String LOG_TAG = "TextView";
static final boolean DEBUG_EXTRACT = false;
private static int PRIORITY = 100;
private ColorStateList mTextColor;
private int mCurTextColor;
private ColorStateList mHintTextColor;
private ColorStateList mLinkTextColor;
private int mCurHintTextColor;
private boolean mLinksClickable = true;
private int mLines = 0;
private int mContentLines = 0;
private int mTextCharNum = 0;
private int mOneLineChNum = -1;
private int mDefaultWidth = -1;
private boolean mIsChanged = false;
private static int mDefaultSize = 30;
protected int mMaxWidth = Integer.MAX_VALUE;
private int mMaxWidthMode = PIXELS;
protected int mMinWidth = -1;
private int mMinWidthMode = PIXELS;
protected int mScrollX;
protected int mScrollY;
private long mLastScroll;
private Scroller mScroller = null;
private BoringLayout.Metrics mBoring;
private BoringLayout.Metrics mHintBoring;
private static final int LINES = 1;
private static final int EMS = LINES;
private static final int PIXELS = 2;
private int mMaximum = Integer.MAX_VALUE;
private int mMaxMode = LINES;
private int mMinimum = 0;
private int mMinMode = LINES;
final int[] mTempCoords = new int[2];
Rect mTempRect;
private boolean mFreezesText;
private boolean mTemporaryDetach;
private boolean mDispatchTemporaryDetach;
private boolean mShowErrorAfterAttach;
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 = PREDRAW_NOT_REGISTERED;
private Layout mLayout;
private float mShadowRadius, mShadowDx, mShadowDy;
private float mSpacingMult = 1;
private float mSpacingAdd = 0;
private CharWrapper mCharWrapper = null;
private TransformationMethod mTransformation;
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;
int mTextSelectHandleLeftRes;
int mTextSelectHandleRightRes;
int mTextSelectHandleRes;
protected int mInputType = InputType.TYPE_NULL;
private KeyListener mInput;
private boolean mEditable;
// display attributes
private final TextPaint mTextPaint;
private boolean mUserSetTextScaleX;
//private final Paint mHighlightPaint;
private int mHighlightColor = 0xCC475925;
private long mShowCursor;
private Blink mBlink;
private boolean mCursorVisible = true;
private boolean mScrolled = false;
private static final int BLINK = 500;
private static final int ANIMATED_SCROLL_GAP = 250;
private static int clipMoveX = 0;
private static int clipMoveY = 0;
private static int clipWidth = 0;
private static int clipHeight = 0;
// Cursor Controllers. Null when disabled.
private CursorController mInsertionPointCursorController;
private CursorController mSelectionModifierCursorController;
private boolean mInsertionControllerEnabled;
private boolean mSelectionControllerEnabled;
private boolean mInBatchEditControllers;
private boolean mIsInTextSelectionMode = false;
private CharSequence mError;
private boolean mErrorWasChanged;
private ErrorPopup mPopup;
private String mHint;
private Layout mHintLayout;
protected boolean mSingleLine = false;
private boolean mIncludePad = true;
protected int mMaxLength = -1;
private boolean mEatTouchRelease = false;
private ArrayList<TextWatcher> mListeners = null;
Drawable mSelectHandleLeft;
Drawable mSelectHandleRight;
Drawable mSelectHandleCenter;
private Spannable.Factory mSpannableFactory = Spannable.Factory.getInstance();
private MovementMethod mMovement;
class InputContentType {
int imeOptions = EditorInfo.IME_NULL;
String privateImeOptions;
CharSequence imeActionLabel;
int imeActionId;
Bundle extras;
OnEditorActionListener onEditorActionListener;
boolean enterDown;
}
InputContentType mInputContentType;
/**
* 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.
*
* @param v
* The view that was clicked.
* @param actionId
* Identifier of the action. This will be either the
* identifier you supplied, or {@link EditorInfo#IME_NULL
* EditorInfo.IME_NULL} if being called due to the enter key
* being pressed.
* @param event
* If triggered by an enter key, this is the event;
* otherwise, this is null.
* @return Return true if you have consumed the action, else false.
*/
boolean onEditorAction(TextView v, int actionId, KeyEvent event);
}
/**
* 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)
* @param line which line to examine (0..getLineCount() - 1)
* @param bounds Optional. If not null, it returns the extent of the line
* @return the Y-coordinate of the baseline
*/
public int getLineBounds(int line, Rect bounds) {
if (bounds != null) {
bounds.set(0, 0, 0, 0);
}
int baseline = mCurHeight;
// int baseline = mLayout.getLineBounds(line, bounds);
int voffset = getExtendedPaddingTop();
// if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
// voffset += getVerticalOffset(true);
// }
if (bounds != null) {
bounds.offset(getCompoundPaddingLeft(), voffset);
}
return baseline + voffset;
}
public int getLineCount() {
return mLayout != null ? mLayout.getLineCount() : 0;
}
public TextView(Context context) {
this(context, null);
}
// TODO will cause error in j2s?
public TextView(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.textViewStyle);
}
public TextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mText = "";
mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.density = getResources().getDisplayMetrics().density;
TypedArray a =
context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.TextView, 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(
com.android.internal.R.styleable.TextView_textAppearance, -1);
if (ap != -1) {
appearance = context.obtainStyledAttributes(ap,
com.android.internal.R.styleable.TextAppearance);
}
if (appearance != null) {
int n = appearance.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = appearance.getIndex(i);
switch (attr) {
case com.android.internal.R.styleable.TextAppearance_textColorHighlight:
textColorHighlight = appearance.getColor(attr, textColorHighlight);
break;
case com.android.internal.R.styleable.TextAppearance_textColor:
textColor = appearance.getColorStateList(attr);
break;
case com.android.internal.R.styleable.TextAppearance_textColorHint:
textColorHint = appearance.getColorStateList(attr);
break;
case com.android.internal.R.styleable.TextAppearance_textColorLink:
textColorLink = appearance.getColorStateList(attr);
break;
case com.android.internal.R.styleable.TextAppearance_textSize:
textSize = appearance.getDimensionPixelSize(attr, textSize);
break;
case com.android.internal.R.styleable.TextAppearance_typeface:
typefaceIndex = appearance.getInt(attr, -1);
break;
case com.android.internal.R.styleable.TextAppearance_textStyle:
styleIndex = appearance.getInt(attr, -1);
break;
}
}
appearance.recycle();
}
boolean editable = getDefaultEditable();
CharSequence text = "";
int inputType = InputType.TYPE_NULL;
boolean singleLine = false;
int maxlength = -1;
boolean password = false;
int n = a.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
switch (attr) {
case com.android.internal.R.styleable.TextView_editable:
editable = a.getBoolean(attr, editable);
break;
case com.android.internal.R.styleable.TextView_text:
text = a.getText(attr);
break;
case com.android.internal.R.styleable.TextView_textSize:
textSize = a.getDimensionPixelSize(attr, textSize);
break;
case com.android.internal.R.styleable.TextView_textColor:
textColor = a.getColorStateList(attr);
break;
case com.android.internal.R.styleable.TextView_textColorHint:
textColorHint = a.getColorStateList(attr);
break;
case com.android.internal.R.styleable.TextView_textColorLink:
textColorLink = a.getColorStateList(attr);
break;
case com.android.internal.R.styleable.TextView_lines:
setLines(a.getInt(attr, -1));
break;
case com.android.internal.R.styleable.TextView_gravity:
setGravity(a.getInt(attr, -1));
break;
case com.android.internal.R.styleable.TextView_inputType:
inputType = a.getInt(attr, mInputType);
break;
case com.android.internal.R.styleable.TextView_singleLine:
singleLine = a.getBoolean(attr, singleLine);
mSingleLine = singleLine;
break;
case com.android.internal.R.styleable.TextView_maxLength:
maxlength = a.getInt(attr, -1);
mMaxLength = maxlength;
break;
case com.android.internal.R.styleable.TextView_freezesText:
mFreezesText = a.getBoolean(attr, false);
break;
case com.android.internal.R.styleable.TextView_hint:
mHint = (String)a.getText(attr);
break;
case com.android.internal.R.styleable.TextView_width:
setWidth(a.getDimensionPixelSize(attr, -1));
break;
case com.android.internal.R.styleable.TextView_height:
setHeight(a.getDimensionPixelSize(attr, -1));
break;
case com.android.internal.R.styleable.TextView_scrollHorizontally:
if (a.getBoolean(attr, false)) {
setHorizontallyScrolling(true);
}
break;
case com.android.internal.R.styleable.TextView_typeface:
typefaceIndex = a.getInt(attr, typefaceIndex);
break;
case com.android.internal.R.styleable.TextView_textStyle:
styleIndex = a.getInt(attr, styleIndex);
break;
case com.android.internal.R.styleable.TextView_password:
password = a.getBoolean(attr, password);
break;
case com.android.internal.R.styleable.TextView_lineSpacingExtra:
mSpacingAdd = a.getDimensionPixelSize(attr, (int) mSpacingAdd);
break;
case com.android.internal.R.styleable.TextView_lineSpacingMultiplier:
mSpacingMult = a.getFloat(attr, mSpacingMult);
break;
}
}
a.recycle();
if ((inputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION))
== (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD)) {
password = true;
}
mEditable = editable;
setRawTextSize(textSize);
if (inputType != InputType.TYPE_NULL) {
setInputType(inputType);
singleLine = (inputType & (InputType.TYPE_MASK_CLASS | InputType.TYPE_TEXT_FLAG_MULTI_LINE)) != (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
} else if (editable) {
mInputType = InputType.TYPE_CLASS_TEXT;
if (!singleLine) {
mInputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE;
}
}
if (password && (mInputType&EditorInfo.TYPE_MASK_CLASS)
== EditorInfo.TYPE_CLASS_TEXT) {
mInputType = (mInputType & ~(EditorInfo.TYPE_MASK_VARIATION))
| EditorInfo.TYPE_TEXT_VARIATION_PASSWORD;
}
if (singleLine) {
setLines(1);
mSingleLine = true;
}
setTypefaceByIndex(typefaceIndex, styleIndex);
setTextColor(textColor != null ? textColor : ColorStateList
.valueOf(0xFF000000));
setHintTextColor(textColorHint);
setLinkTextColor(textColorLink);
setText(text);
}
private void setTypefaceByIndex(int typefaceIndex, int styleIndex) {
Typeface tf = null;
switch (typefaceIndex) {
case SANS:
tf = Typeface.SANS_SERIF;
break;
case SERIF:
tf = Typeface.SERIF;
break;
case MONOSPACE:
tf = Typeface.MONOSPACE;
break;
}
setTypeface(tf, styleIndex);
}
/**
* @return 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 mTextPaint;
}
/**
* @return the flags on the Paint being used to display the text.
* @see Paint#getFlags
*/
public int getPaintFlags() {
return mTextPaint.getFlags();
}
/**
* Sets flags on the Paint being used to display the text and
* reflows the text if they are different from the old flags.
* @see Paint#setFlags
*/
public void setPaintFlags(int flags) {
if (mTextPaint.getFlags() != flags) {
mTextPaint.setFlags(flags);
nullLayouts();
requestLayout();
invalidate();
}
}
/**
* @return the extent by which text is currently being stretched
* horizontally. This will usually be 1.
*/
public float getTextScaleX() {
return mTextPaint.getTextScaleX();
}
/**
* @return the size (in pixels) of the default text size in this TextView.
*/
public float getTextSize() {
return mTextPaint.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.
*
* @param size The scaled pixel size.
*
* @attr ref android.R.styleable#TextView_textSize
*/
public void setTextSize(float size) {
setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
}
/**
* Set the default text size to a given unit and value. See {@link
* TypedValue} for the possible dimension units.
*
* @param unit The desired dimension unit.
* @param size The desired size in the given units.
*
* @attr ref android.R.styleable#TextView_textSize
*/
public void setTextSize(int unit, float size) {
Context c = getContext();
Resources r;
if (c == null)
r = Resources.getSystem();
else
r = c.getResources();
setRawTextSize(TypedValue.applyDimension(unit, size,
r.getDisplayMetrics()));
}
/**
* @return the current typeface and style in which the text is being
* displayed.
*/
public Typeface getTypeface() {
return mTextPaint.getTypeface();
}
private void setRawTextSize(float size) {
if (size != mTextPaint.getTextSize()) {
mTextPaint.setTextSize(size);
nullLayouts();
requestLayout();
invalidate();
}
}
/**
* Sets the extent by which text should be stretched horizontally.
*
* @attr ref android.R.styleable#TextView_textScaleX
*/
public void setTextScaleX(float size) {
if (size != mTextPaint.getTextScaleX()) {
mTextPaint.setTextScaleX(size);
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
* {@link #setTypeface(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 (mTextPaint.getTypeface() != tf) {
mTextPaint.setTypeface(tf);
nullLayouts();
requestLayout();
invalidate();
}
}
/**
* 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 tf, int style) {
if (style > 0) {
if (tf == null) {
tf = Typeface.defaultFromStyle(style);
} else {
tf = Typeface.create(tf, style);
}
setTypeface(tf);
// now compute what (if any) algorithmic styling is needed
int typefaceStyle = tf != null ? tf.getStyle() : 0;
int need = style & ~typefaceStyle;
mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
} else {
mTextPaint.setFakeBoldText(false);
mTextPaint.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;
}
public void setTextColor(int color) {
mTextColor = 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();
}
mTextColor = colors;
updateTextColors();
}
/**
* 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 context, TypedArray attrs) {
ColorStateList colors;
colors = attrs.getColorStateList(com.android.internal.R.styleable.
TextView_textColor);
if (colors == null) {
int ap = attrs.getResourceId(com.android.internal.R.styleable.
TextView_textAppearance, -1);
if (ap != -1) {
TypedArray appearance;
appearance = context.obtainStyledAttributes(ap,
com.android.internal.R.styleable.TextAppearance);
colors = appearance.getColorStateList(com.android.internal.R.styleable.
TextAppearance_textColor);
appearance.recycle();
}
}
return colors;
}
private void updateTextColors() {
boolean inval = false;
int color = mTextColor.getColorForState(getDrawableState(), 0);
if (color != mCurTextColor) {
mCurTextColor = color;
inval = true;
}
if (mLinkTextColor != null) {
color = mLinkTextColor.getColorForState(getDrawableState(), 0);
if (color != mTextPaint.linkColor) {
mTextPaint.linkColor = color;
inval = true;
}
}
if (mHintTextColor != null) {
color = mHintTextColor.getColorForState(getDrawableState(), 0);
if (color != mCurHintTextColor && mText.length() == 0) {
mCurHintTextColor = color;
inval = true;
}
}
if (inval) {
invalidate();
}
}
public void setWidth(int pixels) {
// System.out.println(TAG + "setWidth, pixels: " + pixels);
mMaxWidth = mMinWidth = pixels;
requestLayout();
invalidate();
}
public void setLineSpacing(float add, float mult) {
mSpacingMult = mult;
mSpacingAdd = add;
if (mLayout != null) {
nullLayouts();
requestLayout();
invalidate();
}
}
public void setHeight(int pixels) {
mMaximum = mMinimum = pixels;
mMaxMode = mMinMode = PIXELS;
requestLayout();
invalidate();
}
/** Mayloon workaround
* we need to setVisibility for our only DOM textarea except canvas
*/
@Override
public void setVisibility(int visibility) {
super.setVisibility(visibility);
String visible = "visible";
if (visibility != VISIBLE)
visible = "hidden";
int thisViewId = getUIElementID();
/**
* @j2sNative
* var thisView = document.getElementById(thisViewId);
* if (thisView != null) thisView.style.visibility = visible;
*/{}
}
class Drawables {
final Rect mCompoundRect = new Rect();
Drawable mDrawableTop, mDrawableBottom, mDrawableLeft, mDrawableRight;
int mDrawableSizeTop, mDrawableSizeBottom, mDrawableSizeLeft, mDrawableSizeRight;
int mDrawableWidthTop, mDrawableWidthBottom, mDrawableHeightLeft, mDrawableHeightRight;
int mDrawablePadding;
}
private Drawables mDrawables;
public void setCompoundDrawables(Drawable left, Drawable top,
Drawable right, Drawable bottom) {
Drawables dr = mDrawables;
final boolean drawables = left != null || top != null || right != null
|| bottom != null;
if (!drawables) {
if (dr != null) {
if (dr.mDrawablePadding == 0) {
mDrawables = 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 != null)
dr.mDrawableLeft.setCallback(null);
dr.mDrawableLeft = null;
if (dr.mDrawableTop != null)
dr.mDrawableTop.setCallback(null);
dr.mDrawableTop = null;
if (dr.mDrawableRight != null)
dr.mDrawableRight.setCallback(null);
dr.mDrawableRight = null;
if (dr.mDrawableBottom != null)
dr.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) {
mDrawables = 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;
}
}
requestLayout();
invalidate();
}
public void setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top,
Drawable right, Drawable bottom) {
if (left != null) {
System.out.println("left is not null");
left.setBounds(0, 0, left.getIntrinsicWidth(), left.getIntrinsicHeight());
}
if (right != null) {
System.out.println("right is not null");
right.setBounds(0, 0, right.getIntrinsicWidth(), right.getIntrinsicHeight());
}
if (top != null) {
System.out.println("top is not null");
top.setBounds(0, 0, top.getIntrinsicWidth(), top.getIntrinsicHeight());
}
if (bottom != null) {
System.out.println("bottom is not null");
bottom.setBounds(0, 0, bottom.getIntrinsicWidth(), bottom.getIntrinsicHeight());
}
setCompoundDrawables(left, top, right, bottom);
}
public void setInputType(int type) {
boolean isPassword = type == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
boolean multiLine = (type&(EditorInfo.TYPE_MASK_CLASS
| EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE)) ==
(EditorInfo.TYPE_CLASS_TEXT
| EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE);
// We need to update the single line mode if it has changed or we
// were previously in password mode.
if (mSingleLine == multiLine) {
// Change single line mode, but only change the transformation if
// we are not in password mode.
applySingleLine(!multiLine, !isPassword);
}
mInputType = type;
/**
* @j2sNative
* var thisView = document.getElementById(this.getUIElementID());
* if(!thisView) return;
* if(isPassword) {
* thisView.type="password";
* }
* else {
* thisView.type="text";
* }
*/{}
}
public void setLines(int lines) {
mLines = mMaximum = mMinimum = lines;
mMaxMode = mMinMode = LINES;
requestLayout();
invalidate();
}
/**
* 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 mText;
}
/**
* Returns the length, in characters, of the text managed by this TextView
*/
public int length() {
return mText.toString().length();
}
/**
* Returns the top padding of the view, plus space for the top Drawable if
* any.
*/
public int getCompoundPaddingTop() {
final Drawables dr = mDrawables;
if (dr == null || dr.mDrawableTop == null) {
return mPaddingTop;
} else {
return mPaddingTop + 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 = mDrawables;
if (dr == null || dr.mDrawableBottom == null) {
return mPaddingBottom;
} else {
return mPaddingBottom + 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 = mDrawables;
if (dr == null || dr.mDrawableLeft == null) {
return mPaddingLeft;
} else {
return mPaddingLeft + 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 = mDrawables;
if (dr == null || dr.mDrawableRight == null) {
return mPaddingRight;
} else {
return mPaddingRight + 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 (mMaxMode != LINES) {
return getCompoundPaddingTop();
}
// if (mLayout.getLineCount() <= mMaximum) {
if (this.getLineCount() <= mMaximum) {
return getCompoundPaddingTop();
}
int top = getCompoundPaddingTop();
int bottom = getCompoundPaddingBottom();
int viewht = getHeight() - top - bottom;
int layoutht = mLayout.getLineTop(mMaximum);
if (layoutht >= viewht) {
return top;
}
final int gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
if (gravity == Gravity.TOP) {
return top;
} else if (gravity == Gravity.BOTTOM) {
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 (mMaxMode != LINES) {
return getCompoundPaddingBottom();
}
//if (mLayout.getLineCount() <= mMaximum) {
if (this.getLineCount() <= mMaximum) {
return getCompoundPaddingBottom();
}
int top = getCompoundPaddingTop();
int bottom = getCompoundPaddingBottom();
int viewht = getHeight() - top - bottom;
int layoutht = mLayout.getLineTop(mMaximum);
if (layoutht >= viewht) {
return bottom;
}
final int gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
if (gravity == Gravity.TOP) {
return bottom + viewht - layoutht;
} else if (gravity == Gravity.BOTTOM) {
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();
}
/**
* 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();
}
@Override
public void setPadding(int left, int top, int right, int bottom) {
if (left != mPaddingLeft ||
right != mPaddingRight ||
top != mPaddingTop ||
bottom != mPaddingBottom) {
nullLayouts();
}
// the super call will requestLayout()
super.setPadding(left, top, right, bottom);
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.toString().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 text, int start, int end) {
// if(this.mSingleLine&&(text.equals("\r\n")||text.equals("\n"))){
// return;
// }
//replace line feed character "\n" in java by "<br/>" in javascript
int cnt = 0;
for (int i = start; i <= end; i++) {
if (text.charAt(i) == '\n') cnt++;
}
CharSequence prefix = text.subSequence(0, start - 1);
CharSequence strTmp = text.subSequence(start, end);
/**
* @j2sNative strTmp = strTmp.replace("\n", "<br/>");
*/{}
text = prefix.toString() + strTmp.toString();
end += 4 * cnt;
if (!(mText instanceof Editable)) {
setText(mText, BufferType.EDITABLE);
}
((Editable) mText).append(text, start, end);
}
/**
* 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.
*
* @param 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) {
mFreezesText = freezesText;
}
/**
* Return whether this text view is including its entire text contents in
* frozen icicles.
*
* @return Returns true if text is included, false if it isn't.
*
* @see #setFreezesText
*/
public boolean getFreezesText() {
return mFreezesText;
}
/**
* Sets the Factory used to create new Editables.
*/
public void setEditableFactory(Editable.Factory factory) {
mEditableFactory = factory;
setText(mText);
}
/**
* Like {@link #setText(CharSequence)}, except that the cursor position (if
* any) is retained in the new text.
*
* @param text The new text to place in the text view.
* @see #setText(CharSequence)
*/
public final void setTextKeepState(CharSequence text) {
setTextKeepState(text, mBufferType);
}
/**
* Like {@link #setText(CharSequence, android.widget.TextView.BufferType)},
* except that the cursor position (if any) is retained in the new text.
*
* @see #setText(CharSequence, android.widget.TextView.BufferType)
*/
public final void setTextKeepState(CharSequence text, BufferType type) {
int start = getSelectionStart();
int end = getSelectionEnd();
int len = text.length();
setText(text, type);
if (start >= 0 || end >= 0) {
if (mText instanceof Spannable) {
Selection.setSelection((Spannable) mText,
Math.max(0, Math.min(start, len)),
Math.max(0, Math.min(end, len)));
}
}
}
public final void setText(int resid) {
CharSequence text = getContext().getResources().getText(resid);
if (text == null) {
this.setText("");
} else {
this.setText(text);
}
}
public final void setText(int resid, BufferType type) {
CharSequence text = getContext().getResources().getText(resid);
if (text == null) {
this.setText("", type);
} else {
this.setText(text, type);
}
}
/**
* Sets the string value of the TextView. TextView <em>does not</em> 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
* {@link android.text.SpannableString SpannableString}, or see the <a
* href="{@docRoot}
* guide/topics/resources/available-resources.html#stringresources">
* Available Resource Types</a> 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, mBufferType);
}
public void setText(CharSequence text, BufferType type) {
setText(text, type, true, 0);
}
/**
* 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[] text, int start, int 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 (mText != null) {
oldlen = mText.length();
// sendBeforeTextChanged(mText, 0, oldlen, len);
}
// else {
// sendBeforeTextChanged("", 0, 0, len);
// }
if (mCharWrapper == null) {
mCharWrapper = new CharWrapper(text, start, len);
} else {
mCharWrapper.set(text, start, len);
}
setText(mCharWrapper, mBufferType, false, oldlen);
}
/**
* 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) {
//mHint = TextUtils.stringOrSpannedString(hint);
mHint = (String) hint;
if (mLayout != null) {
// TODO: checkForRelayout();
}
if (mText.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));
}
/**
* 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) {
// mHorizontallyScrolling = whether;
if (whether) {
setLines(1);
mSingleLine = true;
}
// if (mLayout != null) {
nullLayouts();
requestLayout();
invalidate();
// }
}
/**
* 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 mHint;
}
@Override
protected boolean isPaddingOffsetRequired() {
return mShadowRadius != 0;
}
@Override
protected int getLeftPaddingOffset() {
return getCompoundPaddingLeft() - mPaddingLeft
+ (int) Math.min(0, mShadowDx - mShadowRadius);
}
@Override
protected int getTopPaddingOffset() {
return (int) Math.min(0, mShadowDy - mShadowRadius);
}
@Override
protected int getBottomPaddingOffset() {
return (int) Math.max(0, mShadowDy + mShadowRadius);
}
@Override
protected int getRightPaddingOffset() {
return -(getCompoundPaddingRight() - mPaddingRight)
+ (int) Math.max(0, mShadowDx + mShadowRadius);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width = 0;
int height = 0;
BoringLayout.Metrics boring = UNKNOWN_BORING;
BoringLayout.Metrics hintBoring = UNKNOWN_BORING;
int des = -1;
boolean fromexisting = false;
if (widthMode == MeasureSpec.EXACTLY) {
width = (int) FloatMath.ceil(Layout.getDesiredWidth(this.mText, mTextPaint));
if (width > widthSize)
mOneLineChNum = (int) Math.floor(mTextCharNum * (float) widthSize / width);
width = widthSize;
} else {
if (mLayout != null && mEllipsize == null) {
des = desired(mLayout);
}
if (des < 0) {
boring = BoringLayout.isBoring(mTransformed, mTextPaint, mBoring);
if (boring != null) {
// The textarea or input is larger than div 2 pixels by each side that in the same font.
if (mEditable) {
boring.width = boring.width + 4;
boring.bottom = boring.bottom + 4;
}
mBoring = boring;
}
} else {
fromexisting = true;
}
if (boring == null || boring == UNKNOWN_BORING) {
if (des < 0) {
des = (int) FloatMath.ceil(Layout.getDesiredWidth(mTransformed, mTextPaint));
}
width = des;
} else {
width = boring.width;
}
final Drawables dr = mDrawables;
int dWidth = 0;
if (dr != null) {
dWidth = Math.max(width, dr.mDrawableWidthTop);
dWidth = Math.max(width, dr.mDrawableWidthBottom);
}
width += dWidth;
width += getCompoundPaddingLeft() + getCompoundPaddingRight();
if (width > mMaxWidth) {
mOneLineChNum = (int) Math.floor(mTextCharNum * (float) mMaxWidth / width);
width = mMaxWidth;
}
// Check against our minimum width
int minWidth = Math.max(mMinWidth, getSuggestedMinimumWidth());
if (width < minWidth) {
mOneLineChNum = (int) Math.floor(mTextCharNum * (float) minWidth / width);
width = minWidth;
}
if (widthMode == MeasureSpec.AT_MOST) {
if (width > widthSize) {
mOneLineChNum = (int) Math.floor(mTextCharNum * (float) widthSize / width);
width = widthSize;
}
}
}
int want = width - getCompoundPaddingLeft() - getCompoundPaddingRight();
int unpaddedWidth = want;
int hintWant = want;
int hintWidth = mHintLayout == null ? hintWant : mHintLayout.getWidth();
if (mLayout == null) {
makeNewLayout(want, hintWant, boring, hintBoring,
width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false);
} else if ((mLayout.getWidth() != want) || (hintWidth != hintWant)) {
if (mHint == null && mEllipsize == null && want > mLayout.getWidth() && (mLayout instanceof BoringLayout || (fromexisting && des >= 0 && des <= want))) {
mLayout.increaseWidthTo(want);
} else {
makeNewLayout(want, hintWant, boring, hintBoring, width - getCompoundPaddingLeft() - getCompoundPaddingRight(), false);
}
} else {
// Width has not changed.
}
mCurWidth = width;
int desired = getDesiredHeight(this.mText, true);
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
height = desired;
if (heightMode == MeasureSpec.AT_MOST) {
height = Math.min(desired, heightSize);
}
}
// Mayloon : When size of text in INPUT tag is close to the height of INPUT tag,
// it will cause shaking after inputing something or changing text in INPUT
// so that we cannot see the complete text in INPUT.
// this method just make sure we can see the complete text,
// we still need to figure out why INPUT is shaking
int pad = getCompoundPaddingTop() + getCompoundPaddingBottom();
boolean textarea = true;
if (mSingleLine && this instanceof EditText)
textarea = false;
if (!textarea && mTextPaint.getTextSize() * 0.9 >= height - pad)
height = (int) (mTextPaint.getTextSize() * 0.9 + pad);
mCurHeight = height;
setMeasuredDimension(width, height);
}
private int mCurWidth = 0;
private int mCurHeight = 0;
private float mOneLineHeight = -1;
public int getDesiredHeight(CharSequence text, boolean cap) {
if (mLayout == null) {
return 0;
}
int linecount = mLayout.getLineCount();
int pad = getCompoundPaddingTop() + getCompoundPaddingBottom();
int desired = mLayout.getLineTop(linecount);
final Drawables dr = mDrawables;
if (dr != null) {
desired = Math.max(desired, dr.mDrawableHeightLeft);
desired = Math.max(desired, dr.mDrawableHeightRight);
}
desired += pad;
if (mMaxMode == LINES) {
/*
* 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 > mMaximum) {
// TODO: Handle multiple lines
desired = mLayout.getLineTop(mMaximum);
// layout.getBottomPadding();
if (dr != null) {
desired = Math.max(desired, dr.mDrawableHeightLeft);
desired = Math.max(desired, dr.mDrawableHeightRight);
}
desired += pad;
linecount = mMaximum;
}
}
} else {
desired = Math.min(desired, mMaximum);
}
if (mMinMode == LINES) {
if (linecount < mMinimum) {
desired += getLineHeight() * (mMinimum - linecount);
}
} else {
desired = Math.max(desired, mMinimum);
}
// Check against our minimum height
desired = Math.max(desired, getSuggestedMinimumHeight());
return desired;
}
protected CharSequence mText = null;
private CharSequence mTransformed = null;
private ChangeWatcher mChangeWatcher;
protected void onTextChanged(CharSequence text, int start, int before, int after) {
if (before != 0 && after == 0) {
mIsChanged = true;
}
mTextCharNum = 0;
for (int i = 0; i < text.toString().length(); i++) {
char c = text.charAt(i);
if (c >= 0x0001 && c <= 0x00ff)
mTextCharNum++;
else
mTextCharNum += 2;
}
int thisViewId = getUIElementID();
int width = (int) FloatMath.ceil(Layout.getDesiredWidth(text, mTextPaint));
int height = getDesiredHeight(text, true);
// System.out.println(thisViewId
// + " onTextChanged>>>this.mMeasuredWidth: "
// + this.mMeasuredWidth + ", getDesiredWidth: " + width);
if ((mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.RIGHT) {
/**
* @j2sNative
* var thisView = document.getElementById(thisViewId);
* if (null != thisView) {
* thisView.style.textAlign = "right";
* }
*/{}
}
if ((mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.CENTER_HORIZONTAL) {
/**
* @j2sNative
* var thisView = document.getElementById(thisViewId);
* if (null != thisView) {
* thisView.style.textAlign = "center";
* }
*/{}
}
if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.CENTER_VERTICAL) {
/**
@j2sNative
// myself = document.getElementById(this.mUIElementID + "_text");
// if (myself != null) {
// // vertical
// if (this.mTextSize > 0) {
// // myself.style.fontSize = this.mTextSize + "px";
// padding = this.mMeasuredHeight - this.mTextSize;
// if (padding % 2 != 0)
// padding = padding + 1;
// padding = padding / 2;
// if (padding > 0) {
// myself.childNodes[0].style.paddingTop = padding + "px";
// myself.childNodes[0].style.paddingBottom = padding + "px";
// }
// }
// }
*/{}
}
}
/**
* Adds a TextWatcher to the list of those whose methods are called
* whenever this TextView's text changes.
* <p>
* In 1.0, the {@link TextWatcher#afterTextChanged} method was erroneously
* not called after {@link #setText} calls. Now, doing {@link #setText}
* 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 (mListeners == null) {
mListeners = new ArrayList<TextWatcher>();
}
mListeners.add(watcher);
}
private void setText(CharSequence text, BufferType type,
boolean notifyBefore, int oldlen) {
if (text == null) {
text = "";
}
if (!mUserSetTextScaleX) mTextPaint.setTextScaleX(1.0f);
if (text instanceof Spanned &&
((Spanned) text).getSpanStart(TextUtils.TruncateAt.MARQUEE) >= 0) {
setHorizontalFadingEdgeEnabled(true);
setEllipsize(TextUtils.TruncateAt.MARQUEE);
}
int n = mFilters.length;
for (int i = 0; i < n; i++) {
CharSequence out = mFilters[i].filter(text, 0, text.length(),
EMPTY_SPANNED, 0, 0);
if (out != null) {
text = out;
}
}
if (notifyBefore) {
if (mText != null) {
oldlen = mText.toString().length();
sendBeforeTextChanged(mText, 0, oldlen, text.length());
} else {
sendBeforeTextChanged("", 0, 0, text.length());
}
}
boolean needEditableForNotification = false;
if (mListeners != null && mListeners.size() != 0) {
needEditableForNotification = true;
}
if (type == BufferType.EDITABLE) {
Editable t = mEditableFactory.newEditable(text);
text = t;
} else if (type == BufferType.SPANNABLE) {
text = mSpannableFactory.newSpannable(text);
} else if (!(text instanceof CharWrapper)) {
text = TextUtils.stringOrSpannedString(text);
}
mBufferType = type;
mText = text;
if (mTransformation == null)
mTransformed = text;
else
mTransformed = mTransformation.getTransformation(text, this);
final int textLength = text.toString().length();
if (text instanceof Spannable) {
Spannable sp = (Spannable) text;
// Remove any ChangeWatchers that might have come
// from other TextViews.
final ChangeWatcher[] watchers = sp.getSpans(0, sp.toString()
.length(), ChangeWatcher.class);
final int count = watchers.length;
for (int i = 0; i < count; i++)
sp.removeSpan(watchers[i]);
if (mChangeWatcher == null)
mChangeWatcher = new ChangeWatcher();
sp.setSpan(mChangeWatcher, 0, textLength,
Spanned.SPAN_INCLUSIVE_INCLUSIVE
| (PRIORITY << Spanned.SPAN_PRIORITY_SHIFT));
}
if (mLayout != null) {
checkForRelayout();
}
// sendOnTextChanged(text, 0, oldlen, textLength);
onTextChanged(text, 0, oldlen, textLength);
// if (needEditableForNotification) {
// sendAfterTextChanged((Editable) text);
// }
// SelectionModifierCursorController depends on textCanBeSelected, which depends on text
prepareCursorControllers();
handleTextChanged(text, 0, oldlen, textLength);
requestLayout(); // necessary if size of textarea changed
invalidate();
}
// @Override
// protected void setMeasuredDimension(int measuredWidth, int measuredHeight) {
// this.basicSetDimension(measuredWidth, measuredHeight);
//
// /**
// @j2sNative
// thisView = document.getElementById(this.mUIElementID);
// if (thisView.childNodes[0] != null) {
// if (this.mMinWidth > 0) {
// contentWidth = this.mMinWidth;
// if (this.mMeasuredWidth < this.mMinWidth)
// contentWidth = this.mMeasuredWidth;
// thisView.childNodes[0].style.width = contentWidth + "px";
// } else {
// thisView.childNodes[0].style.width = this.mMeasuredWidth + "px";
// }
// thisView.childNodes[0].style.height = this.mMeasuredHeight + "px";
// }
// */
// {
// }
// }
public static class BufferType {
private BufferType(int iValue) {
mValue = iValue;
}
public static BufferType NORMAL = new BufferType(0);
public static BufferType SPANNABLE = new BufferType(1);
public static BufferType EDITABLE = new BufferType(2);
private final int mValue;
}
private BufferType mBufferType = BufferType.NORMAL;
private Editable.Factory mEditableFactory = Editable.Factory.getInstance();
private class ChangeWatcher implements TextWatcher, SpanWatcher {
private CharSequence mBeforeText;
public void beforeTextChanged(CharSequence buffer, int start,
int before, int after) {
mBeforeText = buffer.toString();
}
public void onTextChanged(CharSequence buffer, int start, int before,
int after) {
if (DEBUG_EXTRACT)
Log.v(LOG_TAG, "onTextChanged start=" + start + " before="
+ before + " after=" + after + ": " + buffer);
TextView.this.handleTextChanged(buffer, start, before, after);
mBeforeText = null;
}
public void afterTextChanged(Editable buffer) {
}
public void onSpanChanged(Spannable buf, Object what, int s, int e,
int st, int en) {
}
public void onSpanAdded(Spannable buf, Object what, int s, int e) {
}
public void onSpanRemoved(Spannable buf, Object what, int s, int e) {
}
}
public void handleTextChanged(CharSequence buffer, int start, int before,
int after) {
/**
* @j2sNative
* var thisText = document.getElementById(this.getUIElementID());
* if (thisText != null) {
* if (thisText.tagName == "DIV") {
* thisText.innerHTML = this.mText.toString();
* } else {
* thisText.value = this.mText.toString();
* }
* // For bug 848, scrollLeft when text changed
* if (this.mSingleLine) {
* if (thisText.style.textAlign == "right") {
* thisText.scrollLeft = thisText.scrollWidth - thisText.clientWidth
* + this.getCompoundPaddingRight() + this.getCompoundPaddingLeft();
* }
* }
* }
*/{}
onTextChanged(buffer, start, before, after);
}
protected void onDraw(Canvas canvas) {
// 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 = mScrollX;
final int scrollY = mScrollY;
final int right = mRight;
final int left = mLeft;
final int bottom = mBottom;
final int top = mTop;
final Drawables dr = mDrawables;
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 + mPaddingLeft,
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 - mPaddingRight - 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();
float tempX=scrollX + compoundPaddingLeft + (hspace - dr.mDrawableWidthTop)/2;
float tempY=scrollY + mPaddingTop;
System.out.println("canvas translate X:"+tempX);
System.out.println("canvas translate Y:"+tempY);
canvas.translate(scrollX + compoundPaddingLeft + (hspace - dr.mDrawableWidthTop) / 2,
scrollY + mPaddingTop);
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 - mPaddingBottom - dr.mDrawableSizeBottom);
dr.mDrawableBottom.draw(canvas);
canvas.restore();
}
}
// draw the text element
drawText();
}
private int getTextHeight() {
int cNum = mOneLineChNum > 0 ? mOneLineChNum : mTextCharNum;
boolean textarea = true;
String temp = "";
Object text = null;
if (mSingleLine && this instanceof EditText)
textarea = false;
/**
* @j2sNative
* text = document.getElementById(this.getUIElementID());
*/{}
if (text != null) {
if (textarea) {
if (mEditable) {
/**
* @j2sNative
* temp = text.value;
*/{}
return getTextareaParams(temp, cNum, 1, 2);
} else {
/**
* @j2sNative
* temp = text.innerHTML;
*/{}
return getDivParams(temp, mSingleLine, 2);
}
} else {
/**
* @j2sNative
* temp = text.value;
*/{}
return getInputParams(temp, 2);
}
}
return 0;
}
/////////////////////////////////////////////////////////////////////////
private int getVerticalOffset(boolean forceNormal) {
int voffset = 0;
final int gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
Layout l = mLayout;
// if (!forceNormal && mText.length() == 0 && mHintLayout != null) {
// l = mHintLayout;
// }
if (gravity != Gravity.TOP) {
int boxht;
/* if (l == mHintLayout) {
boxht = getMeasuredHeight() - getCompoundPaddingTop() -
getCompoundPaddingBottom();
} else*/ {
boxht = getMeasuredHeight() - getExtendedPaddingTop() - getExtendedPaddingBottom();
}
int textht = 0;
if (l != null) {
textht = l.getHeight();
}
if (textht < boxht) {
if (gravity == Gravity.BOTTOM)
voffset = boxht - textht;
else // (gravity == Gravity.CENTER_VERTICAL)
voffset = (boxht - textht) >> 1;
}
}
return voffset;
}
public void setTextAppearance(Context context, int resid) {
TypedArray appearance =
context.obtainStyledAttributes(resid,
com.android.internal.R.styleable.TextAppearance);
int color;
ColorStateList colors;
int ts;
// color = appearance.getColor(com.android.internal.R.styleable.TextAppearance_textColorHighlight, 0);
// if (color != 0) {
// setHighlightColor(color);
// }
colors = appearance.getColorStateList(com.android.internal.R.styleable.
TextAppearance_textColor);
if (colors != null) {
setTextColor(colors);
}
ts = appearance.getDimensionPixelSize(com.android.internal.R.styleable.
TextAppearance_textSize, 0);
if (ts != 0) {
setRawTextSize(ts);
}
// colors = appearance.getColorStateList(com.android.internal.R.styleable.TextAppearance_textColorHint);
// if (colors != null) {
// setHintTextColor(colors);
// }
colors = appearance.getColorStateList(com.android.internal.R.styleable.TextAppearance_textColorLink);
if (colors != null) {
setLinkTextColor(colors);
}
int typefaceIndex, styleIndex;
typefaceIndex = appearance.getInt(com.android.internal.R.styleable.
TextAppearance_typeface, -1);
styleIndex = appearance.getInt(com.android.internal.R.styleable.
TextAppearance_textStyle, -1);
setTypefaceByIndex(typefaceIndex, styleIndex);
appearance.recycle();
}
/**
* @j2sKeep
*/
private void setTextAreaAlignment() {
// From spec, text alignment only take effect when text size is smaller than View block
int voffsetText = 0;
if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
voffsetText = getVerticalOffset(false);
}
if ((mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.LEFT) {
CharSequence gravity = "center";
if ((mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.RIGHT) {
gravity = "right";
}
/**
* @j2sNative
* text = document.getElementById(this.getUIElementID());
* text.style.textAlign = gravity;
*/{}
}
int x = getCompoundPaddingLeft();
int y = getExtendedPaddingTop() + voffsetText;
int paddingRight = getCompoundPaddingRight();
int paddingBottom = getExtendedPaddingBottom();
/**
* @j2sNative
* text = document.getElementById(this.getUIElementID());
* if (x > 0)
* text.style.paddingLeft = x + "px";
* if (y > 0)
* text.style.paddingTop = y + "px";
* if (paddingRight > 0)
* text.style.paddingRight = paddingRight + "px";
* if (paddingBottom > 0)
* text.style.paddingBottom = paddingBottom + "px";
* text.style.height = this.mMeasuredHeight - y - paddingBottom + "px";
* text.style.width = this.mMeasuredWidth - x - paddingRight + "px";
*/{}
}
/**
* @j2sKeep
*/
private void setFontProperties(String id) {
Typeface tf = mTextPaint.getTypeface();
String color = Color.toString(this.mCurTextColor);
/**
* @j2sNative
* var thisText = document.getElementById(id);
* // set font style
* if (tf != null) {
* switch (tf.getStyle()) {
* case 0:
* thisText.style.fontStyle = "normal";
* break;
* case 1:
* thisText.style.fontWeight = "bold";
* break;
* case 2:
* thisText.style.fontStyle = "italic";
* break;
* case 3:
* thisText.style.fontWeight = "bold";
* thisText.style.fontStyle = "italic";
* break;
* default:
* thisText.style.fontWeight = "bold";
* thisText.style.fontStyle = "italic";
* break;
* }
* // set font family
* if (tf.getFamilyName() == "sans-serif" || tf.getFamilyName() == "serif" || tf.getFamilyName() == "monospace") {
* thisText.style.fontFamily = tf.getFamilyName();
* } else if (tf.getFamilyName()) {
* thisText.style.fontFamily = "sans-serif";
* // console.loge("We don't support this font: " + tf.getFamilyName());
* }
* } else {
* // if tf is null thus set font default
* thisText.style.fontWeight = "normal";
* thisText.style.fontStyle = "normal";
* thisText.style.fontFamily = "serif";
* }
* // set font color and size
* thisText.style.fontSize = this.mTextPaint.getTextSize() + "px";
* thisText.style.color = color;
*/{}
}
private void drawText() {
int cNum = mOneLineChNum > 0 ? mOneLineChNum : mTextCharNum;
final int typeNull = InputType.TYPE_NULL;
String mTemp;
if (mText instanceof SpannedString) {
mTemp = Html.toHtml((SpannedString) mText);
} else {
mTemp = mText.toString();
}
String repText = mTemp;
if (mSingleLine) {
repText = repText.replaceAll("<br/>", " ");
}
/**
* @j2sNative
* var thisText = document.getElementById(this.getUIElementID());
* if (thisText == null || thisText.tagName == "SPAN" || thisText.tagName == "DIV") {
* if (this.mEditable) {
* var text = document.createElement("textarea");
* text.value = this.mText.toString();
* } else {
* var text = document.createElement("div");
* if (repText.length > cNum) {
* text.style.wordWrap = "break-word";
* }
* if (repText.isHtml) {
* text.innerHTML = repText;
* } else {
* text.innerText = repText;
* }
* }
* if (this.mSingleLine) {
* text.style.whiteSpace = "nowrap";
* } else {
* text.style.whiteSpace = "pre-wrap";
* }
* text.style.textAlign = "left";
* text.style.background = "transparent";
* text.style.overflow = "hidden";
* text.style.border = "none";
* text.style.resize = "none";
* text.style.position = "absolute";
* text.style.outline = "none";
* text.style.cursor = "default";
* text.style.padding = "0px";
* text.style.margin = "0px";
* text.id = this.getUIElementID();
* var parentId = this.getParent().getUIElementID();
* var curView = document.getElementById(parentId);
* if (thisText == null) {
* var viewRootId = this.getRootView().getParent().getViewRootID();
* var viewRoot = document.getElementById(viewRootId);
* if( curView != null) {
* curView.appendChild(text);
* } else {
* viewRoot.childNodes[1].appendChild(text); // append to decorview
* }
* } else {
* thisText.parentNode.replaceChild(text, thisText);
* text.style.left = thisText.style.left;
* text.style.top = thisText.style.top;
* text.style.right = thisText.style.right;
* text.style.bottom = thisText.style.bottom;
* }
* thisText = text;
* }
* if (!android.util.DebugUtils.DEBUG_VIEW_IN_BROWSER) {
* thisText.style.left = this.getAbsoluteLeft() + "px";
* thisText.style.top = this.getAbsoluteTop() + "px";
* thisText.style.right = this.getAbsoluteLeft() + this.getWidth() + "px";
* thisText.style.bottom = this.getAbsoluteTop() + this.getHeight() + "px";
* if (this.mAttachInfo != null && this.mAttachInfo.getScrollContainers() != null) {
* var length = this.mAttachInfo.getScrollContainers().size();
* var scrollContainers = this.mAttachInfo.getScrollContainers();
* for (var i = 0; i < length; i++) {
* if (thisText != null && thisText.tagName == "DIV" && (thisText.id == scrollContainers.get(i).getUIElementID() + 1 || parentId == scrollContainers.get(i).getUIElementID() + 1)) {
* this.clipHeight = scrollContainers.get(i).getHeight();
* this.clipWidth = scrollContainers.get(i).getWidth();
* this.clipMoveY = scrollContainers.get(i).getScrollY();
* this.clipMoveX = scrollContainers.get(i).getScrollX();
* var clipTop = this.clipMoveY + "px,";
* var clipRight = this.clipMoveX + this.getWidth() + "px,";
* var clipBottom = this.clipMoveY + this.clipHeight +"px,";
* var clipLeft = this.clipMoveX + "px";
* if(thisText.id == scrollContainers.get(i).getUIElementID() + 1) {
* thisText.style.clip = "rect(" + clipTop + clipRight + clipBottom + clipLeft + ")";
* } else if (parentId == scrollContainers.get(i).getUIElementID() + 1 && curView != null) {
* curView.style.clip = "rect(0px," + this.clipWidth+ "px," + this.clipHeight + "px,0px)";
* }
* break;
* }
* }
* }
* }
* thisText.placeholder = this.mHint;
* this.setFontProperties(thisText.id);
*/{}
setTextAreaAlignment();
}
/**
* Convenience for {@link Selection#getSelectionStart}.
*/
public int getSelectionStart() {
return Selection.getSelectionStart(getText());
}
/**
* Convenience for {@link Selection#getSelectionEnd}.
*/
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;
}
/**
* 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 {@link #setSingleLine} or {@link #setHorizontallyScrolling}
* to constrain the text to a single line. Use <code>null</code>
* to turn off ellipsizing.
*
* @attr ref android.R.styleable#TextView_ellipsize
*/
public void setEllipsize(TextUtils.TruncateAt where) {
mEllipsize = where;
if (mLayout != null) {
nullLayouts();
requestLayout();
invalidate();
}
}
/**
* Returns where, if anywhere, words that are longer than the view
* is wide should be ellipsized.
*/
public TextUtils.TruncateAt getEllipsize() {
return mEllipsize;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(DEBUG_EXTRACT) System.out.println("TextView onKeyDown");
int which = doKeyDown(keyCode, event, null);
if (which == 0) {
// Go through default dispatching.
return super.onKeyDown(keyCode, event);
}
return true;
}
// This is just a demo Move BaseKeyListener's implementation here temporary,
// For formal product we should keep android's original architecture
// instead.
/**
* Performs the action that happens when you press the DEL key in a
* TextView. If there is a selection, deletes the selection; otherwise, DEL
* alone deletes the character before the cursor, if any; ALT+DEL deletes
* everything on the line the cursor is on.
*
* @return true if anything was deleted; false otherwise.
*/
public boolean backspace(Editable content, int keyCode, KeyEvent event) {
System.out.println("backspace1");
int selStart, selEnd;
boolean result = true;
{
System.out.println("backspace2");
int a = Selection.getSelectionStart(content);
int b = Selection.getSelectionEnd(content);
selStart = Math.min(a, b);
selEnd = Math.max(a, b);
System.out.println("a " + a + " b " + b);
System.out.println("selStart " + selStart + " selEnd " + selEnd);
}
if (selStart != selEnd) {
System.out.println("backspace3");
content.delete(selStart, selEnd);
} else {
int to = TextUtils.getOffsetBefore(content, selEnd);
if (to != selEnd) {
content.delete(Math.min(to, selEnd), Math.max(to, selEnd));
} else {
result = false;
}
}
System.out.println("backspace4");
return result;
}
private int doKeyDown(int keyCode, KeyEvent event, KeyEvent otherEvent) {
if (DEBUG_EXTRACT)
System.out.println("doKeyDown");
if (!isEnabled()) {
if (DEBUG_EXTRACT)
System.out.println("doKeyDown 1");
return 0;
}
if (keyCode == KeyEvent.KEYCODE_DEL) {
System.out.println("mText class " + mText.getClass().getName()
+ " " + (mText instanceof Editable));
if (mBufferType == BufferType.EDITABLE && mText != "" && mText != null) {
System.out.println("backspace");
Editable et = (Editable) mText;
backspace(et, keyCode, event);
return 1;
}
}
if (DEBUG_EXTRACT)
System.out.println("keyCode: " + keyCode);
return 0;
}
protected int mGravity = Gravity.TOP | Gravity.LEFT;
/**
* Returns the horizontal and vertical alignment of this TextView.
*
* @see android.view.Gravity
* @attr ref android.R.styleable#TextView_gravity
*/
public int getGravity() {
return mGravity;
}
/**
* 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 android.view.Gravity
* @attr ref android.R.styleable#TextView_gravity
*/
public void setGravity(int gravity) {
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) {
gravity |= Gravity.LEFT;
}
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
gravity |= Gravity.TOP;
}
boolean newLayout = false;
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) !=
(mGravity & Gravity.HORIZONTAL_GRAVITY_MASK)) {
newLayout = true;
}
if (gravity != mGravity) {
invalidate();
}
mGravity = gravity;
if (mLayout != null && newLayout) {
// XXX this is heavy-handed because no actual content changes.
int want = mLayout.getWidth();
int hintWant = mHintLayout == null ? 0 : mHintLayout.getWidth();
makeNewLayout(want, hintWant, UNKNOWN_BORING, UNKNOWN_BORING,
mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(), true);
}
}
/**
* Makes the TextView at least this many pixels tall
*
* @attr ref android.R.styleable#TextView_minHeight
*/
public void setMinHeight(int minHeight) {
mMinimum = minHeight;
mMinMode = PIXELS;
requestLayout();
invalidate();
}
/**
* Makes the TextView at most this many lines tall
*
* @attr ref android.R.styleable#TextView_maxLines
*/
public void setMaxLines(int maxlines) {
mMaximum = maxlines;
mMaxMode = LINES;
requestLayout();
invalidate();
}
/**
* Makes the TextView at most this many pixels tall
*
* @attr ref android.R.styleable#TextView_maxHeight
*/
public void setMaxHeight(int maxHeight) {
mMaximum = maxHeight;
mMaxMode = PIXELS;
requestLayout();
invalidate();
}
@Override
protected boolean verifyDrawable(Drawable who) {
final boolean verified = super.verifyDrawable(who);
if (!verified && mDrawables != null) {
return who == mDrawables.mDrawableLeft || who == mDrawables.mDrawableTop ||
who == mDrawables.mDrawableRight || who == mDrawables.mDrawableBottom;
}
return verified;
}
@Override
public boolean performLongClick() {
if (super.performLongClick()) {
mEatTouchRelease = true;
return true;
}
return false;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mTemporaryDetach = false;
if (mShowErrorAfterAttach) {
//showError();
mShowErrorAfterAttach = false;
}
final ViewTreeObserver observer = getViewTreeObserver();
if (observer != null) {
if (mInsertionPointCursorController != null) {
observer.addOnTouchModeChangeListener(mInsertionPointCursorController);
}
if (mSelectionModifierCursorController != null) {
observer.addOnTouchModeChangeListener(mSelectionModifierCursorController);
}
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
final ViewTreeObserver observer = getViewTreeObserver();
if (observer != null) {
if (mPreDrawState != PREDRAW_NOT_REGISTERED) {
//observer.removeOnPreDrawListener(this);
mPreDrawState = PREDRAW_NOT_REGISTERED;
}
if (mInsertionPointCursorController != null) {
observer.removeOnTouchModeChangeListener(mInsertionPointCursorController);
}
if (mSelectionModifierCursorController != null) {
observer.removeOnTouchModeChangeListener(mSelectionModifierCursorController);
}
}
if (mError != null) {
//hideError();
}
if (mBlink != null) {
mBlink.cancel();
}
if (mInsertionPointCursorController != null) {
mInsertionPointCursorController.onDetached();
}
if (mSelectionModifierCursorController != null) {
mSelectionModifierCursorController.onDetached();
}
//hideControllers();
}
@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 (!mDispatchTemporaryDetach) mTemporaryDetach = 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 (!mDispatchTemporaryDetach) mTemporaryDetach = false;
}
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
if (!isShown()) {
return false;
}
// TODO: final boolean isPassword = isPasswordInputType(mInputType);
final boolean isPassword = false;
if (!isPassword) {
CharSequence text = getText();
if (TextUtils.isEmpty(text)) {
text = getHint();
}
if (!TextUtils.isEmpty(text)) {
if (text.length() > AccessibilityEvent.MAX_TEXT_LENGTH) {
text = text.subSequence(0, AccessibilityEvent.MAX_TEXT_LENGTH + 1);
}
event.getText().add(text);
}
} else {
event.setPassword(isPassword);
}
return false;
}
/**
* A CursorController instance can be used to control a cursor in the text.
* It is not used outside of {@link TextView}.
* @hide
*/
private interface CursorController extends ViewTreeObserver.OnTouchModeChangeListener {
/**
* Makes the cursor controller visible on screen. Will be drawn by {@link #draw(Canvas)}.
* See also {@link #hide()}.
*/
public void show();
/**
* Hide the cursor controller from screen.
* See also {@link #show()}.
*/
public void hide();
/**
* @return true if the CursorController is currently visible
*/
public boolean isShowing();
/**
* Update the controller's position.
*/
public void updatePosition(HandleView handle, int x, int y);
public void updatePosition();
/**
* This method is called by {@link #onTouchEvent(MotionEvent)} and gives the controller
* a chance to become active and/or visible.
* @param event The touch event
*/
public boolean onTouchEvent(MotionEvent event);
/**
* Called when the view is detached from window. Perform house keeping task, such as
* stopping Runnable thread that would otherwise keep a reference on the context, thus
* preventing the activity to be recycled.
*/
public void onDetached();
}
private class HandleView extends View {
private boolean mPositionOnTop = false;
private Drawable mDrawable;
//private PopupWindow mContainer;
private int mPositionX;
private int mPositionY;
private CursorController mController;
private boolean mIsDragging;
private float mTouchToWindowOffsetX;
private float mTouchToWindowOffsetY;
private float mHotspotX;
private float mHotspotY;
private int mHeight;
private float mTouchOffsetY;
private int mLastParentX;
private int mLastParentY;
public static final int LEFT = 0;
public static final int CENTER = 1;
public static final int RIGHT = 2;
public HandleView(CursorController controller, int pos) {
super(TextView.this.mContext);
mController = controller;
/*
mContainer = new PopupWindow(TextView.this.mContext, null,
com.android.internal.R.attr.textSelectHandleWindowStyle);
mContainer.setSplitTouchEnabled(true);
mContainer.setClippingEnabled(false);
mContainer.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
*/
setOrientation(pos);
}
public void setOrientation(int pos) {
int handleWidth;
switch (pos) {
case LEFT: {
if (mSelectHandleLeft == null) {
mSelectHandleLeft = mContext.getResources().getDrawable(
mTextSelectHandleLeftRes);
}
mDrawable = mSelectHandleLeft;
handleWidth = mDrawable.getIntrinsicWidth();
mHotspotX = (handleWidth * 3) / 4;
break;
}
case RIGHT: {
if (mSelectHandleRight == null) {
mSelectHandleRight = mContext.getResources().getDrawable(
mTextSelectHandleRightRes);
}
mDrawable = mSelectHandleRight;
handleWidth = mDrawable.getIntrinsicWidth();
mHotspotX = handleWidth / 4;
break;
}
case CENTER:
default: {
if (mSelectHandleCenter == null) {
mSelectHandleCenter = mContext.getResources().getDrawable(
mTextSelectHandleRes);
}
mDrawable = mSelectHandleCenter;
handleWidth = mDrawable.getIntrinsicWidth();
mHotspotX = handleWidth / 2;
break;
}
}
final int handleHeight = mDrawable.getIntrinsicHeight();
mTouchOffsetY = -handleHeight * 0.3f;
mHotspotY = 0;
mHeight = handleHeight;
invalidate();
}
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(mDrawable.getIntrinsicWidth(),
mDrawable.getIntrinsicHeight());
}
public void show() {
if (!isPositionVisible()) {
hide();
return;
}
//mContainer.setContentView(this);
final int[] coords = mTempCoords;
TextView.this.getLocationInWindow(coords);
coords[0] += mPositionX;
coords[1] += mPositionY;
//mContainer.showAtLocation(TextView.this, 0, coords[0], coords[1]);
}
public void hide() {
mIsDragging = false;
//mContainer.dismiss();
}
public boolean isShowing() {
return false;
//return mContainer.isShowing();
}
private boolean isPositionVisible() {
// Always show a dragging handle.
if (mIsDragging) {
return true;
}
/*
if (isInBatchEditMode()) {
return false;
}
*/
final int extendedPaddingTop = getExtendedPaddingTop();
final int extendedPaddingBottom = getExtendedPaddingBottom();
final int compoundPaddingLeft = getCompoundPaddingLeft();
final int compoundPaddingRight = getCompoundPaddingRight();
final TextView hostView = TextView.this;
final int left = 0;
final int right = hostView.getWidth();
final int top = 0;
final int bottom = hostView.getHeight();
if (mTempRect == null) {
mTempRect = new Rect();
}
final Rect clip = mTempRect;
clip.left = left + compoundPaddingLeft;
clip.top = top + extendedPaddingTop;
clip.right = right - compoundPaddingRight;
clip.bottom = bottom - extendedPaddingBottom;
final ViewParent parent = hostView.getParent();
if (parent == null || !parent.getChildVisibleRect(hostView, clip, null)) {
return false;
}
final int[] coords = mTempCoords;
hostView.getLocationInWindow(coords);
final int posX = coords[0] + mPositionX + (int) mHotspotX;
final int posY = coords[1] + mPositionY + (int) mHotspotY;
return posX >= clip.left && posX <= clip.right &&
posY >= clip.top && posY <= clip.bottom;
}
private void moveTo(int x, int y) {
mPositionX = x - TextView.this.mScrollX;
mPositionY = y - TextView.this.mScrollY;
if (isPositionVisible()) {
int[] coords = null;
/*
if (mContainer.isShowing()) {
coords = mTempCoords;
TextView.this.getLocationInWindow(coords);
mContainer.update(coords[0] + mPositionX, coords[1] + mPositionY,
mRight - mLeft, mBottom - mTop);
} else {
show();
}
*/
if (mIsDragging) {
if (coords == null) {
coords = mTempCoords;
TextView.this.getLocationInWindow(coords);
}
if (coords[0] != mLastParentX || coords[1] != mLastParentY) {
mTouchToWindowOffsetX += coords[0] - mLastParentX;
mTouchToWindowOffsetY += coords[1] - mLastParentY;
mLastParentX = coords[0];
mLastParentY = coords[1];
}
}
} else {
hide();
}
}
@Override
public void onDraw(Canvas c) {
mDrawable.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
if (mPositionOnTop) {
c.save();
c.rotate(180, (mRight - mLeft) / 2, (mBottom - mTop) / 2);
mDrawable.draw(c);
c.restore();
} else {
mDrawable.draw(c);
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
final float rawX = ev.getRawX();
final float rawY = ev.getRawY();
mTouchToWindowOffsetX = rawX - mPositionX;
mTouchToWindowOffsetY = rawY - mPositionY;
final int[] coords = mTempCoords;
TextView.this.getLocationInWindow(coords);
mLastParentX = coords[0];
mLastParentY = coords[1];
mIsDragging = true;
break;
}
case MotionEvent.ACTION_MOVE: {
final float rawX = ev.getRawX();
final float rawY = ev.getRawY();
final float newPosX = rawX - mTouchToWindowOffsetX + mHotspotX;
final float newPosY = rawY - mTouchToWindowOffsetY + mHotspotY + mTouchOffsetY;
mController.updatePosition(this, Math.round(newPosX), Math.round(newPosY));
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mIsDragging = false;
}
return true;
}
public boolean isDragging() {
return mIsDragging;
}
/*
void positionAtCursor(final int offset, boolean bottom) {
final int width = mDrawable.getIntrinsicWidth();
final int height = mDrawable.getIntrinsicHeight();
final int line = mLayout.getLineForOffset(offset);
final int lineTop = mLayout.getLineTop(line);
final int lineBottom = mLayout.getLineBottom(line);
final Rect bounds = sCursorControllerTempRect;
bounds.left = (int) (mLayout.getPrimaryHorizontal(offset) - mHotspotX)
+ TextView.this.mScrollX;
bounds.top = (bottom ? lineBottom : lineTop - mHeight) + TextView.this.mScrollY;
bounds.right = bounds.left + width;
bounds.bottom = bounds.top + height;
convertFromViewportToContentCoordinates(bounds);
moveTo(bounds.left, bounds.top);
}
*/
}
private static class Blink extends Handler implements Runnable {
private final WeakReference<TextView> mView;
private boolean mCancelled;
public Blink(TextView v) {
mView = new WeakReference<TextView>(v);
}
public void run() {
if (mCancelled) {
return;
}
removeCallbacks(Blink.this);
TextView tv = mView.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() + BLINK);
}
}
}
void cancel() {
if (!mCancelled) {
removeCallbacks(Blink.this);
mCancelled = true;
}
}
void uncancel() {
mCancelled = false;
}
}
/**
* @return 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 mLayout;
}
/**
* 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 {@link KeyListener#getInputType() 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.
* <p>
* 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
* {@link #setFocusable} 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);
// fixFocusableAndClickableSettings();
if (input != null) {
try {
mInputType = mInput.getInputType();
} catch (IncompatibleClassChangeError e) {
mInputType = InputType.TYPE_CLASS_TEXT;
}
if ((mInputType&InputType.TYPE_MASK_CLASS)
== InputType.TYPE_CLASS_TEXT) {
if (mSingleLine) {
mInputType &= ~InputType.TYPE_TEXT_FLAG_MULTI_LINE;
} else {
mInputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE;
}
}
} else {
mInputType = InputType.TYPE_NULL;
}
}
private void setKeyListenerOnly(KeyListener input) {
mInput = input;
// if (mInput != null && !(mText instanceof Editable))
// setText(mText);
// setFilters((Editable) mText, mFilters);
}
/**
* @return the movement method being used for this TextView.
* This will frequently be null for non-EditText TextViews.
*/
public final MovementMethod getMovementMethod() {
return mMovement;
}
/**
* 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.
* <p>
* 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
* {@link #setFocusable} again after calling this to get the focusability
* back the way you want it.
*/
public final void setMovementMethod(MovementMethod movement) {
mMovement = movement;
if (mMovement != null && !(mText instanceof Spannable))
setText(mText);
fixFocusableAndClickableSettings();
// SelectionModifierCursorController depends on textCanBeSelected, which depends on mMovement
prepareCursorControllers();
}
private void fixFocusableAndClickableSettings() {
if ((mMovement != null) || mInput != null) {
setFocusable(true);
setClickable(true);
setLongClickable(true);
} else {
setFocusable(false);
setClickable(false);
setLongClickable(false);
}
}
private void prepareCursorControllers() {
boolean windowSupportsHandles = false;
ViewGroup.LayoutParams params = getRootView().getLayoutParams();
if (params instanceof WindowManager.LayoutParams) {
WindowManager.LayoutParams windowParams = (WindowManager.LayoutParams) params;
windowSupportsHandles = windowParams.type < WindowManager.LayoutParams.FIRST_SUB_WINDOW
|| windowParams.type > WindowManager.LayoutParams.LAST_SUB_WINDOW;
}
// TODO Add an extra android:cursorController flag to disable the controller?
mInsertionControllerEnabled = windowSupportsHandles && mCursorVisible && mLayout != null;
mSelectionControllerEnabled = windowSupportsHandles && textCanBeSelected() &&
mLayout != null;
if (!mInsertionControllerEnabled) {
mInsertionPointCursorController = null;
}
if (!mSelectionControllerEnabled) {
// Stop selection mode if the controller becomes unavailable.
stopTextSelectionMode();
mSelectionModifierCursorController = null;
}
}
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 (mText instanceof Spannable &&
mMovement != null &&
mMovement.canSelectArbitrarily());
}
private void stopTextSelectionMode() {
if (mIsInTextSelectionMode) {
Selection.setSelection((Spannable) mText, getSelectionEnd());
hideSelectionModifierCursorController();
mIsInTextSelectionMode = false;
}
}
private void hideSelectionModifierCursorController() {
if (mSelectionModifierCursorController != null) {
mSelectionModifierCursorController.hide();
}
}
/**
* @j2sNative
* console.log("Missing method: setMarqueeRepeatLimit");
*/
@MayloonStubAnnotation()
public void setMarqueeRepeatLimit(int marqueeLimit) {
System.out.println("Stub" + " Function : setMarqueeRepeatLimit");
return;
}
/**
* @j2sNative
* console.log("Missing method: setRawInputType");
*/
@MayloonStubAnnotation()
public void setRawInputType(int type) {
System.out.println("Stub" + " Function : setRawInputType");
return;
}
public void setOnEditorActionListener(OnEditorActionListener l) {
if (mInputContentType == null) {
mInputContentType = new InputContentType();
}
mInputContentType.onEditorActionListener = l;
}
public void setEms(int ems) {
mMaxWidth = mMinWidth = (ems * getLineHeight());
// mMaxWidthMode = mMinWidthMode = EMS;
requestLayout();
invalidate();
}
/**
* @j2sNative
* console.log("Missing method: onTextContextMenuItem");
*/
@MayloonStubAnnotation()
public boolean onTextContextMenuItem(int id) {
System.out.println("Stub" + " Function : onTextContextMenuItem");
return true;
}
/**
* @j2sNative
* console.log("Missing method: getImeActionLabel");
*/
@MayloonStubAnnotation()
public CharSequence getImeActionLabel() {
System.out.println("Stub" + " Function : getImeActionLabel");
return null;
}
/**
* @j2sNative
* console.log("Missing method: clearComposingText");
*/
@MayloonStubAnnotation()
public void clearComposingText() {
System.out.println("Stub" + " Function : clearComposingText");
return;
}
public void setMinWidth(int minpixels) {
mMinWidth = minpixels;
// mMinWidthMode = PIXELS;
requestLayout();
invalidate();
}
public void setMinLines(int minlines) {
mMinimum = minlines;
mMinMode = LINES;
requestLayout();
invalidate();
}
/**
* @j2sNative
* console.log("Missing method: onBeginBatchEdit");
*/
@MayloonStubAnnotation()
public void onBeginBatchEdit() {
System.out.println("Stub" + " Function : onBeginBatchEdit");
return;
}
public final void setLinksClickable(boolean whether) {
mLinksClickable = whether;
}
/**
* @j2sNative
* console.log("Missing method: isInputMethodTarget");
*/
@MayloonStubAnnotation()
public boolean isInputMethodTarget() {
System.out.println("Stub" + " Function : isInputMethodTarget");
return true;
}
/**
* @j2sNative
* console.log("Missing method: beginBatchEdit");
*/
@MayloonStubAnnotation()
public void beginBatchEdit() {
System.out.println("Stub" + " Function : beginBatchEdit");
return;
}
/**
* @j2sNative
* console.log("Missing method: onEditorAction");
*/
@MayloonStubAnnotation()
public void onEditorAction(int actionCode) {
System.out.println("Stub" + " Function : onEditorAction");
return;
}
public final void setLinkTextColor(int color) {
mLinkTextColor = 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) {
mLinkTextColor = colors;
updateTextColors();
}
/**
* <p>Returns the color used to paint links in the text.</p>
*
* @return Returns the list of link text colors.
*/
public final ColorStateList getLinkTextColors() {
return mLinkTextColor;
}
/**
* @j2sNative
* console.log("Missing method: getCompoundDrawablePadding");
*/
@MayloonStubAnnotation()
public int getCompoundDrawablePadding() {
System.out.println("Stub" + " Function : getCompoundDrawablePadding");
return 0;
}
/**
* @j2sNative
* console.log("Missing method: setSelectAllOnFocus");
*/
@MayloonStubAnnotation()
public void setSelectAllOnFocus(boolean selectAllOnFocus) {
System.out.println("Stub" + " Function : setSelectAllOnFocus");
return;
}
/**
* @j2sNative
* console.log("Missing method: getPrivateImeOptions");
*/
@MayloonStubAnnotation()
public String getPrivateImeOptions() {
System.out.println("Stub" + " Function : getPrivateImeOptions");
return null;
}
/**
* @j2sNative
* console.log("Missing method: getInputType");
*/
@MayloonStubAnnotation()
public int getInputType() {
System.out.println("Stub" + " Function : getInputType");
return 0;
}
public final void setHintTextColor(int color) {
mHintTextColor = ColorStateList.valueOf(color);
updateTextColors();
}
/**
* Sets the color of the hint text.
*
* @attr ref android.R.styleable#TextView_textColorHint
*/
public final void setHintTextColor(ColorStateList colors) {
mHintTextColor = colors;
updateTextColors();
}
/**
* <p>Return the color used to paint the hint text.</p>
*
* @return Returns the list of hint text colors.
*/
public final ColorStateList getHintTextColors() {
return mHintTextColor;
}
/**
* @j2sNative
* console.log("Missing method: endBatchEdit");
*/
@MayloonStubAnnotation()
public void endBatchEdit() {
System.out.println("Stub" + " Function : endBatchEdit");
return;
}
/**
* @j2sNative
* console.log("Missing method: set");
*/
@MayloonStubAnnotation()
void set(char[] chars, int start, int len) {
System.out.println("Stub" + " Function : set");
return;
}
public void setSingleLine() {
setSingleLine(true);
}
public int getLineHeight() {
return FastMath.round(mTextPaint.getFontMetricsInt(null) * mSpacingMult + mSpacingAdd);
}
private int getTextareaParams() {
int value = 0;
/**
* @j2sNative
* var div = document.createElement("textarea");
* div.rows = 1;
* div.cols = this.mDefaultSize;
* div.id = "measure_div";
* div.style.visibility = "hidden";
* document.body.appendChild(div);
* div.style.position = "absolute";
* div.style.overflow = "hidden";
* div.style.border = "none";
* div.style.paddingBottom = div.style.paddingTop = div.style.paddingLeft = div.style.paddingRight = "0px";
* div.style.marginBottom = div.style.marginTop = div.style.marginLeft = div.style.marginRight = "0px";
* this.setFontProperties(div.id);
* value = div.offsetWidth;
* div.parentNode.removeChild(div);
*/{}
return value;
}
/**
* @param rows In general there are 3, first 1 lines, second min lines, third max lines
* @param style 1.width 2.height
* @return
*/
private int getTextareaParams(CharSequence text, int cNum, int rows, int style) {
int value = 0;
if (mDefaultWidth == mDefaultSize) {
cNum = mDefaultSize;
}
/**
* @j2sNative
* var div = document.createElement("textarea");
* div.value = text.toString();
* div.rows = rows;
* div.cols = cNum;
* div.id = "measure_div";
* div.style.visibility = "hidden";
* document.body.appendChild(div);
* div.style.position = "absolute";
* div.style.overflow = "hidden";
* div.style.border = "none";
* div.style.padding = "0px";
* div.style.margin = "0px";
* this.setFontProperties(div.id);
* if (style == 1) {
* value = cNum > 0 ? div.offsetWidth : 0;
* } else if (style == 2) {
* if (rows == 1) {
* this.mOneLineHeight = div.offsetHeight;
* div.style.posHeight = div.scrollHeight;
* this.mLines = Math.ceil(div.scrollHeight / this.mOneLineHeight);
* value = div.scrollHeight;
* } else {
* value = div.offsetHeight;
* }
* }
* div.parentNode.removeChild(div);
*/{}
return value;
}
/**
* @param text
* @param singleLine
* @param rows In general there are 3, first 1 lines, second min lines, third max lines
* @param style 0. lineHeight 1.width 2.height
* @return
*/
private int getDivParams(CharSequence text, Boolean singleLine, int style) {
int value = 0;
/**
* @j2sNative
* var div = document.createElement("div");
* var temp = text.toString();
* if (style != 0) {
* var lineHeight = this.getLineHeight();
* div.style.height = lineHeight + "px";
* }
* if (style == 2 && this.mCurWidth > 0) {
* div.style.width = this.mCurWidth + "px";
* }
* if (temp == " ") {
* temp = temp.replaceAll(" ", " ");
* } else if (temp == "<type>") {
* temp = "<" + "type" + ">";
* } else if (temp == "<untitled>") {
* temp = "<" + "untitled" + ">";
* } else {
* temp = temp.replaceAll("\r\n", "<br/>");
* temp = temp.replaceAll("\n", "<br/>");
* temp = temp.replaceAll("<br />", "<br/>");
* }
* if ((singleLine).booleanValue ()) {
* temp = temp.replaceAll("<br/>", " ");
* div.style.whiteSpace = "nowrap";
* }
* div.innerHTML = temp;
* div.id = "measure_div";
* div.style.visibility = "hidden";
* document.body.appendChild(div);
* div.style.position = "absolute";
* div.style.overflow = "hidden";
* div.style.border = "none";
* div.style.paddingBottom = div.style.paddingTop = div.style.paddingLeft = div.style.paddingRight = "0px";
* div.style.marginBottom = div.style.marginTop = div.style.marginLeft = div.style.marginRight = "0px";
* this.setFontProperties(div.id);
* if (style == 1) {
* value = div.offsetWidth;
* } else if (style == 2) {
* this.mOneLineHeight = div.offsetHeight;
* div.style.posHeight = div.scrollHeight;
* this.mLines = Math.ceil(div.scrollHeight / this.mOneLineHeight);
* value = div.scrollHeight;
* } else if (style == 0) {
* value = this.mOneLineHeight = div.offsetHeight;
* }
* div.parentNode.removeChild(div);
*/{}
return value;
}
/**
* Return width or height according to the text
* @param style 1.width 2.height
*/
private int getInputParams(CharSequence text, int style) {
int value = 0;
/**
* @j2sNative
* var div = document.createElement("input");
* div.type = "text";
* div.value = text.toString();
* div.id = "measure_div";
* div.style.visibility = "hidden";
* document.body.appendChild(div);
* div.style.position = "absolute";
* div.style.border = "none";
* div.style.paddingBottom = div.style.paddingTop = div.style.paddingLeft = div.style.paddingRight = "0px";
* div.style.marginBottom = div.style.marginTop = div.style.marginLeft = div.style.marginRight = "0px";
* this.setFontProperties(div.id);
* if (style == 1) {
* value = div.offsetWidth;
* } else {
* this.mLines = 1;
* value = this.mOneLineHeight = div.offsetHeight;
* }
* div.parentNode.removeChild(div);
*/{}
return value;
}
/**
* @j2sNative
* console.log("Missing method: getImeActionId");
*/
@MayloonStubAnnotation()
public int getImeActionId() {
System.out.println("Stub" + " Function : getImeActionId");
return 0;
}
public void setMinEms(int minems) {
mMinWidth = (minems * getLineHeight());
// mMinWidthMode = EMS;
requestLayout();
invalidate();
}
public final int getCurrentHintTextColor() {
return mHintTextColor != null ? mCurHintTextColor : mCurTextColor;
}
/**
* 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) {
mIncludePad = includepad;
if (mLayout != null) {
nullLayouts();
requestLayout();
invalidate();
}
}
public boolean moveCursorToVisibleOffset() {
if (!(mText 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 = mLayout.getLineForOffset(start);
final int top = mLayout.getLineTop(line);
final int bottom = mLayout.getLineTop(line + 1);
final int vspace = mBottom - mTop - getExtendedPaddingTop() - getExtendedPaddingBottom();
int vslack = (bottom - top) / 2;
if (vslack > vspace / 4)
vslack = vspace / 4;
final int vs = mScrollY;
if (top < (vs+vslack)) {
line = mLayout.getLineForVertical(vs+vslack+(bottom-top));
} else if (bottom > (vspace+vs-vslack)) {
line = mLayout.getLineForVertical(vspace+vs-vslack-(bottom-top));
}
// Next: make sure the character is visible on screen:
final int hspace = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight();
final int hs = mScrollX;
final int leftChar = mLayout.getOffsetForHorizontal(line, hs);
final int rightChar = mLayout.getOffsetForHorizontal(line, hspace+hs);
int newStart = start;
if (newStart < leftChar) {
newStart = leftChar;
} else if (newStart > rightChar) {
newStart = rightChar;
}
if (newStart != start) {
Selection.setSelection((Spannable)mText, newStart);
return true;
}
return false;
}
public final ColorStateList getTextColors() {
return mTextColor;
}
public final int getCurrentTextColor() {
return mCurTextColor;
}
public void setError(CharSequence error) {
if (error == null) {
setError(null, null);
} else {
Drawable dr = getContext().getResources().
getDrawable(com.android.internal.R.drawable.
indicator_input_error);
dr.setBounds(0, 0, dr.getIntrinsicWidth(), dr.getIntrinsicHeight());
setError(error, dr);
}
}
/**
* 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 {@link Drawable#setBounds} set on it.
* If the <code>error</code> is <code>null</code>, the error message will
* be cleared (and you should provide a <code>null</code> icon as well).
*/
public void setError(CharSequence error, Drawable icon) {
error = TextUtils.stringOrSpannedString(error);
mError = error;
mErrorWasChanged = true;
final Drawables dr = mDrawables;
if (dr != null) {
setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop,
icon, dr.mDrawableBottom);
} else {
setCompoundDrawables(null, null, icon, null);
}
if (error == null) {
if (mPopup != null) {
if (mPopup.isShowing()) {
mPopup.dismiss();
}
mPopup = null;
}
} else {
if (isFocused()) {
showError();
}
}
}
private void showError() {
// if (getWindowToken() == null) {
// mShowErrorAfterAttach = true;
// return;
// }
if (mPopup == null) {
LayoutInflater inflater = LayoutInflater.from(getContext());
final TextView err = (TextView) inflater.inflate(
com.android.internal.R.layout.textview_hint,
null);
final float scale = getResources().getDisplayMetrics().density;
mPopup = new ErrorPopup(err, (int) (200 * scale + 0.5f),
(int) (50 * scale + 0.5f));
mPopup.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.
mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
}
TextView tv = (TextView) mPopup.getContentView();
// chooseSize(mPopup, mError, tv);
tv.setText(mError);
// mPopup.showAsDropDown(this, getErrorX(), getErrorY());
mPopup.fixDirection(mPopup.isAboveAnchor());
}
private static class ErrorPopup extends PopupWindow {
private boolean mAbove = false;
private final TextView mView;
ErrorPopup(TextView v, int width, int height) {
super(v, width, height);
mView = v;
}
void fixDirection(boolean above) {
mAbove = above;
if (above) {
mView.setBackgroundResource(com.android.internal.R.drawable.popup_inline_error_above);
} else {
mView.setBackgroundResource(com.android.internal.R.drawable.popup_inline_error);
}
}
@Override
public void update(int x, int y, int w, int h, boolean force) {
super.update(x, y, w, h, force);
boolean above = isAboveAnchor();
if (above != mAbove) {
fixDirection(above);
}
}
}
@Override
public int getBaseline() {
if (mLayout == null) {
return super.getBaseline();
}
int voffset = 0;
if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
if (!mSingleLine) {
voffset = getVerticalOffset(true);
}
}
return getExtendedPaddingTop() + voffset;
// return getExtendedPaddingTop() + voffset + mLayout.getLineBaseline(0);
}
/**
* @j2sNative
* console.log("Missing method: onPreDraw");
*/
@MayloonStubAnnotation()
public boolean onPreDraw() {
System.out.println("Stub" + " Function : onPreDraw");
return true;
}
/**
* @j2sNative
* console.log("Missing method: setPrivateImeOptions");
*/
@MayloonStubAnnotation()
public void setPrivateImeOptions(String type) {
System.out.println("Stub" + " Function : setPrivateImeOptions");
return;
}
/**
* @j2sNative
* console.log("Missing method: Marquee");
*/
@MayloonStubAnnotation()
void Marquee(TextView v) {
System.out.println("Stub" + " Function : Marquee");
return;
}
/**
* @j2sNative
* console.log("Missing method: setAutoLinkMask");
*/
@MayloonStubAnnotation()
public final void setAutoLinkMask(int mask) {
System.out.println("Stub" + " Function : setAutoLinkMask");
return;
}
/**
* @j2sNative
* console.log("Missing method: getAutoLinkMask");
*/
@MayloonStubAnnotation()
public final int getAutoLinkMask() {
System.out.println("Stub" + " Function : getAutoLinkMask");
return 0;
}
public void setMaxWidth(int maxpixels) {
mMaxWidth = maxpixels;
mMaxWidthMode = PIXELS;
requestLayout();
invalidate();
}
/**
* @j2sNative
* console.log("Missing method: setCompoundDrawablePadding");
*/
@MayloonStubAnnotation()
public void setCompoundDrawablePadding(int pad) {
System.out.println("Stub" + " Function : setCompoundDrawablePadding");
return;
}
/**
* @j2sNative
* console.log("Missing method: setCursorVisible");
*/
@MayloonStubAnnotation()
public void setCursorVisible(boolean visible) {
System.out.println("Stub" + " Function : setCursorVisible");
return;
}
/**
* @j2sNative
* console.log("Missing method: cancel");
*/
@MayloonStubAnnotation()
void cancel() {
System.out.println("Stub" + " Function : cancel");
return;
}
public void setMaxEms(int maxems) {
mMaxWidth = (maxems * getLineHeight());
// mMaxWidthMode = EMS;
requestLayout();
invalidate();
}
/**
* @j2sNative
* console.log("Missing method: getImeOptions");
*/
@MayloonStubAnnotation()
public int getImeOptions() {
System.out.println("Stub" + " Function : getImeOptions");
return 0;
}
public CharSequence getError() {
return mError;
}
/**
* @j2sNative
* console.log("Missing method: didTouchFocusSelect");
*/
@MayloonStubAnnotation()
public boolean didTouchFocusSelect() {
System.out.println("Stub" + " Function : didTouchFocusSelect");
return true;
}
public void setHighlightColor(int color) {
if (mHighlightColor != color) {
mHighlightColor = color;
invalidate();
}
}
/**
* Return the text the TextView is displaying as an Editable object. If
* the text is not editable, null is returned.
*
* @see #getText
*/
public Editable getEditableText() {
return (mText instanceof Editable) ? (Editable)mText : null;
}
/**
* 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 (mListeners != null) {
int i = mListeners.indexOf(watcher);
if (i >= 0) {
mListeners.remove(i);
}
}
}
/**
* 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 radius, float dx, float dy, int color) {
mShadowRadius = radius;
mShadowDx = dx;
mShadowDy = dy;
invalidate();
}
public final boolean getLinksClickable() {
return mLinksClickable;
}
public void setSingleLine(boolean singleLine) {
if ((mInputType&EditorInfo.TYPE_MASK_CLASS)
== EditorInfo.TYPE_CLASS_TEXT) {
if (singleLine) {
mInputType &= ~EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
} else {
mInputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
}
}
applySingleLine(singleLine, true);
}
private void applySingleLine(boolean singleLine, boolean applyTransformation) {
mSingleLine = singleLine;
if (singleLine) {
setLines(1);
setHorizontallyScrolling(true);
if (applyTransformation) {
setTransformationMethod(SingleLineTransformationMethod.
getInstance());
}
} else {
setMaxLines(Integer.MAX_VALUE);
setHorizontallyScrolling(false);
if (applyTransformation) {
setTransformationMethod(null);
}
}
}
/**
* 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 == mTransformation) {
// Avoid the setText() below if the transformation is
// the same.
return;
}
if (mTransformation != null) {
if (mText instanceof Spannable) {
((Spannable) mText).removeSpan(mTransformation);
}
}
mTransformation = method;
setText(mText);
}
/**
* @return the current transformation method for this TextView.
* This will frequently be null except for single-line and password
* fields.
*/
public final TransformationMethod getTransformationMethod() {
return mTransformation;
}
/**
* Returns the list of URLSpans attached to the text
* (by {@link Linkify} or otherwise) if any. You can call
* {@link URLSpan#getURL} on them to find where they link to
* or use {@link Spanned#getSpanStart} and {@link Spanned#getSpanEnd}
* to find the region of the text they are attached to.
*/
public URLSpan[] getUrls() {
if (mText instanceof Spanned) {
return ((Spanned) mText).getSpans(0, mText.length(), URLSpan.class);
} else {
return new URLSpan[0];
}
}
/**
* Retrieve the input extras currently associated with the text view, which
* can be viewed as well as modified.
*
* @param 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 #setInputExtras(int)
* @see EditorInfo#extras
* @attr ref android.R.styleable#TextView_editorExtras
*/
public Bundle getInputExtras(boolean create) {
if (mInputContentType == null) {
if (!create) return null;
mInputContentType = new InputContentType();
}
if (mInputContentType.extras == null) {
if (!create) return null;
mInputContentType.extras = new Bundle();
}
return mInputContentType.extras;
}
/**
* @j2sNative
* console.log("Missing method: debug");
*/
@MayloonStubAnnotation()
public void debug(int depth) {
System.out.println("Stub" + " Function : debug");
return;
}
/**
* @j2sNative
* console.log("Missing method: onEndBatchEdit");
*/
@MayloonStubAnnotation()
public void onEndBatchEdit() {
System.out.println("Stub" + " Function : onEndBatchEdit");
return;
}
/**
* @j2sNative
* console.log("Missing method: setImeOptions");
*/
@MayloonStubAnnotation()
public void setImeOptions(int imeOptions) {
System.out.println("Stub" + " Function : setImeOptions");
return;
}
public boolean bringPointIntoView(int offset) {
boolean changed = false;
int line = mLayout.getLineForOffset(offset);
// FIXME: Is it okay to truncate this, or should we round?
final int x = (int)mLayout.getPrimaryHorizontal(offset);
final int top = mLayout.getLineTop(line);
final int bottom = mLayout.getLineTop(line + 1);
int left = (int) FloatMath.floor(mLayout.getLineLeft(line));
int right = (int) FloatMath.ceil(mLayout.getLineRight(line));
int ht = mLayout.getHeight();
int grav;
switch (mLayout.getParagraphAlignment(line)) {
case ALIGN_NORMAL:
grav = 1;
break;
case ALIGN_OPPOSITE:
grav = -1;
break;
default:
grav = 0;
}
grav *= mLayout.getParagraphDirection(line);
int hspace = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight();
int vspace = mBottom - mTop - 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 = mScrollX;
int vs = mScrollY;
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 != mScrollX || vs != mScrollY) {
if (mScroller == null) {
scrollTo(hs, vs);
} else {
long duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll;
int dx = hs - mScrollX;
int dy = vs - mScrollY;
if (duration > ANIMATED_SCROLL_GAP) {
mScroller.startScroll(mScrollX, mScrollY, dx, dy);
awakenScrollBars(mScroller.getDuration());
invalidate();
} else {
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
scrollBy(dx, dy);
}
mLastScroll = 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(x, top, x + 1, bottom);
getInterestingRect(r, line);
r.offset(mScrollX, mScrollY);
if (requestRectangleOnScreen(r)) {
changed = true;
}
}
return changed;
}
private void getInterestingRect(Rect r, int line) {
convertFromViewportToContentCoordinates(r);
// 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 == mLayout.getLineCount() - 1) r.bottom += getExtendedPaddingBottom();
}
private void convertFromViewportToContentCoordinates(Rect r) {
final int horizontalOffset = viewportToContentHorizontalOffset();
r.left += horizontalOffset;
r.right += horizontalOffset;
final int verticalOffset = viewportToContentVerticalOffset();
r.top += verticalOffset;
r.bottom += verticalOffset;
}
@Override
public void cancelLongPress() {
super.cancelLongPress();
mScrolled = true;
}
private int viewportToContentHorizontalOffset() {
return getCompoundPaddingLeft() - mScrollX;
}
private int viewportToContentVerticalOffset() {
int offset = getExtendedPaddingTop() - mScrollY;
if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) {
offset += getVerticalOffset(false);
}
return offset;
}
private static class CharWrapper
implements CharSequence, GetChars, GraphicsOperations {
private char[] mChars;
private int mStart, mLength;
public CharWrapper(char[] chars, int start, int len) {
mChars = chars;
mStart = start;
mLength = len;
}
/* package */void set(char[] chars, int start, int len) {
mChars = chars;
mStart = start;
mLength = len;
}
public int length() {
return mLength;
}
public char charAt(int off) {
return mChars[off + mStart];
}
@Override
public String toString() {
return new String(mChars, mStart, mLength);
}
public CharSequence subSequence(int start, int end) {
if (start < 0 || end < 0 || start > mLength || end > mLength) {
throw new IndexOutOfBoundsException(start + ", " + end);
}
return new String(mChars, start + mStart, end - start);
}
public void getChars(int start, int end, char[] buf, int off) {
if (start < 0 || end < 0 || start > mLength || end > mLength) {
throw new IndexOutOfBoundsException(start + ", " + end);
}
System.arraycopy(mChars, start + mStart, buf, off, end - start);
}
public void drawText(Canvas c, int start, int end,
float x, float y, Paint p) {
c.drawText(mChars, start + mStart, end - start, x, y, p);
}
public float measureText(int start, int end, Paint p) {
return p.measureText(mChars, start + mStart, end - start);
}
public int getTextWidths(int start, int end, float[] widths, Paint p) {
return p.getTextWidths(mChars, start + mStart, end - start, widths);
}
}
private static final InputFilter[] NO_FILTERS = new InputFilter[0];
private InputFilter[] mFilters = NO_FILTERS;
private static final Spanned EMPTY_SPANNED = new SpannedString("");
protected MovementMethod getDefaultMovementMethod() {
return null;
}
public static int getTextColor(Context context, TypedArray attrs, int def) {
ColorStateList colors = getTextColors(context, attrs);
if (colors == null) {
return def;
} else {
return colors.getDefaultColor();
}
}
private static final BoringLayout.Metrics UNKNOWN_BORING = new BoringLayout.Metrics();
/**
* The width passed in is now the desired layout width,
* not the full view width with padding.
* {@hide}
*/
protected void makeNewLayout(int w, int hintWidth,
BoringLayout.Metrics boring,
BoringLayout.Metrics hintBoring,
int ellipsisWidth, boolean bringIntoView) {
if (w < 0) {
w = 0;
}
if (hintWidth < 0) {
hintWidth = 0;
}
Layout.Alignment alignment;
switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
alignment = Layout.Alignment.ALIGN_CENTER;
break;
case Gravity.RIGHT:
alignment = Layout.Alignment.ALIGN_OPPOSITE;
break;
default:
alignment = Layout.Alignment.ALIGN_NORMAL;
}
mEllipsize = null;/** mayloon does not support ellipsis now */
boolean shouldEllipsize = mEllipsize != null && mInput == null;
if (mText instanceof Spannable) {//like input and textarea;
mLayout = new DynamicLayout(mText, mTransformed, mTextPaint, w, alignment, mSpacingMult,
mSpacingAdd, mIncludePad, mInput == null ? mEllipsize : null, ellipsisWidth);
} else {
if (boring == UNKNOWN_BORING) {
boring = BoringLayout.isBoring(mTransformed, mTextPaint, mBoring);
if (boring != null) {
// The textarea or input is larger than div 2 pixels by each side that in the same font.
if (mEditable) {
boring.width = boring.width + 4;
boring.bottom = boring.bottom + 4;
}
mBoring = boring;
}
}
if (boring != null) {
if (boring.width <= w && (mEllipsize == null || boring.width <= ellipsisWidth)) {
mLayout = BoringLayout.make(mTransformed, mTextPaint, w, alignment, mSpacingMult, mSpacingAdd, boring, mIncludePad);
} else {//single but too long so should wordWrap
mLayout = new StaticLayout(mTransformed, mTextPaint, w, alignment, mSpacingMult, mSpacingAdd, mIncludePad);
}
} else {//div and mult
mLayout = new StaticLayout(mTransformed, mTextPaint, w, alignment, mSpacingMult, mSpacingAdd, mIncludePad);
}
}
// CursorControllers need a non-null mLayout
prepareCursorControllers();
}
private int desired(Layout layout) {
CharSequence text = layout.getText();
if (!text.toString().contains("\n")) {
return -1;
} else {
float max = layout.getDesiredWidth(text, mTextPaint);
return (int) FloatMath.ceil(max);
}
}
private void nullLayouts() {
mLayout = mHintLayout = null;
}
/**
* 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 ((mLayoutParams.width != LayoutParams.WRAP_CONTENT ||
(mMaxWidth == mMinWidth)) &&
(mHint == null || mHintLayout != null) &&
(mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight() > 0)) {
// Static width, so try making a new text layout.
int oldht = this.getHeight();
int want = mLayout.getWidth();
int hintWant = mHintLayout == null ? 0 : mHintLayout.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(want, hintWant, UNKNOWN_BORING, UNKNOWN_BORING,
mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(),
false);
// We lose: the height has changed and we have a dynamic height.
// Request a new view layout using our new text layout.
} else {
// Dynamic width, so we have no choice but to request a new
// view layout with a new text layout.
nullLayouts();
}
}
private void sendBeforeTextChanged(CharSequence text, int start, int before,
int after) {
if (mListeners != null) {
final ArrayList<TextWatcher> list = mListeners;
final int count = list.size();
for (int i = 0; i < count; i++) {
list.get(i).beforeTextChanged(text, start, before, after);
}
}
}
}