Fix setText incomplete problem when the IME is slow
The fix is to add delay after each KeyEvent injection.
Also removed the misleading ViewElement#overrideClassName method.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=138252579
diff --git a/src/io/appium/droiddriver/UiElement.java b/src/io/appium/droiddriver/UiElement.java
index a003367..aa55d09 100644
--- a/src/io/appium/droiddriver/UiElement.java
+++ b/src/io/appium/droiddriver/UiElement.java
@@ -17,9 +17,7 @@
 package io.appium.droiddriver;
 
 import android.graphics.Rect;
-
-import java.util.List;
-
+import android.view.accessibility.AccessibilityNodeInfo;
 import io.appium.droiddriver.actions.Action;
 import io.appium.droiddriver.actions.InputInjector;
 import io.appium.droiddriver.finders.Attribute;
@@ -27,113 +25,114 @@
 import io.appium.droiddriver.instrumentation.InstrumentationDriver;
 import io.appium.droiddriver.scroll.Direction.PhysicalDirection;
 import io.appium.droiddriver.uiautomation.UiAutomationDriver;
+import java.util.List;
 
 /**
  * Represents an UI element within an Android App.
- * <p>
- * UI elements are generally views. Users can get attributes and perform
- * actions. Note that actions often update UiElement, so users are advised not
- * to store instances for later use -- the instances could become stale.
+ *
+ * <p>UI elements are generally views. Users can get attributes and perform actions. Note that
+ * actions often update UiElement, so users are advised not to store instances for later use -- the
+ * instances could become stale.
  */
 public interface UiElement {
-  /**
-   * Gets the text of this element.
-   */
+  /** Filters out invisible children. */
+  Predicate<UiElement> VISIBLE =
+      new Predicate<UiElement>() {
+        @Override
+        public boolean apply(UiElement element) {
+          return element.isVisible();
+        }
+
+        @Override
+        public String toString() {
+          return "VISIBLE";
+        }
+      };
+
+  /** Gets the text of this element. */
   String getText();
 
   /**
-   * Gets the content description of this element.
+   * Sets the text of this element. The implementation may not work on all UiElements if the
+   * underlying view is not EditText.
+   *
+   * <p>If this element already has text, it is cleared first if the device has API 11 or higher.
+   *
+   * <p>TODO: Support this behavior on older devices.
+   *
+   * <p>The soft keyboard may be shown after this call. If the {@code text} ends with {@code '\n'},
+   * the IME may be closed automatically. If the soft keyboard is open, you can call {@link
+   * UiDevice#pressBack()} to close it.
+   *
+   * <p>If you are using {@link io.appium.droiddriver.instrumentation.InstrumentationDriver}, you
+   * may use {@link io.appium.droiddriver.actions.view.CloseKeyboardAction} to close it. The
+   * advantage of {@code CloseKeyboardAction} is that it is a no-op if the soft keyboard is hidden.
+   * This is useful when the state of the soft keyboard cannot be determined.
+   *
+   * @param text the text to enter
    */
+  void setText(String text);
+
+  /** Gets the content description of this element. */
   String getContentDescription();
 
   /**
-   * Gets the class name of the underlying view. The actual name could be
-   * overridden.
-   *
-   * @see io.appium.droiddriver.instrumentation.ViewElement#overrideClassName
+   * Gets the class name of the underlying view. The actual name could be overridden if viewed with
+   * uiautomatorviewer, which gets the name from {@link AccessibilityNodeInfo#getClassName}. If the
+   * app uses custom View classes that do not call {@link AccessibilityNodeInfo#setClassName} with
+   * the actual class name, uiautomatorviewer will report the wrong name.
    */
   String getClassName();
 
-  /**
-   * Gets the resource id of this element.
-   */
+  /** Gets the resource id of this element. */
   String getResourceId();
 
-  /**
-   * Gets the package name of this element.
-   */
+  /** Gets the package name of this element. */
   String getPackageName();
 
-  /**
-   * @return whether or not this element is visible on the device's display.
-   */
+  /** @return whether or not this element is visible on the device's display. */
   boolean isVisible();
 
-  /**
-   * @return whether this element is checkable.
-   */
+  /** @return whether this element is checkable. */
   boolean isCheckable();
 
-  /**
-   * @return whether this element is checked.
-   */
+  /** @return whether this element is checked. */
   boolean isChecked();
 
-  /**
-   * @return whether this element is clickable.
-   */
+  /** @return whether this element is clickable. */
   boolean isClickable();
 
-  /**
-   * @return whether this element is enabled.
-   */
+  /** @return whether this element is enabled. */
   boolean isEnabled();
 
-  /**
-   * @return whether this element is focusable.
-   */
+  /** @return whether this element is focusable. */
   boolean isFocusable();
 
-  /**
-   * @return whether this element is focused.
-   */
+  /** @return whether this element is focused. */
   boolean isFocused();
 
-  /**
-   * @return whether this element is scrollable.
-   */
+  /** @return whether this element is scrollable. */
   boolean isScrollable();
 
-  /**
-   * @return whether this element is long-clickable.
-   */
+  /** @return whether this element is long-clickable. */
   boolean isLongClickable();
 
-  /**
-   * @return whether this element is password.
-   */
+  /** @return whether this element is password. */
   boolean isPassword();
 
-  /**
-   * @return whether this element is selected.
-   */
+  /** @return whether this element is selected. */
   boolean isSelected();
 
   /**
-   * Gets the UiElement bounds in screen coordinates. The coordinates may not be
-   * visible on screen.
+   * Gets the UiElement bounds in screen coordinates. The coordinates may not be visible on screen.
    */
   Rect getBounds();
 
-  /**
-   * Gets the UiElement bounds in screen coordinates. The coordinates will be
-   * visible on screen.
-   */
+  /** Gets the UiElement bounds in screen coordinates. The coordinates will be visible on screen. */
   Rect getVisibleBounds();
 
-  /**
-   * @return value of the given attribute.
-   */
+  /** @return value of the given attribute. */
+  @SuppressWarnings("TypeParameterUnusedInFormals")
   <T> T get(Attribute attribute);
 
   /**
@@ -144,37 +143,13 @@
    */
   boolean perform(Action action);
 
-  /**
-   * Sets the text of this element. The implementation may not work on all UiElements if the
-   * underlying view is not EditText. <p> If this element already has text, it is cleared first if
-   * the device has API 11 or higher. <p> TODO: Support this behavior on older devices. <p> The IME
-   * (soft keyboard) may be shown after this call. If the {@code text} ends with {@code '\n'}, the
-   * IME may be closed automatically. If the IME is open, you can call {@link UiDevice#pressBack()}
-   * to close it. <p> If you are using {@link io.appium.droiddriver.instrumentation.InstrumentationDriver},
-   * you may use {@link io.appium.droiddriver.actions.view.CloseKeyboardAction} to close it. The
-   * advantage of {@code CloseKeyboardAction} is that it is a no-op if the IME is hidden. This is
-   * useful when the state of the IME cannot be determined.
-   *
-   * @param text the text to enter
-   */
-  void setText(String text);
-
-  /**
-   * Clicks this element. The click will be at the center of the visible
-   * element.
-   */
+  /** Clicks this element. The click will be at the center of the visible element. */
   void click();
 
-  /**
-   * Long-clicks this element. The click will be at the center of the visible
-   * element.
-   */
+  /** Long-clicks this element. The click will be at the center of the visible element. */
   void longClick();
 
-  /**
-   * Double-clicks this element. The click will be at the center of the visible
-   * element.
-   */
+  /** Double-clicks this element. The click will be at the center of the visible element. */
   void doubleClick();
 
   /**
@@ -185,50 +160,27 @@
   void scroll(PhysicalDirection direction);
 
   /**
-   * Gets an immutable {@link List} of immediate children that satisfy
-   * {@code predicate}. It always filters children that are null. This gives a
-   * low level access to the underlying data. Do not use it unless you are sure
-   * about the subtle details. Note the count may not be what you expect. For
-   * instance, a dynamic list may show more items when scrolling beyond the end,
-   * varying the count. The count also depends on the driver implementation:
+   * Gets an immutable {@link List} of immediate children that satisfy {@code predicate}. It always
+   * filters children that are null. This gives a low level access to the underlying data. Do not
+   * use it unless you are sure about the subtle details. Note the count may not be what you expect.
+   * For instance, a dynamic list may show more items when scrolling beyond the end, varying the
+   * count. The count also depends on the driver implementation:
+   *
    * <ul>
-   * <li>{@link InstrumentationDriver} includes all.</li>
-   * <li>the Accessibility API (which {@link UiAutomationDriver} depends on)
-   * does not include off-screen children, but may include invisible on-screen
-   * children.</li>
+   *   <li>{@link InstrumentationDriver} includes all.
+   *   <li>the Accessibility API (which {@link UiAutomationDriver} depends on) does not include
+   *       off-screen children, but may include invisible on-screen children.
    * </ul>
-   * <p>
-   * Another discrepancy between {@link InstrumentationDriver}
-   * {@link UiAutomationDriver} is the order of children. The Accessibility API
-   * returns children in the order of layout (see
-   * {@link android.view.ViewGroup#addChildrenForAccessibility}, which is added
-   * in API16).
-   * </p>
+   *
+   * <p>Another discrepancy between {@link InstrumentationDriver} {@link UiAutomationDriver} is the
+   * order of children. The Accessibility API returns children in the order of layout (see {@link
+   * android.view.ViewGroup#addChildrenForAccessibility}, which is added in API16).
    */
   List<? extends UiElement> getChildren(Predicate<? super UiElement> predicate);
 
-  /**
-   * Filters out invisible children.
-   */
-  Predicate<UiElement> VISIBLE = new Predicate<UiElement>() {
-    @Override
-    public boolean apply(UiElement element) {
-      return element.isVisible();
-    }
-
-    @Override
-    public String toString() {
-      return "VISIBLE";
-    }
-  };
-
-  /**
-   * Gets the parent.
-   */
+  /** Gets the parent. */
   UiElement getParent();
 
-  /**
-   * Gets the {@link InputInjector} for injecting InputEvent.
-   */
+  /** Gets the {@link InputInjector} for injecting InputEvent. */
   InputInjector getInjector();
 }
diff --git a/src/io/appium/droiddriver/actions/TextAction.java b/src/io/appium/droiddriver/actions/TextAction.java
index 28565e8..18b28e8 100644
--- a/src/io/appium/droiddriver/actions/TextAction.java
+++ b/src/io/appium/droiddriver/actions/TextAction.java
@@ -21,29 +21,26 @@
 import android.os.SystemClock;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
-
+import android.view.ViewConfiguration;
 import io.appium.droiddriver.UiElement;
 import io.appium.droiddriver.exceptions.ActionException;
 import io.appium.droiddriver.util.Preconditions;
 import io.appium.droiddriver.util.Strings;
 
-/**
- * An action to type text.
- */
+/** An action to type text. */
 public class TextAction extends KeyAction {
 
   @SuppressLint("InlinedApi")
   @SuppressWarnings("deprecation")
   private static final KeyCharacterMap KEY_CHAR_MAP =
-      Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB ? KeyCharacterMap
-          .load(KeyCharacterMap.BUILT_IN_KEYBOARD) : KeyCharacterMap
-          .load(KeyCharacterMap.VIRTUAL_KEYBOARD);
-
+      Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB
+          ? KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD)
+          : KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+  // KeyRepeatDelay is a good heuristic for KeyInjectionDelay.
+  private static long keyInjectionDelayMillis = ViewConfiguration.getKeyRepeatDelay();
   private final String text;
 
-  /**
-   * Defaults timeoutMillis to 100.
-   */
+  /** Defaults timeoutMillis to 100. */
   public TextAction(String text) {
     this(text, 100L, false);
   }
@@ -53,6 +50,14 @@
     this.text = Preconditions.checkNotNull(text);
   }
 
+  public static long getKeyInjectionDelayMillis() {
+    return keyInjectionDelayMillis;
+  }
+
+  public static void setKeyInjectionDelayMillis(long keyInjectionDelayMillis) {
+    TextAction.keyInjectionDelayMillis = keyInjectionDelayMillis;
+  }
+
   @Override
   public boolean perform(InputInjector injector, UiElement element) {
     maybeCheckFocused(element);
@@ -71,6 +76,7 @@
         if (!injector.injectInputEvent(modifiedEvent)) {
           throw new ActionException("Failed to inject " + event);
         }
+        SystemClock.sleep(keyInjectionDelayMillis);
       }
     } else {
       throw new ActionException("The given text is not supported: " + text);
diff --git a/src/io/appium/droiddriver/helpers/BaseDroidDriverTest.java b/src/io/appium/droiddriver/helpers/BaseDroidDriverTest.java
index 0b17dc5..607da95 100644
--- a/src/io/appium/droiddriver/helpers/BaseDroidDriverTest.java
+++ b/src/io/appium/droiddriver/helpers/BaseDroidDriverTest.java
@@ -91,9 +91,6 @@
    * behavior - if multiple subclasses override this method, only the first override is executed.
    * Other overrides are silently ignored. You can either use {@link SingleRun} in {@link #setUp},
    * or override this method, which is a simpler alternative with the aforementioned catch.
-   * <p>
-   * If an InstrumentationDriver is used, this is a good place to call {@link
-   * io.appium.droiddriver.instrumentation.ViewElement#overrideClassName}
    */
   protected void classSetUp() {
     DroidDriversInitializer.get(DroidDrivers.newDriver()).singleRun();
diff --git a/src/io/appium/droiddriver/instrumentation/ViewElement.java b/src/io/appium/droiddriver/instrumentation/ViewElement.java
index ab87817..da7f2d0 100644
--- a/src/io/appium/droiddriver/instrumentation/ViewElement.java
+++ b/src/io/appium/droiddriver/instrumentation/ViewElement.java
@@ -22,33 +22,102 @@
 import android.graphics.Rect;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.Checkable;
 import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.EnumMap;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.FutureTask;
-
 import io.appium.droiddriver.actions.InputInjector;
 import io.appium.droiddriver.base.BaseUiElement;
 import io.appium.droiddriver.base.DroidDriverContext;
 import io.appium.droiddriver.finders.Attribute;
 import io.appium.droiddriver.util.InstrumentationUtils;
 import io.appium.droiddriver.util.Preconditions;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.FutureTask;
 
-/**
- * A UiElement that is backed by a View.
- */
+/** A UiElement that is backed by a View. */
 public class ViewElement extends BaseUiElement<View, ViewElement> {
+  private final DroidDriverContext<View, ViewElement> context;
+  private final View view;
+  private final Map<Attribute, Object> attributes;
+  private final boolean visible;
+  private final Rect visibleBounds;
+  private final ViewElement parent;
+  private final List<ViewElement> children;
+
+  /**
+   * A snapshot of all attributes is taken at construction. The attributes of a {@code ViewElement}
+   * instance are immutable. If the underlying view is updated, a new {@code ViewElement} instance
+   * will be created in {@link io.appium.droiddriver.DroidDriver#refreshUiElementTree}.
+   */
+  public ViewElement(DroidDriverContext<View, ViewElement> context, View view, ViewElement parent) {
+    this.context = Preconditions.checkNotNull(context);
+    this.view = Preconditions.checkNotNull(view);
+    this.parent = parent;
+    AttributesSnapshot attributesSnapshot = new AttributesSnapshot(view);
+    InstrumentationUtils.runOnMainSyncWithTimeout(attributesSnapshot);
+
+    attributes = Collections.unmodifiableMap(attributesSnapshot.attribs);
+    this.visibleBounds = attributesSnapshot.visibleBounds;
+    this.visible = attributesSnapshot.visible;
+    if (attributesSnapshot.childViews == null) {
+      this.children = null;
+    } else {
+      List<ViewElement> children = new ArrayList<>(attributesSnapshot.childViews.size());
+      for (View childView : attributesSnapshot.childViews) {
+        children.add(context.getElement(childView, this));
+      }
+      this.children = Collections.unmodifiableList(children);
+    }
+  }
+
+  @Override
+  public Rect getVisibleBounds() {
+    return visibleBounds;
+  }
+
+  @Override
+  public boolean isVisible() {
+    return visible;
+  }
+
+  @Override
+  public ViewElement getParent() {
+    return parent;
+  }
+
+  @Override
+  protected List<ViewElement> getChildren() {
+    return children;
+  }
+
+  @Override
+  protected Map<Attribute, Object> getAttributes() {
+    return attributes;
+  }
+
+  @Override
+  public InputInjector getInjector() {
+    return context.getDriver().getInjector();
+  }
+
+  @Override
+  protected void doPerformAndWait(FutureTask<Boolean> futureTask, long timeoutMillis) {
+    futureTask.run();
+    InstrumentationUtils.tryWaitForIdleSync(timeoutMillis);
+  }
+
+  @Override
+  public View getRawElement() {
+    return view;
+  }
+
   private static class AttributesSnapshot implements Callable<Void> {
+    final Map<Attribute, Object> attribs = new EnumMap<>(Attribute.class);
     private final View view;
-    final Map<Attribute, Object> attribs = new EnumMap<Attribute, Object>(Attribute.class);
     boolean visible;
     Rect visibleBounds;
     List<View> childViews;
@@ -107,9 +176,7 @@
     }
 
     private String getClassName() {
-      String className = view.getClass().getName();
-      return CLASS_NAME_OVERRIDES.containsKey(className) ? CLASS_NAME_OVERRIDES.get(className)
-          : className;
+      return view.getClass().getName();
     }
 
     private String getResourceId() {
@@ -168,7 +235,7 @@
       }
       ViewGroup group = (ViewGroup) view;
       int childCount = group.getChildCount();
-      childViews = new ArrayList<View>(childCount);
+      childViews = new ArrayList<>(childCount);
       for (int i = 0; i < childCount; i++) {
         View child = group.getChildAt(i);
         if (child != null) {
@@ -177,104 +244,4 @@
       }
     }
   }
-
-  private static final Map<String, String> CLASS_NAME_OVERRIDES = new HashMap<String, String>();
-
-  /**
-   * Typically users find the class name to use in tests using SDK tool
-   * uiautomatorviewer. This name is returned by
-   * {@link AccessibilityNodeInfo#getClassName}. If the app uses custom View
-   * classes that do not call {@link AccessibilityNodeInfo#setClassName} with
-   * the actual class name, different types of drivers see different class names
-   * (InstrumentationDriver sees the actual class name, while UiAutomationDriver
-   * sees {@link AccessibilityNodeInfo#getClassName}).
-   * <p>
-   * If tests fail with InstrumentationDriver, find the actual class name by
-   * examining app code or by calling
-   * {@link io.appium.droiddriver.DroidDriver#dumpUiElementTree}, then
-   * call this method in setUp to override it with the class name seen in
-   * uiautomatorviewer.
-   * </p>
-   * A better solution is to use resource-id instead of classname, which is an
-   * implementation detail and subject to change.
-   */
-  public static void overrideClassName(String actualClassName, String overridingClassName) {
-    CLASS_NAME_OVERRIDES.put(actualClassName, overridingClassName);
-  }
-
-  private final DroidDriverContext<View, ViewElement> context;
-  private final View view;
-  private final Map<Attribute, Object> attributes;
-  private final boolean visible;
-  private final Rect visibleBounds;
-  private final ViewElement parent;
-  private final List<ViewElement> children;
-
-  /**
-   * A snapshot of all attributes is taken at construction. The attributes of a
-   * {@code ViewElement} instance are immutable. If the underlying view is
-   * updated, a new {@code ViewElement} instance will be created in
-   * {@link io.appium.droiddriver.DroidDriver#refreshUiElementTree}.
-   */
-  public ViewElement(DroidDriverContext<View, ViewElement> context, View view, ViewElement parent) {
-    this.context = Preconditions.checkNotNull(context);
-    this.view = Preconditions.checkNotNull(view);
-    this.parent = parent;
-    AttributesSnapshot attributesSnapshot = new AttributesSnapshot(view);
-    InstrumentationUtils.runOnMainSyncWithTimeout(attributesSnapshot);
-
-    attributes = Collections.unmodifiableMap(attributesSnapshot.attribs);
-    this.visibleBounds = attributesSnapshot.visibleBounds;
-    this.visible = attributesSnapshot.visible;
-    if (attributesSnapshot.childViews == null) {
-      this.children = null;
-    } else {
-      List<ViewElement> children = new ArrayList<ViewElement>(attributesSnapshot.childViews.size());
-      for (View childView : attributesSnapshot.childViews) {
-        children.add(context.getElement(childView, this));
-      }
-      this.children = Collections.unmodifiableList(children);
-    }
-  }
-
-  @Override
-  public Rect getVisibleBounds() {
-    return visibleBounds;
-  }
-
-  @Override
-  public boolean isVisible() {
-    return visible;
-  }
-
-  @Override
-  public ViewElement getParent() {
-    return parent;
-  }
-
-  @Override
-  protected List<ViewElement> getChildren() {
-    return children;
-  }
-
-  @Override
-  protected Map<Attribute, Object> getAttributes() {
-    return attributes;
-  }
-
-  @Override
-  public InputInjector getInjector() {
-    return context.getDriver().getInjector();
-  }
-
-  @Override
-  protected void doPerformAndWait(FutureTask<Boolean> futureTask, long timeoutMillis) {
-    futureTask.run();
-    InstrumentationUtils.tryWaitForIdleSync(timeoutMillis);
-  }
-
-  @Override
-  public View getRawElement() {
-    return view;
-  }
 }