blob: 3027fcc4a86d80ac97a07c9272c756688783cfd2 [file] [log] [blame]
package com.xtremelabs.robolectric.shadows;
import android.content.Context;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.text.Layout;
import android.text.SpannableStringBuilder;
import android.text.TextPaint;
import android.text.TextWatcher;
import android.text.method.MovementMethod;
import android.text.method.TransformationMethod;
import android.text.style.URLSpan;
import android.text.util.Linkify;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.inputmethod.EditorInfo;
import android.widget.TextView;
import com.xtremelabs.robolectric.internal.Implementation;
import com.xtremelabs.robolectric.internal.Implements;
import java.util.ArrayList;
import java.util.List;
import static android.view.View.VISIBLE;
import static com.xtremelabs.robolectric.Robolectric.shadowOf;
import static com.xtremelabs.robolectric.Robolectric.shadowOf_;
@SuppressWarnings({"UnusedDeclaration"})
@Implements(TextView.class)
public class ShadowTextView extends ShadowView {
private CharSequence text = "";
private CompoundDrawables compoundDrawablesImpl = new CompoundDrawables(0, 0, 0, 0);
private Integer textColorHexValue;
private Integer hintColorHexValue;
private float textSize = 14.0f;
private boolean autoLinkPhoneNumbers;
private int autoLinkMask;
private CharSequence hintText;
private CharSequence errorText;
private int compoundDrawablePadding;
private MovementMethod movementMethod;
private boolean linksClickable;
private int gravity;
private TextView.OnEditorActionListener onEditorActionListener;
private int imeOptions = EditorInfo.IME_NULL;
private int textAppearanceId;
private TransformationMethod transformationMethod;
private int inputType;
protected int selectionStart = 0;
protected int selectionEnd = 0;
private Typeface typeface;
private List<TextWatcher> watchers = new ArrayList<TextWatcher>();
private List<Integer> previousKeyCodes = new ArrayList<Integer>();
private List<KeyEvent> previousKeyEvents = new ArrayList<KeyEvent>();
private Layout layout;
@Override
public void applyAttributes() {
super.applyAttributes();
applyTextAttribute();
applyTextColorAttribute();
applyHintAttribute();
applyHintColorAttribute();
applyCompoundDrawablesWithIntrinsicBoundsAttributes();
}
@Implementation(i18nSafe = false)
public void setText(CharSequence text) {
if (text == null) {
text = "";
}
sendBeforeTextChanged(text);
CharSequence oldValue = this.text;
this.text = text;
sendOnTextChanged(oldValue);
sendAfterTextChanged();
}
@Implementation
public final void append(CharSequence text) {
boolean isSelectStartAtEnd = selectionStart == this.text.length();
boolean isSelectEndAtEnd = selectionEnd == this.text.length();
CharSequence oldValue = this.text;
StringBuffer sb = new StringBuffer(this.text);
sb.append(text);
sendBeforeTextChanged(sb.toString());
this.text = sb.toString();
if (isSelectStartAtEnd) {
selectionStart = this.text.length();
}
if (isSelectEndAtEnd) {
selectionEnd = this.text.length();
}
sendOnTextChanged(oldValue);
sendAfterTextChanged();
}
@Implementation
public void setText(int textResourceId) {
sendBeforeTextChanged(text);
CharSequence oldValue = this.text;
this.text = getResources().getText(textResourceId);
sendOnTextChanged(oldValue);
sendAfterTextChanged();
}
private void sendAfterTextChanged() {
for (TextWatcher watcher : watchers) {
watcher.afterTextChanged(new SpannableStringBuilder(getText()));
}
}
private void sendOnTextChanged(CharSequence oldValue) {
for (TextWatcher watcher : watchers) {
watcher.onTextChanged(text, 0, oldValue.length(), text.length());
}
}
private void sendBeforeTextChanged(CharSequence newValue) {
for (TextWatcher watcher : watchers) {
watcher.beforeTextChanged(this.text, 0, this.text.length(), newValue.length());
}
}
@Implementation
public CharSequence getText() {
return text;
}
@Implementation
public Typeface getTypeface() {
return typeface;
}
@Implementation
public void setTypeface(Typeface typeface) {
this.typeface = typeface;
}
@Implementation
public int length() {
return text.length();
}
@Implementation
public void setTextColor(int color) {
textColorHexValue = color;
}
@Implementation
public void setTextSize(float size) {
textSize = size;
}
@Implementation
public void setTextAppearance(Context context, int resid) {
textAppearanceId = resid;
}
@Implementation
public void setInputType(int type) {
this.inputType = type;
}
@Implementation
public int getInputType() {
return this.inputType;
}
@Implementation
public final void setHint(int resId) {
this.hintText = getResources().getText(resId);
}
@Implementation(i18nSafe = false)
public final void setHint(CharSequence hintText) {
this.hintText = hintText;
}
@Implementation
public CharSequence getHint() {
return hintText;
}
@Implementation
public final void setHintTextColor(int color) {
hintColorHexValue = color;
}
@Implementation
public final boolean getLinksClickable() {
return linksClickable;
}
@Implementation
public final void setLinksClickable(boolean whether) {
linksClickable = whether;
}
@Implementation
public final MovementMethod getMovementMethod() {
return movementMethod;
}
@Implementation
public final void setMovementMethod(MovementMethod movement) {
movementMethod = movement;
}
@Implementation
public URLSpan[] getUrls() {
String[] words = text.toString().split("\\s+");
List<URLSpan> urlSpans = new ArrayList<URLSpan>();
for (String word : words) {
if (word.startsWith("http://")) {
urlSpans.add(new URLSpan(word));
}
}
return urlSpans.toArray(new URLSpan[urlSpans.size()]);
}
@Implementation
public final void setAutoLinkMask(int mask) {
autoLinkMask = mask;
autoLinkPhoneNumbers = (mask & Linkify.PHONE_NUMBERS) != 0;
}
@Implementation
public void setCompoundDrawablesWithIntrinsicBounds(int left, int top, int right, int bottom) {
compoundDrawablesImpl = new CompoundDrawables(left, top, right, bottom);
}
@Implementation
public void setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top,
Drawable right, Drawable bottom) {
compoundDrawablesImpl = new CompoundDrawables(left, top, right, bottom);
}
@Implementation
public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom) {
compoundDrawablesImpl = new CompoundDrawables(left, top, right, bottom);
}
@Implementation
public Drawable[] getCompoundDrawables() {
if (compoundDrawablesImpl == null) {
return new Drawable[]{null, null, null, null};
}
return new Drawable[]{
compoundDrawablesImpl.leftDrawable,
compoundDrawablesImpl.topDrawable,
compoundDrawablesImpl.rightDrawable,
compoundDrawablesImpl.bottomDrawable
};
}
@Implementation
public void setCompoundDrawablePadding(int compoundDrawablePadding) {
this.compoundDrawablePadding = compoundDrawablePadding;
}
@Implementation
public int getCompoundDrawablePadding() {
return compoundDrawablePadding;
}
@Implementation
public boolean onKeyDown(int keyCode, KeyEvent event) {
previousKeyCodes.add(keyCode);
previousKeyEvents.add(event);
if (onKeyListener != null) {
return onKeyListener.onKey(realView, keyCode, event);
} else {
return false;
}
}
@Implementation
public boolean onKeyUp(int keyCode, KeyEvent event) {
previousKeyCodes.add(keyCode);
previousKeyEvents.add(event);
if (onKeyListener != null) {
return onKeyListener.onKey(realView, keyCode, event);
} else {
return false;
}
}
public int getPreviousKeyCode(int index) {
return previousKeyCodes.get(index);
}
public KeyEvent getPreviousKeyEvent(int index) {
return previousKeyEvents.get(index);
}
@Implementation
public int getGravity() {
return gravity;
}
@Implementation
public void setGravity(int gravity) {
this.gravity = gravity;
}
@Implementation
public int getImeOptions() {
return imeOptions;
}
@Implementation
public void setImeOptions(int imeOptions) {
this.imeOptions = imeOptions;
}
/**
* Returns the text string of this {@code TextView}.
* <p/>
* Robolectric extension.
*/
@Override
public String innerText() {
return (text == null || getVisibility() != VISIBLE) ? "" : text.toString();
}
@Implementation
public void setError(CharSequence error) {
errorText = error;
}
@Implementation
public CharSequence getError() {
return errorText;
}
@Override
@Implementation
public boolean equals(Object o) {
return super.equals(shadowOf_(o));
}
@Override
@Implementation
public int hashCode() {
return super.hashCode();
}
public CompoundDrawables getCompoundDrawablesImpl() {
return compoundDrawablesImpl;
}
void setCompoundDrawablesImpl(CompoundDrawables compoundDrawablesImpl) {
this.compoundDrawablesImpl = compoundDrawablesImpl;
}
public Integer getTextColorHexValue() {
return textColorHexValue;
}
public int getTextAppearanceId() {
return textAppearanceId;
}
public Integer getHintColorHexValue() {
return hintColorHexValue;
}
@Implementation
public float getTextSize() {
return textSize;
}
public boolean isAutoLinkPhoneNumbers() {
return autoLinkPhoneNumbers;
}
private void applyTextAttribute() {
String text = attributeSet.getAttributeValue("android", "text");
if (text != null) {
if (text.startsWith("@string/")) {
int textResId = attributeSet.getAttributeResourceValue("android", "text", 0);
text = context.getResources().getString(textResId);
}
setText(text);
}
}
private void applyTextColorAttribute() {
String colorValue = attributeSet.getAttributeValue("android", "textColor");
if (colorValue != null) {
if (colorValue.startsWith("@color/") || colorValue.startsWith("@android:color/")) {
int colorResId = attributeSet.getAttributeResourceValue("android", "textColor", 0);
setTextColor(context.getResources().getColor(colorResId));
} else if (colorValue.startsWith("#")) {
int colorFromHex = (int) Long.valueOf(colorValue.replaceAll("#", ""), 16).longValue();
setTextColor(colorFromHex);
}
}
}
private void applyHintAttribute() {
String hint = attributeSet.getAttributeValue("android", "hint");
if (hint != null) {
if (hint.startsWith("@string/")) {
int textResId = attributeSet.getAttributeResourceValue("android", "hint", 0);
hint = context.getResources().getString(textResId);
}
setHint(hint);
}
}
private void applyHintColorAttribute() {
String colorValue = attributeSet.getAttributeValue("android", "hintColor");
if (colorValue != null) {
if (colorValue.startsWith("@color/") || colorValue.startsWith("@android:color/")) {
int colorResId = attributeSet.getAttributeResourceValue("android", "hintColor", 0);
setHintTextColor(context.getResources().getColor(colorResId));
} else if (colorValue.startsWith("#")) {
int colorFromHex = (int) Long.valueOf(colorValue.replaceAll("#", ""), 16).longValue();
setHintTextColor(colorFromHex);
}
}
}
private void applyCompoundDrawablesWithIntrinsicBoundsAttributes() {
setCompoundDrawablesWithIntrinsicBounds(
attributeSet.getAttributeResourceValue("android", "drawableLeft", 0),
attributeSet.getAttributeResourceValue("android", "drawableTop", 0),
attributeSet.getAttributeResourceValue("android", "drawableRight", 0),
attributeSet.getAttributeResourceValue("android", "drawableBottom", 0));
}
@Implementation
public void setOnEditorActionListener(android.widget.TextView.OnEditorActionListener onEditorActionListener) {
this.onEditorActionListener = onEditorActionListener;
}
public boolean triggerEditorAction(int imeAction) {
if (onEditorActionListener != null) {
return onEditorActionListener.onEditorAction((TextView) realView, imeAction, null);
}
return false;
}
@Implementation
public void setTransformationMethod(TransformationMethod transformationMethod) {
this.transformationMethod = transformationMethod;
}
@Implementation
public TransformationMethod getTransformationMethod() {
return transformationMethod;
}
@Implementation
public void addTextChangedListener(TextWatcher watcher) {
this.watchers.add(watcher);
}
@Implementation
public void removeTextChangedListener(TextWatcher watcher) {
this.watchers.remove(watcher);
}
@Implementation
public TextPaint getPaint() {
return new TextPaint();
}
@Implementation
public Layout getLayout() {
return this.layout;
}
public void setSelection(int index) {
setSelection(index, index);
}
public void setSelection(int start, int end) {
selectionStart = start;
selectionEnd = end;
}
@Implementation
public int getSelectionStart() {
return selectionStart;
}
@Implementation
public int getSelectionEnd() {
return selectionEnd;
}
@Implementation
public boolean onTouchEvent(MotionEvent event) {
boolean superResult = super.onTouchEvent(event);
if (movementMethod != null) {
boolean handled = movementMethod.onTouchEvent(null, null, event);
if (handled) {
return true;
}
}
return superResult;
}
/**
* @return the list of currently registered watchers/listeners
*/
public List<TextWatcher> getWatchers() {
return watchers;
}
public void setLayout(Layout layout) {
this.layout = layout;
}
public static class CompoundDrawables {
public Drawable leftDrawable;
public Drawable topDrawable;
public Drawable rightDrawable;
public Drawable bottomDrawable;
public CompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom) {
leftDrawable = left;
topDrawable = top;
rightDrawable = right;
bottomDrawable = bottom;
}
public CompoundDrawables(int left, int top, int right, int bottom) {
leftDrawable = left != 0 ? ShadowDrawable.createFromResourceId(left) : null;
topDrawable = top != 0 ? ShadowDrawable.createFromResourceId(top) : null;
rightDrawable = right != 0 ? ShadowDrawable.createFromResourceId(right) : null;
bottomDrawable = bottom != 0 ? ShadowDrawable.createFromResourceId(bottom) : null;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CompoundDrawables that = (CompoundDrawables) o;
if (getBottom() != that.getBottom()) return false;
if (getLeft() != that.getLeft()) return false;
if (getRight() != that.getRight()) return false;
if (getTop() != that.getTop()) return false;
return true;
}
@Override
public int hashCode() {
int result = getLeft();
result = 31 * result + getTop();
result = 31 * result + getRight();
result = 31 * result + getBottom();
return result;
}
@Override
public String toString() {
return "CompoundDrawables{" +
"left=" + getLeft() +
", top=" + getTop() +
", right=" + getRight() +
", bottom=" + getBottom() +
'}';
}
public int getLeft() {
return shadowOf(leftDrawable).getLoadedFromResourceId();
}
public int getTop() {
return shadowOf(topDrawable).getLoadedFromResourceId();
}
public int getRight() {
return shadowOf(rightDrawable).getLoadedFromResourceId();
}
public int getBottom() {
return shadowOf(bottomDrawable).getLoadedFromResourceId();
}
}
}