@Override public boolean injectString(String str) throws InjectEventSecurityException { checkNotNull(str); checkState(Looper.myLooper() == mainLooper, "Expecting to be on main thread!"); initialize(); // No-op if string is empty. if (str.length() == 0) { Log.w(TAG, "Supplied string is empty resulting in no-op (nothing is typed)."); return true; } boolean eventInjected = false; KeyCharacterMap keyCharacterMap = getKeyCharacterMap(); // TODO(user): 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++) { 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; }