Skip to content

Commit bb97026

Browse files
Fix input typing by dispatching key events instead of adb input text (mobile-dev-inc#417)
1 parent f3f9168 commit bb97026

File tree

7 files changed

+148
-3
lines changed

7 files changed

+148
-3
lines changed

debug.keystore

2.42 KB
Binary file not shown.

maestro-android/build.gradle

+45
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ android {
4949
minifyEnabled false
5050
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
5151
}
52+
debug {
53+
signingConfig signingConfigs.debug
54+
}
55+
}
56+
signingConfigs {
57+
debug {
58+
storeFile file('../debug.keystore')
59+
}
5260
}
5361
compileOptions {
5462
sourceCompatibility JavaVersion.VERSION_1_8
@@ -63,6 +71,43 @@ android {
6371
}
6472
}
6573

74+
tasks.register("copyMaestroAndroid", Copy) {
75+
def maestroAndroidApkPath = "outputs/apk/debug/maestro-android-debug.apk"
76+
def maestroAndroidApkDest = "../../maestro-client/src/main/resources"
77+
def maestroAndroidApkDestPath = "../../maestro-client/src/main/resources/maestro-android-debug.apk"
78+
79+
from layout.buildDirectory.dir(maestroAndroidApkPath)
80+
into layout.buildDirectory.file(maestroAndroidApkDest)
81+
82+
it.doLast {
83+
if (!layout.buildDirectory.file(maestroAndroidApkDestPath).get().asFile.exists())
84+
throw new GradleException("Error: Input source for copyMaestroAndroid doesn't exist")
85+
86+
new File("./maestro-client/src/main/resources/maestro-android-debug.apk")
87+
.renameTo(new File("./maestro-client/src/main/resources/maestro-app.apk"))
88+
}
89+
}
90+
91+
tasks.register("copyMaestroServer", Copy) {
92+
def maestroServerApkPath = "outputs/apk/androidTest/debug/maestro-android-debug-androidTest.apk"
93+
def maestroServerApkDest = "../../maestro-client/src/main/resources"
94+
def maestroServerApkDestPath = "../../maestro-client/src/main/resources/maestro-android-debug-androidTest.apk"
95+
96+
from layout.buildDirectory.dir(maestroServerApkPath)
97+
into layout.buildDirectory.file(maestroServerApkDest)
98+
99+
it.doLast {
100+
if (!layout.buildDirectory.file(maestroServerApkDestPath).get().asFile.exists())
101+
throw new GradleException("Error: Input source for copyMaestroServer doesn't exist")
102+
103+
new File("./maestro-client/src/main/resources/maestro-android-debug-androidTest.apk")
104+
.renameTo(new File("./maestro-client/src/main/resources/maestro-server.apk"))
105+
}
106+
}
107+
108+
tasks.named("assemble") { finalizedBy("copyMaestroAndroid") }
109+
tasks.named("assembleAndroidTest") { finalizedBy("copyMaestroServer") }
110+
66111
sourceSets {
67112
generated {
68113
java {

maestro-android/src/androidTest/java/dev/mobile/maestro/MaestroDriverService.kt

+93
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,30 @@ package dev.mobile.maestro
22

33
import android.app.UiAutomation
44
import android.util.Log
5+
import android.view.KeyEvent.KEYCODE_1
6+
import android.view.KeyEvent.KEYCODE_4
7+
import android.view.KeyEvent.KEYCODE_5
8+
import android.view.KeyEvent.KEYCODE_6
9+
import android.view.KeyEvent.KEYCODE_7
10+
import android.view.KeyEvent.KEYCODE_APOSTROPHE
11+
import android.view.KeyEvent.KEYCODE_AT
12+
import android.view.KeyEvent.KEYCODE_BACKSLASH
13+
import android.view.KeyEvent.KEYCODE_COMMA
14+
import android.view.KeyEvent.KEYCODE_EQUALS
15+
import android.view.KeyEvent.KEYCODE_GRAVE
16+
import android.view.KeyEvent.KEYCODE_LEFT_BRACKET
17+
import android.view.KeyEvent.KEYCODE_MINUS
18+
import android.view.KeyEvent.KEYCODE_NUMPAD_ADD
19+
import android.view.KeyEvent.KEYCODE_NUMPAD_LEFT_PAREN
20+
import android.view.KeyEvent.KEYCODE_NUMPAD_RIGHT_PAREN
21+
import android.view.KeyEvent.KEYCODE_PERIOD
22+
import android.view.KeyEvent.KEYCODE_POUND
23+
import android.view.KeyEvent.KEYCODE_RIGHT_BRACKET
24+
import android.view.KeyEvent.KEYCODE_SEMICOLON
25+
import android.view.KeyEvent.KEYCODE_SLASH
26+
import android.view.KeyEvent.KEYCODE_SPACE
27+
import android.view.KeyEvent.KEYCODE_STAR
28+
import android.view.KeyEvent.META_SHIFT_LEFT_ON
529
import androidx.test.ext.junit.runners.AndroidJUnit4
630
import androidx.test.platform.app.InstrumentationRegistry
731
import androidx.test.uiautomator.Configurator
@@ -11,6 +35,7 @@ import io.grpc.stub.StreamObserver
1135
import maestro_android.MaestroAndroid
1236
import maestro_android.MaestroDriverGrpc
1337
import maestro_android.deviceInfo
38+
import maestro_android.inputTextResponse
1439
import maestro_android.tapResponse
1540
import maestro_android.viewHierarchyResponse
1641
import org.junit.Test
@@ -103,4 +128,72 @@ class Service(
103128
responseObserver.onNext(tapResponse {})
104129
responseObserver.onCompleted()
105130
}
131+
132+
override fun inputText(
133+
request: MaestroAndroid.InputTextRequest,
134+
responseObserver: StreamObserver<MaestroAndroid.InputTextResponse>
135+
) {
136+
Log.d("Maestro", "Inputting text")
137+
setText(request.text)
138+
139+
responseObserver.onNext(inputTextResponse { })
140+
responseObserver.onCompleted()
141+
}
142+
143+
private fun setText(text: String) {
144+
for (element in text) {
145+
Log.d("Maestro", element.code.toString())
146+
when (element.code) {
147+
in 48..57 -> {
148+
/** 0~9 **/
149+
uiDevice.pressKeyCode(element.code - 41)
150+
}
151+
in 65..90 -> {
152+
/** A~Z **/
153+
uiDevice.pressKeyCode(element.code - 36, 1)
154+
}
155+
in 97..121 -> {
156+
/** a~z **/
157+
uiDevice.pressKeyCode(element.code - 68)
158+
}
159+
';'.code -> uiDevice.pressKeyCode(KEYCODE_SEMICOLON)
160+
'='.code -> uiDevice.pressKeyCode(KEYCODE_EQUALS)
161+
','.code -> uiDevice.pressKeyCode(KEYCODE_COMMA)
162+
'-'.code -> uiDevice.pressKeyCode(KEYCODE_MINUS)
163+
'.'.code -> uiDevice.pressKeyCode(KEYCODE_PERIOD)
164+
'/'.code -> uiDevice.pressKeyCode(KEYCODE_SLASH)
165+
'`'.code -> uiDevice.pressKeyCode(KEYCODE_GRAVE)
166+
'\''.code -> uiDevice.pressKeyCode(KEYCODE_APOSTROPHE)
167+
'['.code -> uiDevice.pressKeyCode(KEYCODE_LEFT_BRACKET)
168+
']'.code -> uiDevice.pressKeyCode(KEYCODE_RIGHT_BRACKET)
169+
'\\'.code -> uiDevice.pressKeyCode(KEYCODE_BACKSLASH)
170+
' '.code -> uiDevice.pressKeyCode(KEYCODE_SPACE)
171+
'@'.code -> uiDevice.pressKeyCode(KEYCODE_AT)
172+
'#'.code -> uiDevice.pressKeyCode(KEYCODE_POUND)
173+
'*'.code -> uiDevice.pressKeyCode(KEYCODE_STAR)
174+
'('.code -> uiDevice.pressKeyCode(KEYCODE_NUMPAD_LEFT_PAREN)
175+
')'.code -> uiDevice.pressKeyCode(KEYCODE_NUMPAD_RIGHT_PAREN)
176+
'+'.code -> uiDevice.pressKeyCode(KEYCODE_NUMPAD_ADD)
177+
'!'.code -> keyPressShiftedToEvents(uiDevice, KEYCODE_1)
178+
'$'.code -> keyPressShiftedToEvents(uiDevice, KEYCODE_4)
179+
'%'.code -> keyPressShiftedToEvents(uiDevice, KEYCODE_5)
180+
'^'.code -> keyPressShiftedToEvents(uiDevice, KEYCODE_6)
181+
'&'.code -> keyPressShiftedToEvents(uiDevice, KEYCODE_7)
182+
'"'.code -> keyPressShiftedToEvents(uiDevice, KEYCODE_APOSTROPHE)
183+
'{'.code -> keyPressShiftedToEvents(uiDevice, KEYCODE_LEFT_BRACKET)
184+
'}'.code -> keyPressShiftedToEvents(uiDevice, KEYCODE_RIGHT_BRACKET)
185+
':'.code -> keyPressShiftedToEvents(uiDevice, KEYCODE_SEMICOLON)
186+
'|'.code -> keyPressShiftedToEvents(uiDevice, KEYCODE_BACKSLASH)
187+
'<'.code -> keyPressShiftedToEvents(uiDevice, KEYCODE_COMMA)
188+
'>'.code -> keyPressShiftedToEvents(uiDevice, KEYCODE_PERIOD)
189+
'?'.code -> keyPressShiftedToEvents(uiDevice, KEYCODE_SLASH)
190+
'~'.code -> keyPressShiftedToEvents(uiDevice, KEYCODE_GRAVE)
191+
'_'.code -> keyPressShiftedToEvents(uiDevice, KEYCODE_MINUS)
192+
}
193+
}
194+
}
195+
196+
private fun keyPressShiftedToEvents(uiDevice: UiDevice, keyCode: Int) {
197+
uiDevice.pressKeyCode(keyCode, META_SHIFT_LEFT_ON)
198+
}
106199
}

maestro-client/src/main/java/maestro/drivers/AndroidDriver.kt

+4-3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import maestro.android.asManifest
3737
import maestro.android.resolveLauncherActivity
3838
import maestro_android.MaestroDriverGrpc
3939
import maestro_android.deviceInfoRequest
40+
import maestro_android.inputTextRequest
4041
import maestro_android.tapRequest
4142
import maestro_android.viewHierarchyRequest
4243
import okio.Sink
@@ -304,9 +305,9 @@ class AndroidDriver(
304305
}
305306

306307
override fun inputText(text: String) {
307-
text.chunked(3).forEach {
308-
dadb.shell("input text \"$it\"")
309-
}
308+
blockingStub.inputText(inputTextRequest {
309+
this.text = text
310+
}) ?: throw IllegalStateException("Input Response can't be null")
310311
}
311312

312313
override fun openLink(link: String) {
Binary file not shown.
Binary file not shown.

maestro-proto/src/main/proto/maestro_android.proto

+6
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ service MaestroDriver {
1010

1111
rpc tap(TapRequest) returns (TapResponse) {}
1212

13+
rpc inputText(InputTextRequest) returns (InputTextResponse) {}
1314
}
1415

1516
// Device info
@@ -35,3 +36,8 @@ message TapRequest {
3536
}
3637

3738
message TapResponse {}
39+
40+
message InputTextRequest {
41+
string text = 1;
42+
}
43+
message InputTextResponse {}

0 commit comments

Comments
 (0)