title: Using custom Editable with EditText makes it glitchy while editing
body: <p><strong>note:</strong> I've created a GitHub repo containing the repro for this bug <a href="https://github.com/jamesqo/Repro_CustomEditableBug" rel="nofollow noreferrer">here</a>. Feel free to clone and try the app out yourself to see the bug. The relevant code is <a href="https://github.com/jamesqo/Repro_CustomEditableBug/blob/master/app/src/main/java/com/bluejay/myapplication/MainActivity.java#L34-L45" rel="nofollow noreferrer">here</a>: with the commented part kept in a comment it works fine, uncomment it and you will experience the bug.</p>
  
  <hr>
  
  <p>I'm building a source code editor app for Android. I have a custom <code>Editable</code> type that wraps <code>SpannableStringBuilder</code> (which will henceforth be referred to as <strong>SSB</strong>). Here is its code:</p>
  
  <pre><code>package com.bluejay.myapplication;
  
  import android.text.Editable;
  import android.text.InputFilter;
  import android.text.SpannableStringBuilder;
  
  public class ColoredText implements Editable {
      private final SpannableStringBuilder builder;
  
      public ColoredText(String rawText) {
          assert rawText != null;
          this.builder = new SpannableStringBuilder(rawText);
      }
  
      @Override
      public Editable replace(int st, int en, CharSequence source, int start, int end) {
          this.builder.replace(st, en, source, start, end);
          return this;
      }
  
      @Override
      public Editable replace(int st, int en, CharSequence text) {
          this.builder.replace(st, en, text);
          return this;
      }
  
      @Override
      public Editable insert(int where, CharSequence text, int start, int end) {
          this.builder.insert(where, text, start, end);
          return this;
      }
  
      @Override
      public Editable insert(int where, CharSequence text) {
          this.builder.insert(where, text);
          return this;
      }
  
      @Override
      public Editable delete(int st, int en) {
          this.builder.delete(st, en);
          return this;
      }
  
      @Override
      public Editable append(CharSequence text) {
          this.builder.append(text);
          return this;
      }
  
      @Override
      public Editable append(CharSequence text, int start, int end) {
          this.builder.append(text, start, end);
          return this;
      }
  
      @Override
      public Editable append(char text) {
          this.builder.append(text);
          return this;
      }
  
      @Override
      public void clear() {
          this.builder.clear();
      }
  
      @Override
      public void clearSpans() {
          this.builder.clearSpans();
      }
  
      @Override
      public void setFilters(InputFilter[] filters) {
          this.builder.setFilters(filters);
      }
  
      @Override
      public InputFilter[] getFilters() {
          return this.builder.getFilters();
      }
  
      @Override
      public void getChars(int start, int end, char[] dest, int destoff) {
          this.builder.getChars(start, end, dest, destoff);
      }
  
      @Override
      public void setSpan(Object what, int start, int end, int flags) {
          this.builder.setSpan(what, start, end, flags);
      }
  
      @Override
      public void removeSpan(Object what) {
          this.builder.removeSpan(what);
      }
  
      @Override
      public &lt;T&gt; T[] getSpans(int start, int end, Class&lt;T&gt; type) {
          return this.builder.getSpans(start, end, type);
      }
  
      @Override
      public int getSpanStart(Object tag) {
          return this.builder.getSpanStart(tag);
      }
  
      @Override
      public int getSpanEnd(Object tag) {
          return this.builder.getSpanEnd(tag);
      }
  
      @Override
      public int getSpanFlags(Object tag) {
          return this.builder.getSpanFlags(tag);
      }
  
      @Override
      public int nextSpanTransition(int start, int limit, Class type) {
          return this.builder.nextSpanTransition(start, limit, type);
      }
  
      @Override
      public int length() {
          return this.builder.length();
      }
  
      @Override
      public char charAt(int index) {
          return this.builder.charAt(index);
      }
  
      @Override
      public CharSequence subSequence(int start, int end) {
          return this.builder.subSequence(start, end);
      }
  }
  </code></pre>
  
  <p>As you can see, this type is a simple wrapper for SSB. <code>new ColoredText(str)</code> creates the underlying SSB from <code>str</code>, and all of its method calls (with the exception of <code>append</code>, <code>delete</code>, etc. which <code>return this</code> instead of the SSB) simply forward to the SSB.</p>
  
  <p>Now when I have an <code>EditText</code> and I try to set the <code>ColoredText</code> as the underlying text of the <code>EditText</code>, like so</p>
  
  <pre><code>EditText editText = (EditText) findViewById(R.id.editText);
  // By default, setText() will attempt to copy the passed CharSequence into a new SSB.
  // See https://github.com/android/platform_frameworks_base/blob/master/core/java/android/widget/TextView.java#L4396
  // and https://github.com/android/platform_frameworks_base/blob/master/core/java/android/text/Editable.java#L143
  // I want to prevent this and have the ColoredText instead of an SSB be the EditText's
  // underlying text, that is, I want the mText member to be of type ColoredText.
  editText.setEditableFactory(new Editable.Factory() {
      @Override
      public Editable newEditable(CharSequence source) {
          return (Editable) source; // source is ColoredText
      }
  });
  
  ColoredText text = new ColoredText("Hello world!\nHello world again!");
  editText.setText(text, TextView.BufferType.EDITABLE);
  </code></pre>
  
  <p>The <code>EditText</code> will behave quite glitchy when edited. In the above example, tap anywhere on the first line with <code>Hello world!</code> and start typing random characters. The second line will be affected, and somehow (even if you don't touch a newline or arrow keys) the cursor will eventually spill over into the second line. And some of the chars you type may not get displayed, even though the cursor will move.</p>
  
  <p>Now if you comment out the <code>setEditableFactory</code> part, so the text is copied into an SSB during <code>setText()</code>, and you run the app again, you will see there are no glitches.</p>
  
  <p>It even works if you leave the <code>setEditableFactory</code> part intact, but replace the variable initialization of <code>text</code> with</p>
  
  <pre><code>SpannableStringBuilder text = new SpannableStringBuilder("Hello world!\nHello world again!");
  </code></pre>
  
  <p>Clearly, although <code>setText()</code> says it'll accept any <code>Editable</code>, it doesn't work well when dealing with anything other than an SSB. Why does this happen and how can I fix it? Thanks.</p>