title: Android Intercept Soft Keystrokes From My Own Application - Backspace Issue
body: <p>I have an application that needs to mirror every keystroke made in a certain <code>Fragment</code> that I have. What I am doing so far is attaching a <code>TextWatcher</code> to an <code>EditText</code> and overriding <code>afterTextChanged</code> and pulling the last character (the <code>EditText</code> will be hidden so there can only be 1 character entered at a time).</p>
  
  <p>In order to handle backspaces, I override <code>onKeyDown</code> in the <code>EditText</code> and use a workaround involving a custom <code>BaseInputConnection</code> for Jellybean and above devices (delivers a generated  <code>KEYCODE_DEL</code> to <code>onKeyDown</code> - found this solution somewhere on SO). </p>
  
  <p>The issue I am having is that when the <code>EditText</code> is empty, no <code>KEYCODE_DEL</code> 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 <code>EditText</code> when I create it, and when I detect in <code>afterTextChanged</code> that the <code>EditText</code> 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.</p>
  
  <p>However, the "filler characters" never get deleted. For example, I populate the <code>EditText</code> with an "a" when I instantiate it. If I press backspace, nothing happens. The "a" is not deleted from the <code>EditText</code>. </p>
  
  <p>Anyone know what's going on here?</p>
  
  <p>State variables</p>
  
  <pre><code>private static volatile boolean backspacePressed = false;
  private static volatile boolean ignoreTextChange = false;
  </code></pre>
  
  <p>The <code>TextWatcher</code></p>
  
  <pre><code>private TextWatcher textWatcher = new TextWatcher() {       
      @Override
      public void afterTextChanged(Editable s) {
          if(backspacePressed) {
              Logger.i("BKSPC");
              backspacePressed = false;
              if(et.length() &lt;= 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) {}
  };  
  </code></pre>
  
  <p>The custom <code>EditText</code></p>
  
  <pre><code>public class InterceptTextView extends EditText {
  
      public InterceptTextView(Context context) {
          super(context);
      }
  
      @Override
      public boolean onKeyDown(int keyCode, KeyEvent key) {
          if(keyCode == KeyEvent.KEYCODE_DEL &amp;&amp; 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 &gt;= 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 &amp;&amp; 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;
      }
  
  }
  </code></pre>
  
  <p>My <code>Fragment</code>'s <code>onCreateView</code></p>
  
  <pre><code>@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;
  }
  </code></pre>