package com.andreabaccega.widget;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.widget.AutoCompleteTextView;
import android.widget.EditText;
/**
* AutoCompleteTextView Extension to be used in order to create forms in android.
*
* @author Said Tahsin Dane <tasomaniac@gmail.com>
*/
public class FormAutoCompleteTextView extends AutoCompleteTextView {
public FormAutoCompleteTextView(Context context) {
super(context);
// FIXME how should this constructor be handled
throw new RuntimeException("Not supported");
}
public FormAutoCompleteTextView(Context context, AttributeSet attrs) {
super(context, attrs);
editTextValidator = new DefaultEditTextValidator(this, attrs, context);
}
public FormAutoCompleteTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
editTextValidator = new DefaultEditTextValidator(this, attrs, context);
}
/**
* Add a validator to this AutoCompleteTextView. The validator will be added in the
* queue of the current validators.
*
* @param theValidator
* @throws IllegalArgumentException
* if the validator is null
*/
public void addValidator(com.andreabaccega.formedittextvalidator.Validator theValidator) throws IllegalArgumentException {
editTextValidator.addValidator(theValidator);
}
public EditTextValidator getEditTextValidator() {
return editTextValidator;
}
public void setEditTextValidator(EditTextValidator editTextValidator) {
this.editTextValidator = editTextValidator;
}
/**
* Calling *testValidity()* will cause the AutoCompleteTextView to go through
* customValidators and call {@link com.andreabaccega.formedittextvalidator.Validator#isValid(android.widget.EditText)}
*
* @return true if the validity passes false otherwise.
*/
public boolean testValidity() {
return editTextValidator.testValidity();
}
private EditTextValidator editTextValidator;
/**
* Keep track of which icon we used last
*/
private Drawable lastErrorIcon = null;
/**
* Don't send delete key so edit text doesn't capture it and close error
*/
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
if (TextUtils.isEmpty(getText().toString())
&& keyCode == KeyEvent.KEYCODE_DEL)
return true;
else
return super.onKeyPreIme(keyCode, event);
}
/**
* Resolve an issue where the error icon is hidden under some cases in JB
* due to a bug http://code.google.com/p/android/issues/detail?id=40417
*/
@Override
public void setError(CharSequence error, Drawable icon) {
super.setError(error, icon);
lastErrorIcon = icon;
// if the error is not null, and we are in JB, force
// the error to show
if (error != null /* !isFocused() && */) {
showErrorIconHax(icon);
}
}
/**
* In onFocusChanged() we also have to reshow the error icon as the Editor
* hides it. Because Editor is a hidden class we need to cache the last used
* icon and use that
*/
@Override
protected void onFocusChanged(boolean focused, int direction,
Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
showErrorIconHax(lastErrorIcon);
}
/**
* Use reflection to force the error icon to show. Dirty but resolves the
* issue in 4.2
*/
private void showErrorIconHax(Drawable icon) {
if (icon == null)
return;
// only for JB 4.2 and 4.2.1
if (Build.VERSION.SDK_INT != Build.VERSION_CODES.JELLY_BEAN
&& Build.VERSION.SDK_INT != Build.VERSION_CODES.JELLY_BEAN_MR1)
return;
try {
Class<?> textview = Class.forName("android.widget.TextView");
Field tEditor = textview.getDeclaredField("mEditor");
tEditor.setAccessible(true);
Class<?> editor = Class.forName("android.widget.Editor");
Method privateShowError = editor.getDeclaredMethod("setErrorIcon",
Drawable.class);
privateShowError.setAccessible(true);
privateShowError.invoke(tEditor.get(this), icon);
} catch (Exception e) {
// e.printStackTrace(); // oh well, we tried
}
}
}