TypeTextAction.java revision f69eb9ac2856f470cb79f57141f711ed3ceed99d
1/* 2 * Copyright (C) 2014 The Android Open Source Project 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 17package com.google.android.apps.common.testing.ui.espresso.action; 18 19import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.hasFocus; 20import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.isAssignableFrom; 21import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.isDisplayed; 22import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.supportsInputMethods; 23import static com.google.common.base.Preconditions.checkNotNull; 24import static org.hamcrest.Matchers.allOf; 25import static org.hamcrest.Matchers.anyOf; 26 27import com.google.android.apps.common.testing.ui.espresso.InjectEventSecurityException; 28import com.google.android.apps.common.testing.ui.espresso.PerformException; 29import com.google.android.apps.common.testing.ui.espresso.UiController; 30import com.google.android.apps.common.testing.ui.espresso.ViewAction; 31import com.google.android.apps.common.testing.ui.espresso.util.HumanReadables; 32 33import android.os.Build; 34import android.util.Log; 35import android.view.View; 36import android.widget.SearchView; 37 38import org.hamcrest.Matcher; 39 40/** 41 * Enables typing text on views. 42 */ 43public final class TypeTextAction implements ViewAction { 44 private static final String TAG = TypeTextAction.class.getSimpleName(); 45 private final String stringToBeTyped; 46 private final boolean tapToFocus; 47 48 /** 49 * Constructs {@link TypeTextAction} with given string. If the string is empty it results in no-op 50 * (nothing is typed). By default this action sends a tap event to the center of the view to 51 * attain focus before typing. 52 * 53 * @param stringToBeTyped String To be typed by {@link TypeTextAction} 54 */ 55 public TypeTextAction(String stringToBeTyped) { 56 this(stringToBeTyped, true); 57 } 58 59 /** 60 * Constructs {@link TypeTextAction} with given string. If the string is empty it results in no-op 61 * (nothing is typed). 62 * 63 * @param stringToBeTyped String To be typed by {@link TypeTextAction} 64 * @param tapToFocus indicates whether a tap should be sent to the underlying view before typing. 65 */ 66 public TypeTextAction(String stringToBeTyped, boolean tapToFocus) { 67 checkNotNull(stringToBeTyped); 68 this.stringToBeTyped = stringToBeTyped; 69 this.tapToFocus = tapToFocus; 70 } 71 72 @SuppressWarnings("unchecked") 73 @Override 74 public Matcher<View> getConstraints() { 75 Matcher<View> matchers = allOf(isDisplayed()); 76 if (!tapToFocus) { 77 matchers = allOf(matchers, hasFocus()); 78 } 79 80 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { 81 return allOf(matchers, supportsInputMethods()); 82 } else { 83 // SearchView does not support input methods itself (rather it delegates to an internal text 84 // view for input). 85 return allOf(matchers, anyOf(supportsInputMethods(), isAssignableFrom(SearchView.class))); 86 } 87 } 88 89 @Override 90 public void perform(UiController uiController, View view) { 91 // No-op if string is empty. 92 if (stringToBeTyped.length() == 0) { 93 Log.w(TAG, "Supplied string is empty resulting in no-op (nothing is typed)."); 94 return; 95 } 96 97 if (tapToFocus) { 98 // Perform a click. 99 new GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER, Press.FINGER) 100 .perform(uiController, view); 101 uiController.loopMainThreadUntilIdle(); 102 } 103 104 try { 105 if (!uiController.injectString(stringToBeTyped)) { 106 Log.e(TAG, "Failed to type text: " + stringToBeTyped); 107 throw new PerformException.Builder() 108 .withActionDescription(this.getDescription()) 109 .withViewDescription(HumanReadables.describe(view)) 110 .withCause(new RuntimeException("Failed to type text: " + stringToBeTyped)) 111 .build(); 112 } 113 } catch (InjectEventSecurityException e) { 114 Log.e(TAG, "Failed to type text: " + stringToBeTyped); 115 throw new PerformException.Builder() 116 .withActionDescription(this.getDescription()) 117 .withViewDescription(HumanReadables.describe(view)) 118 .withCause(e) 119 .build(); 120 } 121 } 122 123 @Override 124 public String getDescription() { 125 return "type text"; 126 } 127} 128