private void press(Editable content, Object what) {
    int state = content.getSpanFlags(what);

    if (state == PRESSED) ; // repeat before use
    else if (state == RELEASED) content.setSpan(what, 0, 0, LOCKED);
    else if (state == USED) ; // repeat after use
    else if (state == LOCKED) content.removeSpan(what);
    else content.setSpan(what, 0, 0, PRESSED);
  }
 private static boolean hasCompositionString(Editable content) {
   Object[] spans = content.getSpans(0, content.length(), Object.class);
   if (spans != null) {
     for (Object span : spans) {
       if ((content.getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) {
         // Found composition string.
         return true;
       }
     }
   }
   return false;
 }
  @TestTargetNew(
      level = TestLevel.COMPLETE,
      method = "clearMetaKeyState",
      args = {android.text.Editable.class, int.class})
  public void testClearMetaKeyState2() {
    CharSequence str = "123456";
    Editable text = Editable.Factory.getInstance().newEditable(str);
    text.setSpan(Selection.SELECTION_START, 0, 0, Spanned.SPAN_POINT_POINT);
    text.setSpan(Selection.SELECTION_END, str.length(), str.length(), Spanned.SPAN_POINT_POINT);
    MetaKeyKeyListener.clearMetaKeyState(text, MetaKeyKeyListener.META_SHIFT_ON);
    assertEquals(Spanned.SPAN_POINT_POINT, text.getSpanFlags(Selection.SELECTION_START));
    assertEquals(Spanned.SPAN_POINT_POINT, text.getSpanFlags(Selection.SELECTION_END));

    str = "abc";
    text = Editable.Factory.getInstance().newEditable(str);
    text.setSpan(Selection.SELECTION_START, 0, 0, Spanned.SPAN_POINT_POINT);
    text.setSpan(Selection.SELECTION_END, str.length(), str.length(), Spanned.SPAN_POINT_POINT);
    MetaKeyKeyListener.clearMetaKeyState(text, MetaKeyKeyListener.META_ALT_ON);
    assertEquals(Spanned.SPAN_POINT_POINT, text.getSpanFlags(Selection.SELECTION_START));
    assertEquals(Spanned.SPAN_POINT_POINT, text.getSpanFlags(Selection.SELECTION_END));

    str = "#@%#$^%^";
    text = Editable.Factory.getInstance().newEditable(str);
    text.setSpan(Selection.SELECTION_START, 0, 0, Spanned.SPAN_POINT_POINT);
    text.setSpan(Selection.SELECTION_END, str.length(), str.length(), Spanned.SPAN_POINT_POINT);
    MetaKeyKeyListener.clearMetaKeyState(text, MetaKeyKeyListener.META_SYM_ON);
    assertEquals(Spanned.SPAN_POINT_POINT, text.getSpanFlags(Selection.SELECTION_START));
    assertEquals(Spanned.SPAN_POINT_POINT, text.getSpanFlags(Selection.SELECTION_END));
  }
 private Object getLast(Editable text, Class kind) {
   Object[] objs = text.getSpans(0, text.length(), kind);
   if (objs.length == 0) {
     return null;
   } else {
     for (int i = objs.length; i > 0; i--) {
       if (text.getSpanFlags(objs[i - 1]) == Spannable.SPAN_MARK_MARK) {
         return objs[i - 1];
       }
     }
     return null;
   }
 }
  private void release(Editable content, Object what) {
    int current = content.getSpanFlags(what);

    if (current == USED) content.removeSpan(what);
    else if (current == PRESSED) content.setSpan(what, 0, 0, RELEASED);
  }
  public boolean onKeyDown(View view, Editable content, int keyCode, KeyEvent event) {
    int selStart, selEnd;
    int pref = 0;

    if (view != null) {
      pref = TextKeyListener.getInstance().getPrefs(view.getContext());
    }

    {
      int a = Selection.getSelectionStart(content);
      int b = Selection.getSelectionEnd(content);

      selStart = Math.min(a, b);
      selEnd = Math.max(a, b);
    }

    int activeStart = content.getSpanStart(TextKeyListener.ACTIVE);
    int activeEnd = content.getSpanEnd(TextKeyListener.ACTIVE);

    // now for the multitap cases...

    // Try to increment the character we were working on before
    // if we have one and it's still the same key.

    int rec =
        (content.getSpanFlags(TextKeyListener.ACTIVE) & Spannable.SPAN_USER)
            >>> Spannable.SPAN_USER_SHIFT;

    if (activeStart == selStart
        && activeEnd == selEnd
        && selEnd - selStart == 1
        && rec >= 0
        && rec < sRecs.size()) {
      if (keyCode == KeyEvent.KEYCODE_STAR) {
        char current = content.charAt(selStart);

        if (Character.isLowerCase(current)) {
          content.replace(selStart, selEnd, String.valueOf(current).toUpperCase());
          removeTimeouts(content);
          new Timeout(content); // for its side effects

          return true;
        }
        if (Character.isUpperCase(current)) {
          content.replace(selStart, selEnd, String.valueOf(current).toLowerCase());
          removeTimeouts(content);
          new Timeout(content); // for its side effects

          return true;
        }
      }

      if (sRecs.indexOfKey(keyCode) == rec) {
        String val = sRecs.valueAt(rec);
        char ch = content.charAt(selStart);
        int ix = val.indexOf(ch);

        if (ix >= 0) {
          ix = (ix + 1) % (val.length());

          content.replace(selStart, selEnd, val, ix, ix + 1);
          removeTimeouts(content);
          new Timeout(content); // for its side effects

          return true;
        }
      }

      // Is this key one we know about at all?  If so, acknowledge
      // that the selection is our fault but the key has changed
      // or the text no longer matches, so move the selection over
      // so that it inserts instead of replaces.

      rec = sRecs.indexOfKey(keyCode);

      if (rec >= 0) {
        Selection.setSelection(content, selEnd, selEnd);
        selStart = selEnd;
      }
    } else {
      rec = sRecs.indexOfKey(keyCode);
    }

    if (rec >= 0) {
      // We have a valid key.  Replace the selection or insertion point
      // with the first character for that key, and remember what
      // record it came from for next time.

      String val = sRecs.valueAt(rec);

      int off = 0;
      if ((pref & TextKeyListener.AUTO_CAP) != 0
          && TextKeyListener.shouldCap(mCapitalize, content, selStart)) {
        for (int i = 0; i < val.length(); i++) {
          if (Character.isUpperCase(val.charAt(i))) {
            off = i;
            break;
          }
        }
      }

      if (selStart != selEnd) {
        Selection.setSelection(content, selEnd);
      }

      content.setSpan(OLD_SEL_START, selStart, selStart, Spannable.SPAN_MARK_MARK);

      content.replace(selStart, selEnd, val, off, off + 1);

      int oldStart = content.getSpanStart(OLD_SEL_START);
      selEnd = Selection.getSelectionEnd(content);

      if (selEnd != oldStart) {
        Selection.setSelection(content, oldStart, selEnd);

        content.setSpan(
            TextKeyListener.LAST_TYPED, oldStart, selEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        content.setSpan(
            TextKeyListener.ACTIVE,
            oldStart,
            selEnd,
            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE | (rec << Spannable.SPAN_USER_SHIFT));
      }

      removeTimeouts(content);
      new Timeout(content); // for its side effects

      // Set up the callback so we can remove the timeout if the
      // cursor moves.

      if (content.getSpanStart(this) < 0) {
        KeyListener[] methods = content.getSpans(0, content.length(), KeyListener.class);
        for (Object method : methods) {
          content.removeSpan(method);
        }
        content.setSpan(this, 0, content.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
      }

      return true;
    }

    return super.onKeyDown(view, content, keyCode, event);
  }