blob: 139b1b505bc69abdfb7164416514212cc068f42f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 com.taobao.weex.ui.component;
import android.app.Activity;
import android.content.Context;
import android.support.annotation.NonNull;
import android.text.Editable;
import android.text.InputFilter;
import android.text.InputType;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.method.PasswordTransformationMethod;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;
import com.alibaba.fastjson.JSONObject;
import com.taobao.weex.WXSDKInstance;
import com.taobao.weex.WXSDKManager;
import com.taobao.weex.annotation.JSMethod;
import com.taobao.weex.bridge.WXBridgeManager;
import com.taobao.weex.common.Constants;
import com.taobao.weex.common.WXThread;
import com.taobao.weex.dom.CSSConstants;
import com.taobao.weex.dom.WXStyle;
import com.taobao.weex.layout.ContentBoxMeasurement;
import com.taobao.weex.layout.MeasureMode;
import com.taobao.weex.layout.MeasureSize;
import com.taobao.weex.ui.action.BasicComponentData;
import com.taobao.weex.ui.component.helper.SoftKeyboardDetector;
import com.taobao.weex.ui.component.helper.WXTimeInputHelper;
import com.taobao.weex.ui.view.WXEditText;
import com.taobao.weex.utils.TypefaceUtil;
import com.taobao.weex.utils.WXLogUtils;
import com.taobao.weex.utils.WXResourceUtils;
import com.taobao.weex.utils.WXUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import static com.taobao.weex.dom.WXStyle.UNSET;
/**
* Created by sospartan on 7/11/16.
*/
public abstract class AbstractEditComponent extends WXComponent<WXEditText> {
private final InputMethodManager mInputMethodManager;
private String mBeforeText = "";
private boolean mAutoFocus;
private String mType = "text";
private String mMax = null;
private String mMin = null;
private String mLastValue = "";
private int mEditorAction = EditorInfo.IME_ACTION_DONE;
private String mReturnKeyType = null;
private List<TextView.OnEditorActionListener> mEditorActionListeners;
private boolean mListeningKeyboard = false;
private SoftKeyboardDetector.Unregister mUnregister;
private boolean mIgnoreNextOnInputEvent = false;
private boolean mKeepSelectionIndex = false;
private TextFormatter mFormatter = null;
private List<TextWatcher> mTextChangedListeners;
private TextWatcher mTextChangedEventDispatcher;
private int mFormatRepeatCount = 0;
private static final int MAX_TEXT_FORMAT_REPEAT = 3;
private TextPaint mPaint = new TextPaint();
private int mLineHeight = UNSET;
public AbstractEditComponent(WXSDKInstance instance, WXVContainer parent, boolean isLazy, BasicComponentData basicComponentData) {
super(instance, parent, isLazy, basicComponentData);
mInputMethodManager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
setContentBoxMeasurement(new ContentBoxMeasurement() {
/** uiThread = false **/
@Override
public void measureInternal(float width, float height, int widthMeasureMode, int heightMeasureMode) {
if (CSSConstants.isUndefined(width) || widthMeasureMode == MeasureMode.UNSPECIFIED) {
width = 0;
}
mMeasureWidth = width;
mMeasureHeight = getMeasureHeight();
}
/** uiThread = false **/
@Override
public void layoutBefore() {
updateStyleAndAttrs();
}
/** uiThread = false **/
@Override
public void layoutAfter(float computedWidth, float computedHeight) {
}
});
}
protected final float getMeasuredLineHeight() {
return mLineHeight != UNSET && mLineHeight > 0 ? mLineHeight : mPaint.getFontMetrics(null);
}
protected float getMeasureHeight() {
return getMeasuredLineHeight();
}
protected void updateStyleAndAttrs() {
if (getStyles().size() > 0) {
int fontSize = UNSET, fontStyle = UNSET, fontWeight = UNSET;
String fontFamily = null;
if (getStyles().containsKey(Constants.Name.FONT_SIZE)) {
fontSize = WXStyle.getFontSize(getStyles(),getViewPortWidth());
}
if (getStyles().containsKey(Constants.Name.FONT_FAMILY)) {
fontFamily = WXStyle.getFontFamily(getStyles());
}
if (getStyles().containsKey(Constants.Name.FONT_STYLE)) {
fontStyle = WXStyle.getFontStyle(getStyles());
}
if (getStyles().containsKey(Constants.Name.FONT_WEIGHT)) {
fontWeight = WXStyle.getFontWeight(getStyles());
}
int lineHeight = WXStyle.getLineHeight(getStyles(),getViewPortWidth());
if (lineHeight != UNSET)
mLineHeight = lineHeight;
if (fontSize != UNSET)
mPaint.setTextSize(fontSize);
if (fontFamily != null) {
TypefaceUtil.applyFontStyle(mPaint, fontStyle, fontWeight, fontFamily);
}
}
}
@Override
protected WXEditText initComponentHostView(@NonNull Context context) {
final WXEditText inputView = new WXEditText(context);
appleStyleAfterCreated(inputView);
return inputView;
}
@Override
protected void onHostViewInitialized(WXEditText host) {
super.onHostViewInitialized(host);
addFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(boolean hasFocus) {
if (!hasFocus) {
decideSoftKeyboard();
}
setPseudoClassStatus(Constants.PSEUDO.FOCUS,hasFocus);
}
});
addKeyboardListener(host);
}
@Override
protected boolean isConsumeTouch() {
//EditText always consume touch event except disabled.
return !isDisabled();
}
private OnClickListener mOnClickListener = new OnClickListener() {
@Override
public void onHostViewClick() {
switch (mType) {
case Constants.Value.DATE:
hideSoftKeyboard();
if (getParent() != null) {
getParent().interceptFocus();
}
WXTimeInputHelper.pickDate(mMax, mMin, AbstractEditComponent.this);
break;
case Constants.Value.TIME:
hideSoftKeyboard();
if (getParent() != null) {
getParent().interceptFocus();
}
WXTimeInputHelper.pickTime(AbstractEditComponent.this);
break;
}
}
};
private void applyOnClickListener() {
addClickListener(mOnClickListener);
}
protected int getVerticalGravity(){
return Gravity.CENTER_VERTICAL;
}
/**
* Process view after created.
*
* @param editText
*/
protected void appleStyleAfterCreated(final WXEditText editText) {
String alignStr = (String) getStyles().get(Constants.Name.TEXT_ALIGN);
int textAlign = getTextAlign(alignStr);
if (textAlign <= 0) {
textAlign = Gravity.START;
}
editText.setGravity(textAlign | getVerticalGravity());
final int colorInt = WXResourceUtils.getColor("#999999");
if (colorInt != Integer.MIN_VALUE) {
editText.setHintTextColor(colorInt);
}
mTextChangedEventDispatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if (mTextChangedListeners != null) {
for (TextWatcher watcher : mTextChangedListeners) {
watcher.beforeTextChanged(s, start, count, after);
}
}
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (mFormatter != null) {
String raw = mFormatter.recover(s.toString());
String result = mFormatter.format(raw);
// prevent infinite loop caused by bad format and recover regexp
if (!result.equals(s.toString()) && mFormatRepeatCount < MAX_TEXT_FORMAT_REPEAT) {
mFormatRepeatCount = mFormatRepeatCount + 1;
int index = editText.getSelectionStart();
int cursorIndex = mFormatter.format(mFormatter.recover(s.subSequence(0, index).toString())).length();
editText.setText(result);
editText.setSelection(cursorIndex);
return;
}
mFormatRepeatCount = 0;
}
if (mTextChangedListeners != null) {
for (TextWatcher watcher : mTextChangedListeners) {
watcher.onTextChanged(s, start, before, count);
}
}
}
@Override
public void afterTextChanged(Editable s) {
if (mTextChangedListeners != null) {
for (TextWatcher watcher : mTextChangedListeners) {
watcher.afterTextChanged(s);
}
}
}
};
editText.addTextChangedListener(mTextChangedEventDispatcher);
editText.setTextSize(TypedValue.COMPLEX_UNIT_PX, WXStyle.getFontSize(getStyles(), getInstance().getInstanceViewPortWidth()));
}
@Override
public void addEvent(final String type) {
super.addEvent(type);
if (getHostView() == null || TextUtils.isEmpty(type)) {
return;
}
final TextView text = getHostView();
if (type.equals(Constants.Event.CHANGE)) {
addFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(boolean hasFocus) {
if (hasFocus) {
mLastValue = text.getText().toString();
} else {
CharSequence newValue = text.getText();
newValue = newValue == null ? "" : newValue;
if (!newValue.toString().equals(mLastValue)) {
fireEvent(Constants.Event.CHANGE, newValue.toString());
mLastValue = text.getText().toString();
}
}
}
});
addEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == mEditorAction) {
CharSequence newValue = text.getText();
newValue = newValue == null ? "" : newValue;
if (!newValue.toString().equals(mLastValue)) {
fireEvent(Constants.Event.CHANGE, newValue.toString());
mLastValue = text.getText().toString();
}
if (getParent() != null) {
getParent().interceptFocus();
}
hideSoftKeyboard();
return true;
}
return false;
}
});
} else if (type.equals(Constants.Event.INPUT)) {
addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (mIgnoreNextOnInputEvent) {
mIgnoreNextOnInputEvent = false;
return;
}
if (mBeforeText.equals(s.toString())) {
return;
}
mBeforeText = s.toString();
fireEvent(Constants.Event.INPUT, s.toString());
}
@Override
public void afterTextChanged(Editable s) {
}
});
}
if (Constants.Event.RETURN.equals(type)) {
addEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == mEditorAction) {
Map<String, Object> ret = new HashMap<>(2);
ret.put("returnKeyType", mReturnKeyType);
ret.put("value", v.getText().toString());
fireEvent(Constants.Event.RETURN, ret);
return true;
}
return false;
}
});
}
if (Constants.Event.KEYBOARD.equals(type)) {
mListeningKeyboard = true;
}
}
private void fireEvent(String event, String value) {
if (event != null) {
Map<String, Object> params = new HashMap<>(2);
params.put("value", value);
params.put("timeStamp", System.currentTimeMillis());
Map<String, Object> domChanges = new HashMap<>();
Map<String, Object> attrsChanges = new HashMap<>();
attrsChanges.put("value", value);
domChanges.put("attrs", attrsChanges);
WXSDKManager.getInstance().fireEvent(getInstanceId(), getRef(), event, params, domChanges);
}
}
public void performOnChange(String value) {
if (getEvents() != null) {
String event = getEvents().contains(Constants.Event.CHANGE) ? Constants.Event.CHANGE : null;
fireEvent(event, value);
}
}
@Override
protected boolean setProperty(String key, Object param) {
switch (key) {
case Constants.Name.PLACEHOLDER:
String placeholder = WXUtils.getString(param, null);
if (placeholder != null)
setPlaceholder(placeholder);
return true;
case Constants.Name.PLACEHOLDER_COLOR:
String placeholder_color = WXUtils.getString(param, null);
if (placeholder_color != null)
setPlaceholderColor(placeholder_color);
return true;
case Constants.Name.TYPE:
String input_type = WXUtils.getString(param, null);
if (input_type != null)
setType(input_type);
return true;
case Constants.Name.AUTOFOCUS:
Boolean result = WXUtils.getBoolean(param, null);
if (result != null)
setAutofocus(result);
return true;
case Constants.Name.COLOR:
String color = WXUtils.getString(param, null);
if (color != null)
setColor(color);
return true;
case Constants.Name.FONT_SIZE:
String fontsize = WXUtils.getString(param, null);
if (fontsize != null)
setFontSize(fontsize);
return true;
case Constants.Name.TEXT_ALIGN:
String text_align = WXUtils.getString(param, null);
if (text_align != null)
setTextAlign(text_align);
return true;
case Constants.Name.SINGLELINE:
Boolean singLineResult = WXUtils.getBoolean(param, null);
if (singLineResult != null)
setSingleLine(singLineResult);
return true;
case Constants.Name.LINES:
Integer lines = WXUtils.getInteger(param, null);
if (lines != null)
setLines(lines);
return true;
case Constants.Name.MAX_LENGTH:
Integer maxlength = WXUtils.getInteger(param, null);
if (maxlength != null)
setMaxLength(maxlength);
return true;
case Constants.Name.MAXLENGTH:
Integer maxLength = WXUtils.getInteger(param, null);
if (maxLength != null)
setMaxLength(maxLength);
return true;
case Constants.Name.MAX:
setMax(String.valueOf(param));
return true;
case Constants.Name.MIN:
setMin(String.valueOf(param));
return true;
case Constants.Name.RETURN_KEY_TYPE:
setReturnKeyType(String.valueOf(param));
return true;
case Constants.Name.KEEP_SELECTION_INDEX:
boolean keepIndex = WXUtils.getBoolean(param, false);
mKeepSelectionIndex = keepIndex;
return true;
case Constants.Name.ALLOW_COPY_PASTE:
boolean allowCopyPaste = WXUtils.getBoolean(param, true);
if (getHostView() != null) {
getHostView().setAllowCopyPaste(allowCopyPaste);
}
return true;
}
return super.setProperty(key, param);
}
@WXComponentProp(name = Constants.Name.RETURN_KEY_TYPE)
public void setReturnKeyType(String type) {
if (getHostView() == null) {
return;
}
mReturnKeyType = type;
switch (type) {
case ReturnTypes.DEFAULT:
mEditorAction = EditorInfo.IME_ACTION_UNSPECIFIED;
break;
case ReturnTypes.GO:
mEditorAction = EditorInfo.IME_ACTION_GO;
break;
case ReturnTypes.NEXT:
mEditorAction = EditorInfo.IME_ACTION_NEXT;
break;
case ReturnTypes.SEARCH:
mEditorAction = EditorInfo.IME_ACTION_SEARCH;
break;
case ReturnTypes.SEND:
mEditorAction = EditorInfo.IME_ACTION_SEND;
break;
case ReturnTypes.DONE:
mEditorAction = EditorInfo.IME_ACTION_DONE;
break;
default:
break;
}
//remove focus and hide keyboard first, the ImeOptions will take effect when show keyboard next time
blur();
getHostView().setImeOptions(mEditorAction);
}
@WXComponentProp(name = Constants.Name.PLACEHOLDER)
public void setPlaceholder(String placeholder) {
if (placeholder == null || getHostView() == null) {
return;
}
((WXEditText) getHostView()).setHint(placeholder);
}
@WXComponentProp(name = Constants.Name.PLACEHOLDER_COLOR)
public void setPlaceholderColor(String color) {
if (getHostView() != null && !TextUtils.isEmpty(color)) {
int colorInt = WXResourceUtils.getColor(color);
if (colorInt != Integer.MIN_VALUE) {
((WXEditText) getHostView()).setHintTextColor(colorInt);
}
}
}
@WXComponentProp(name = Constants.Name.TYPE)
public void setType(String type) {
Log.e("weex", "setType=" + type);
if (type == null || getHostView() == null) {
return;
}
mType = type;
((EditText) getHostView()).setRawInputType(getInputType(mType));
switch (mType) {
case Constants.Value.DATE:
case Constants.Value.TIME:
applyOnClickListener();
break;
}
}
@WXComponentProp(name = Constants.Name.AUTOFOCUS)
public void setAutofocus(boolean autofocus) {
if (getHostView() == null) {
return;
}
mAutoFocus = autofocus;
EditText inputView = getHostView();
if (mAutoFocus) {
inputView.setFocusable(true);
inputView.requestFocus();
inputView.setFocusableInTouchMode(true);
showSoftKeyboard();
} else {
hideSoftKeyboard();
}
}
@WXComponentProp(name = Constants.Name.VALUE)
public void setValue(String value) {
WXEditText view;
if ((view = getHostView()) == null) {
return;
}
mIgnoreNextOnInputEvent = true;
int oldIndex = view.getSelectionStart();
view.setText(value);
int index = mKeepSelectionIndex ? oldIndex : value.length();
view.setSelection(value == null ? 0 : index);
}
@WXComponentProp(name = Constants.Name.COLOR)
public void setColor(String color) {
if (getHostView() != null && !TextUtils.isEmpty(color)) {
int colorInt = WXResourceUtils.getColor(color);
if (colorInt != Integer.MIN_VALUE) {
getHostView().setTextColor(colorInt);
}
}
}
@WXComponentProp(name = Constants.Name.FONT_SIZE)
public void setFontSize(String fontSize) {
if (getHostView() != null && fontSize != null ) {
Map<String, Object> map = new HashMap<>(1);
map.put(Constants.Name.FONT_SIZE, fontSize);
getHostView().setTextSize(TypedValue.COMPLEX_UNIT_PX, WXStyle.getFontSize(map, getInstance().getInstanceViewPortWidth()));
}
}
@WXComponentProp(name = Constants.Name.TEXT_ALIGN)
public void setTextAlign(String textAlign) {
int align = getTextAlign(textAlign);
if (align > 0) {
getHostView().setGravity(align | getVerticalGravity());
}
}
@WXComponentProp(name = Constants.Name.SINGLELINE)
public void setSingleLine(boolean singleLine) {
if (getHostView() == null) {
return;
}
getHostView().setSingleLine(singleLine);
}
@WXComponentProp(name = Constants.Name.LINES)
public void setLines(int lines) {
if (getHostView() == null) {
return;
}
getHostView().setLines(lines);
}
/**
* Compatible with both 'max-length' and 'maxlength'
* @param maxLength
*/
@WXComponentProp(name = Constants.Name.MAX_LENGTH)
public void setMaxLength(int maxLength) {
if (getHostView() == null) {
return;
}
getHostView().setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)});
}
/**
* Compatible with both 'max-length' and 'maxlength'
* @param maxLength
*/
@WXComponentProp(name = Constants.Name.MAXLENGTH)
@Deprecated
public void setMaxlength(int maxLength) {
setMaxLength(maxLength);
}
private int getInputType(String type) {
int inputType;
switch (type) {
case Constants.Value.TEXT:
inputType = InputType.TYPE_CLASS_TEXT;
break;
case Constants.Value.DATE:
inputType = InputType.TYPE_NULL;
getHostView().setFocusable(false);
break;
case Constants.Value.DATETIME:
inputType = InputType.TYPE_CLASS_DATETIME;
break;
case Constants.Value.EMAIL:
inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
break;
case Constants.Value.PASSWORD:
inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD;
getHostView().setTransformationMethod(PasswordTransformationMethod.getInstance());
break;
case Constants.Value.TEL:
inputType = InputType.TYPE_CLASS_PHONE;
break;
case Constants.Value.TIME:
inputType = InputType.TYPE_NULL;
getHostView().setFocusable(false);
break;
case Constants.Value.URL:
inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI;
break;
case Constants.Value.NUMBER:
inputType = InputType.TYPE_CLASS_NUMBER;
break;
default:
inputType = InputType.TYPE_CLASS_TEXT;
}
return inputType;
}
@WXComponentProp(name = Constants.Name.MAX)
public void setMax(String max) {
mMax = max;
}
@WXComponentProp(name = Constants.Name.MIN)
public void setMin(String min) {
mMin = min;
}
private boolean showSoftKeyboard() {
if (getHostView() == null) {
return false;
} else {
getHostView().postDelayed(WXThread.secure(new Runnable() {
@Override
public void run() {
mInputMethodManager.showSoftInput(getHostView(), InputMethodManager.SHOW_IMPLICIT);
}
}), 100);
}
return true;
}
private void hideSoftKeyboard() {
if (getHostView() != null) {
getHostView().postDelayed(WXThread.secure(new Runnable() {
@Override
public void run() {
mInputMethodManager.hideSoftInputFromWindow(getHostView().getWindowToken(), 0);
}
}), 16);
}
}
private int getTextAlign(String textAlign) {
int align = Gravity.START;
if (TextUtils.isEmpty(textAlign)) {
return align;
}
if (textAlign.equals(Constants.Value.LEFT)) {
align = Gravity.START;
} else if (textAlign.equals(Constants.Value.CENTER)) {
align = Gravity.CENTER;
} else if (textAlign.equals(Constants.Value.RIGHT)) {
align = Gravity.END;
}
return align;
}
@JSMethod
public void blur() {
WXEditText host = getHostView();
if (host != null && host.hasFocus()) {
if (getParent() != null) {
getParent().interceptFocus();
}
host.clearFocus();
hideSoftKeyboard();
}
}
@JSMethod
public void focus() {
WXEditText host = getHostView();
if (host != null && !host.hasFocus()) {
if (getParent() != null) {
getParent().ignoreFocus();
}
host.requestFocus();
host.setFocusable(true);
host.setFocusableInTouchMode(true);
showSoftKeyboard();
}
}
@Override
protected Object convertEmptyProperty(String propName, Object originalValue) {
switch (propName) {
case Constants.Name.FONT_SIZE:
return WXText.sDEFAULT_SIZE;
case Constants.Name.COLOR:
return "black";
}
return super.convertEmptyProperty(propName, originalValue);
}
private void decideSoftKeyboard() {
View hostView;
if ((hostView = getHostView()) != null) {
final Context context = getContext();
if (context != null && context instanceof Activity) {
hostView.postDelayed(WXThread.secure(new Runnable() {
@Override
public void run() {
View currentFocus = ((Activity) context).getCurrentFocus();
if (!(currentFocus instanceof EditText)) {
mInputMethodManager.hideSoftInputFromWindow(getHostView().getWindowToken(), 0);
}
}
}), 16);
}
}
}
@JSMethod
public void setSelectionRange(int selectionStart, int selectionEnd) {
EditText hostView;
if ((hostView = getHostView()) != null) {
int length = getHostView().length();
if (selectionStart > length || selectionEnd > length) {
return;
}
focus();
hostView.setSelection(selectionStart, selectionEnd);
}
}
@JSMethod
public void getSelectionRange(String callbackId) {
EditText hostView;
Map<String, Object> result = new HashMap<>(2);
if ((hostView = getHostView()) != null) {
int start = hostView.getSelectionStart();
int end = hostView.getSelectionEnd();
if (!hostView.hasFocus()) {
//The default behavior, same as iOS and web
start = 0;
end = 0;
}
result.put(Constants.Name.SELECTION_START, start);
result.put(Constants.Name.SELECTION_END, end);
}
WXBridgeManager.getInstance().callback(getInstanceId(), callbackId, result, false);
}
@JSMethod
public void setTextFormatter(JSONObject params) {
try {
String formatRule = params.getString("formatRule");
String formatReplace = params.getString("formatReplace");
String recoverRule = params.getString("recoverRule");
String recoverReplace = params.getString("recoverReplace");
PatternWrapper format = parseToPattern(formatRule, formatReplace);
PatternWrapper recover = parseToPattern(recoverRule, recoverReplace);
if (format != null && recover != null) {
mFormatter = new TextFormatter(format, recover);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
protected final void addEditorActionListener(TextView.OnEditorActionListener listener) {
TextView view;
if (listener != null && (view = getHostView()) != null) {
if (mEditorActionListeners == null) {
mEditorActionListeners = new ArrayList<>();
view.setOnEditorActionListener(new TextView.OnEditorActionListener() {
private boolean handled = true;
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
for (TextView.OnEditorActionListener l : mEditorActionListeners) {
if (l != null) {
handled = handled & l.onEditorAction(v, actionId, event);
}
}
return handled;
}
});
}
mEditorActionListeners.add(listener);
}
}
public final void addTextChangedListener(TextWatcher watcher) {
if (mTextChangedListeners == null) {
mTextChangedListeners = new ArrayList<>();
}
mTextChangedListeners.add(watcher);
}
private void addKeyboardListener(final WXEditText host) {
if (host == null) {
return;
}
Context context = host.getContext();
if (context != null && context instanceof Activity) {
SoftKeyboardDetector.registerKeyboardEventListener((Activity) context, new SoftKeyboardDetector.OnKeyboardEventListener() {
@Override
public void onKeyboardEvent(boolean isShown) {
if (mListeningKeyboard) {
Map<String, Object> event = new HashMap<>(1);
event.put("isShow", isShown);
fireEvent(Constants.Event.KEYBOARD, event);
}
if (!isShown) {
blur();
}
}
});
}
}
@Override
public void destroy() {
super.destroy();
if (mUnregister != null) {
try {
mUnregister.execute();
mUnregister = null;
} catch (Throwable throwable) {
WXLogUtils.w("Unregister throw ", throwable);
}
}
}
private PatternWrapper parseToPattern(String jsPattern, String replace) {
if (jsPattern == null || replace == null) {
return null;
}
String checker = "/[\\S]+/[i]?[m]?[g]?";
if (!Pattern.compile(checker).matcher(jsPattern).matches()) {
WXLogUtils.w("WXInput", "Illegal js pattern syntax: " + jsPattern);
return null;
}
int flags = 0;
boolean global = false;
String flagsStr = jsPattern.substring(jsPattern.lastIndexOf("/") + 1);
String regExp = jsPattern.substring(jsPattern.indexOf("/") + 1, jsPattern.lastIndexOf("/"));
if (flagsStr.contains("i")) {
flags |= Pattern.CASE_INSENSITIVE;
}
if (flagsStr.contains("m")) {
flags |= Pattern.DOTALL;
}
if (flagsStr.contains("g")) {
global = true;
}
Pattern pattern = null;
try {
pattern = Pattern.compile(regExp, flags);
} catch (PatternSyntaxException e) {
WXLogUtils.w("WXInput", "Pattern syntax error: " + regExp);
}
if (pattern == null) {
return null;
}
PatternWrapper wrapper = new PatternWrapper();
wrapper.global = global;
wrapper.matcher = pattern;
wrapper.replace = replace;
return wrapper;
}
private interface ReturnTypes {
String DEFAULT = "default";
String GO = "go";
String NEXT = "next";
String SEARCH = "search";
String SEND = "send";
String DONE = "done";
}
private static class PatternWrapper {
private boolean global = false;
private Pattern matcher;
private String replace;
}
private static class TextFormatter {
private PatternWrapper format;
private PatternWrapper recover;
private TextFormatter(PatternWrapper format, PatternWrapper recover) {
this.format = format;
this.recover = recover;
}
private String format(String src) {
try {
if (format != null) {
if (format.global) {
return format.matcher.matcher(src).replaceAll(format.replace);
} else {
return format.matcher.matcher(src).replaceFirst(format.replace);
}
}
} catch (Throwable t) {
//maybe IndexOutOfBoundsException caused by illegal replace
WXLogUtils.w("WXInput", "[format] " + t.getMessage());
}
return src;
}
private String recover(String formatted) {
try {
if (recover != null) {
if (recover.global) {
return recover.matcher.matcher(formatted).replaceAll(recover.replace);
} else {
return recover.matcher.matcher(formatted).replaceFirst(recover.replace);
}
}
} catch (Throwable t) {
//same cause as format
WXLogUtils.w("WXInput", "[formatted] " + t.getMessage());
}
return formatted;
}
}
}