I have an application that needs to mirror every keystroke made in a certain Fragment that I have. What I am doing so far is attaching a TextWatcher to an EditText and overriding afterTextChanged and pulling the last character (the EditText will be hidden so there can only be 1 character entered at a time).
In order to handle backspaces, I override onKeyDown in the EditText and use a workaround involving a custom BaseInputConnection for Jellybean and above devices (delivers a generated KEYCODE_DEL to onKeyDown - found this solution somewhere on SO).
The issue I am having is that when the EditText is empty, no KEYCODE_DEL events are generated so I have no way of detecting a backspace (even if it wouldn't do anything). What I'm trying to do is add a single character to the EditText when I create it, and when I detect in afterTextChanged that the EditText is empty, so that if I hit backspace before entering another character, it will delete that filler character, and then repopulate it with another filler character.
However, the "filler characters" never get deleted. For example, I populate the EditText with an "a" when I instantiate it. If I press backspace, nothing happens. The "a" is not deleted from the EditText.
Anyone know what's going on here?
State variables
private static volatile boolean backspacePressed = false;
private static volatile boolean ignoreTextChange = false;
The TextWatcher
private TextWatcher textWatcher = new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
if(backspacePressed) {
Logger.i("BKSPC");
backspacePressed = false;
if(et.length() <= 1) {
ignoreTextChange = true;
et.setText("b");
et.setSelection(1);
}
return;
}
if(ignoreTextChange) {
ignoreTextChange = false;
return;
}
else {
Logger.i("" + s.charAt(s.length() - 1));
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
};
The custom EditText
public class InterceptTextView extends EditText {
public InterceptTextView(Context context) {
super(context);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent key) {
if(keyCode == KeyEvent.KEYCODE_DEL && key.getAction() == KeyEvent.ACTION_DOWN) {
Logger.i("BCKSPC");
}
return false;
}
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
outAttrs.actionLabel = null;
outAttrs.inputType = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
BaseInputConnection connection = new BaseInputConnection(this, false) {
@Override
public boolean deleteSurroundingText (int beforeLength, int afterLength) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
/* In Jelly Bean, they don't send key events for delete.
* Instead, they send beforeLength = 1, afterLength = 0.
* So, we'll just simulate what it used to do. */
if (beforeLength == 0 || beforeLength == 1 && afterLength == 0) {
sendDownUpKeyEventForBackwardCompatibility(KeyEvent.KEYCODE_DEL);
backspacePressed = true;
return true;
}
}
return super.deleteSurroundingText(beforeLength, afterLength);
}
private void sendDownUpKeyEventForBackwardCompatibility (final int code) {
final long eventTime = SystemClock.uptimeMillis();
super.sendKeyEvent(new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, code, 0, 0,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
super.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime, KeyEvent.ACTION_UP, code, 0, 0,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
}
};
return connection;
}
}
My Fragment's onCreateView
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
et = new InterceptTextView(getActivity());
et.addTextChangedListener(textWatcher);
ignoreTextChange = true;
et.setText("a");
et.setSelection(1);
return et;
}
Here's how I did it. In my
InputConnectionI overridegetTextBeforeCursorto always to return" "so that the IME always thinks that there's at least one character it can delete.I also set the input type of the connection to
InputType.TYPE_NULLso that key events will be delivered (official workaround from Google).My custom
EditText:My
Fragment: