/* * Copyright 2011 NativeDriver committers Copyright 2011 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package io.selendroid.server.android; import io.selendroid.server.common.exceptions.SelendroidException; import io.selendroid.server.model.Keyboard; import android.app.Instrumentation; import android.view.KeyEvent; import io.selendroid.server.util.SelendroidLogger; /** * Provides a method to send a string to an application under test. Keys are sent using an * {@code Instrumentation} instance. The strings may contain any character in the * {@link AndroidKeys} {@code enum}. * * <p> * Note that this class does not focus on a particular {@code View} before sending keys, nor does it * require that some {@code View} has focus. This is fine if you are sending the Menu key, or using * the arrow keys to select an item in a list. If you are trying to type into a certain widget, be * sure it has focus before using this class. * * @author Matt DeVore */ public class InstrumentedKeySender implements KeySender { protected final Instrumentation instrumentation; private final KeyboardImpl keyboardImpl; /** * Creates a new instance which sends keys to the given {@code Instrumentation}. */ public InstrumentedKeySender(Instrumentation instrumentation) { this.instrumentation = instrumentation; this.keyboardImpl = new KeyboardImpl(); } /** * Returns a {@code Keyboard} object which sends key using this {@code KeySender}. */ public Keyboard getKeyboard() { return keyboardImpl; } private static int indexOfSpecialKey(CharSequence string, int startIndex) { for (int i = startIndex; i < string.length(); i++) { if (AndroidKeys.hasAndroidKeyEvent(string.charAt(i))) { return i; } } return string.length(); } /** * Sends key events to the {@code Instrumentation}. This method will send a portion of the given * {@code CharSequence} as a single {@code String} if the portion does not contain any special * keys. * * @param text the keys to send to the {@code Instrumentation}. */ public void send(CharSequence text) { int currentIndex = 0; while (currentIndex < text.length()) { char currentCharacter = text.charAt(currentIndex); if (AndroidKeys.hasAndroidKeyEvent(currentCharacter)) { // The next character is special and must be sent individually int keyCode = AndroidKeys.keyCodeFor(currentCharacter); if (keyCode == KeyEvent.KEYCODE_HOME) { throw new RuntimeException( "It is not possible to simulate the HOME key using instrumentation. " + "Please use adb, e.g. 'adb shell input keyevent 3'."); } SelendroidLogger.debug("Send keys, sending special key code"); instrumentation.sendKeyDownUpSync(keyCode); currentIndex++; } else { // There is at least one "normal" character, that is a character // represented by a plain Unicode character that can be sent with // sendStringSync. So send as many such consecutive normal characters // as possible in a single String. int nextSpecialKey = indexOfSpecialKey(text, currentIndex); SelendroidLogger.debug("Send keys, sending string"); instrumentation.sendStringSync(text.subSequence(currentIndex, nextSpecialKey).toString()); currentIndex = nextSpecialKey; } } } private class KeyboardImpl implements Keyboard { @Override public void sendKeys(CharSequence... keysToSend) { StringBuilder sb = new StringBuilder(); for (CharSequence keys : keysToSend) { sb.append(keys); } send(sb.toString()); } } }