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
InputConnection
I overridegetTextBeforeCursor
to 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_NULL
so that key events will be delivered (official workaround from Google).My custom
EditText
:My
Fragment
: