robolectric/src/main/java/org/robolectric/android/internal/LocalUiController.java
@Override
  public boolean injectString(String str) throws InjectEventSecurityException {
    checkNotNull(str);
    checkState(Looper.myLooper() == Looper.getMainLooper(), "Expecting to be on main thread!");

    // No-op if string is empty.
    if (str.isEmpty()) {
      Log.w(TAG, "Supplied string is empty resulting in no-op (nothing is typed).");
      return true;
    }

    boolean eventInjected = false;
    KeyCharacterMap keyCharacterMap = getKeyCharacterMap();

    // TODO(b/80130875): Investigate why not use (as suggested in javadoc of
    // keyCharacterMap.getEvents):
    // http://developer.android.com/reference/android/view/KeyEvent.html#KeyEvent(long,
    // java.lang.String, int, int)
    KeyEvent[] events = keyCharacterMap.getEvents(str.toCharArray());
    if (events == null) {
      throw new RuntimeException(
          String.format(
              "Failed to get key events for string %s (i.e. current IME does not understand how to"
                  + " translate the string into key events). As a workaround, you can use"
                  + " replaceText action to set the text directly in the EditText field.",
              str));
    }

    Log.d(TAG, String.format("Injecting string: \"%s\"", str));

    for (KeyEvent event : events) {
      checkNotNull(
          event,
          String.format(
              "Failed to get event for character (%c) with key code (%s)",
              event.getKeyCode(), event.getUnicodeChar()));

      eventInjected = false;
      for (int attempts = 0; !eventInjected && attempts < 4; attempts++) {
        // We have to change the time of an event before injecting it because
        // all KeyEvents returned by KeyCharacterMap.getEvents() have the same
        // time stamp and the system rejects too old events. Hence, it is
        // possible for an event to become stale before it is injected if it
        // takes too long to inject the preceding ones.
        event = KeyEvent.changeTimeRepeat(event, SystemClock.uptimeMillis(), 0);
        eventInjected = injectKeyEvent(event);
      }

      if (!eventInjected) {
        Log.e(
            TAG,
            String.format(
                "Failed to inject event for character (%c) with key code (%s)",
                event.getUnicodeChar(), event.getKeyCode()));
        break;
      }
    }

    return eventInjected;
  }
@Override
  public boolean injectString(String str) throws InjectEventSecurityException {
    checkNotNull(str);
    checkState(Looper.myLooper() == Looper.getMainLooper(), "Expecting to be on main thread!");

    // No-op if string is empty.
    if (str.isEmpty()) {
      Log.w(TAG, "Supplied string is empty resulting in no-op (nothing is typed).");
      return true;
    }

    boolean eventInjected = false;
    KeyCharacterMap keyCharacterMap = getKeyCharacterMap();

    // TODO(b/80130875): Investigate why not use (as suggested in javadoc of
    // keyCharacterMap.getEvents):
    // http://developer.android.com/reference/android/view/KeyEvent.html#KeyEvent(long,
    // java.lang.String, int, int)
    KeyEvent[] events = keyCharacterMap.getEvents(str.toCharArray());
    if (events == null) {
      throw new RuntimeException(
          String.format(
              "Failed to get key events for string %s (i.e. current IME does not understand how to"
                  + " translate the string into key events). As a workaround, you can use"
                  + " replaceText action to set the text directly in the EditText field.",
              str));
    }

    Log.d(TAG, String.format("Injecting string: \"%s\"", str));

    for (KeyEvent event : events) {
      checkNotNull(
          event,
          String.format(
              "Failed to get event for character (%c) with key code (%s)",
              event.getKeyCode(), event.getUnicodeChar()));

      eventInjected = false;
      for (int attempts = 0; !eventInjected && attempts < 4; attempts++) {
        // We have to change the time of an event before injecting it because
        // all KeyEvents returned by KeyCharacterMap.getEvents() have the same
        // time stamp and the system rejects too old events. Hence, it is
        // possible for an event to become stale before it is injected if it
        // takes too long to inject the preceding ones.
        event = KeyEvent.changeTimeRepeat(event, SystemClock.uptimeMillis(), 0);
        eventInjected = injectKeyEvent(event);
      }

      if (!eventInjected) {
        Log.e(
            TAG,
            String.format(
                "Failed to inject event for character (%c) with key code (%s)",
                event.getUnicodeChar(), event.getKeyCode()));
        break;
      }
    }

    return eventInjected;
  }
@ Override public boolean injectString ( String str ) throws InjectEventSecurityException { checkNotNull ( str ) ; checkState ( Looper . myLooper ( ) == Looper . getMainLooper ( ) , "Expecting to be on main thread!" ) ; // No-op if string is empty. if ( str . isEmpty ( ) ) { Log . w ( TAG , "Supplied string is empty resulting in no-op (nothing is typed)." ) ; return true ; } boolean eventInjected = false ; KeyCharacterMap keyCharacterMap = getKeyCharacterMap ( ) ; // TODO(b/80130875): Investigate why not use (as suggested in javadoc of // keyCharacterMap.getEvents): // http://developer.android.com/reference/android/view/KeyEvent.html#KeyEvent(long, // java.lang.String, int, int) KeyEvent [ ] events = keyCharacterMap . getEvents ( str . toCharArray ( ) ) ; if ( events == null ) { throw new RuntimeException ( String . format ( "Failed to get key events for string %s (i.e. current IME does not understand how to" + " translate the string into key events). As a workaround, you can use" + " replaceText action to set the text directly in the EditText field." , str ) ) ; } Log . d ( TAG , String . format ( "Injecting string: \"%s\"" , str ) ) ; for ( KeyEvent event : events ) { checkNotNull ( event , String . format ( "Failed to get event for character (%c) with key code (%s)" , event . getKeyCode ( ) , event . getUnicodeChar ( ) ) ) ; eventInjected = false ; for ( int attempts = 0 ; ! eventInjected && attempts < 4 ; attempts ++ ) { // We have to change the time of an event before injecting it because // all KeyEvents returned by KeyCharacterMap.getEvents() have the same // time stamp and the system rejects too old events. Hence, it is // possible for an event to become stale before it is injected if it // takes too long to inject the preceding ones. event = KeyEvent . changeTimeRepeat ( event , SystemClock . uptimeMillis ( ) , 0 ) ; eventInjected = injectKeyEvent ( event ) ; } if ( ! eventInjected ) { Log . e ( TAG , String . format ( "Failed to inject event for character (%c) with key code (%s)" , event . getUnicodeChar ( ) , event . getKeyCode ( ) ) ) ; break ; } } return eventInjected ; }
TODO(b/80130000): implementation copied from espresso's UIControllerImpl. Refactor code into common location
TODO ( b / 80130000 ) : implementation copied from espresso s UIControllerImpl . Refactor code into common location