• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 DroidDriver committers
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.appium.droiddriver.actions;
18 
19 import android.annotation.SuppressLint;
20 import android.os.Build;
21 import android.os.SystemClock;
22 import android.view.KeyCharacterMap;
23 import android.view.KeyEvent;
24 import android.view.ViewConfiguration;
25 import io.appium.droiddriver.UiElement;
26 import io.appium.droiddriver.exceptions.ActionException;
27 import io.appium.droiddriver.util.Preconditions;
28 import io.appium.droiddriver.util.Strings;
29 
30 /** An action to type text. */
31 public class TextAction extends KeyAction {
32 
33   @SuppressLint("InlinedApi")
34   @SuppressWarnings("deprecation")
35   private static final KeyCharacterMap KEY_CHAR_MAP =
36       Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB
37           ? KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD)
38           : KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
39   // KeyRepeatDelay is a good heuristic for KeyInjectionDelay.
40   private static long keyInjectionDelayMillis = ViewConfiguration.getKeyRepeatDelay();
41   private final String text;
42 
43   /** Defaults timeoutMillis to 100. */
44   public TextAction(String text) {
45     this(text, 100L, false);
46   }
47 
48   public TextAction(String text, long timeoutMillis, boolean checkFocused) {
49     super(timeoutMillis, checkFocused);
50     this.text = Preconditions.checkNotNull(text);
51   }
52 
53   public static long getKeyInjectionDelayMillis() {
54     return keyInjectionDelayMillis;
55   }
56 
57   public static void setKeyInjectionDelayMillis(long keyInjectionDelayMillis) {
58     TextAction.keyInjectionDelayMillis = keyInjectionDelayMillis;
59   }
60 
61   @Override
62   public boolean perform(InputInjector injector, UiElement element) {
63     maybeCheckFocused(element);
64 
65     // TODO: recycle events?
66     KeyEvent[] events = KEY_CHAR_MAP.getEvents(text.toCharArray());
67 
68     if (events != null) {
69       for (KeyEvent event : events) {
70         // We have to change the time of an event before injecting it because
71         // all KeyEvents returned by KeyCharacterMap.getEvents() have the same
72         // time stamp and the system rejects too old events. Hence, it is
73         // possible for an event to become stale before it is injected if it
74         // takes too long to inject the preceding ones.
75         KeyEvent modifiedEvent = KeyEvent.changeTimeRepeat(event, SystemClock.uptimeMillis(), 0);
76         if (!injector.injectInputEvent(modifiedEvent)) {
77           throw new ActionException("Failed to inject " + event);
78         }
79         SystemClock.sleep(keyInjectionDelayMillis);
80       }
81     } else {
82       throw new ActionException("The given text is not supported: " + text);
83     }
84     return true;
85   }
86 
87   @Override
88   public String toString() {
89     return Strings.toStringHelper(this).addValue(text).toString();
90   }
91 }
92