xref: /aosp_15_r20/cts/libs/input/src/com/android/cts/input/InjectInputInProcess.kt (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1 /*
2  * Copyright 2024 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 
17 package com.android.cts.input.injectinputinprocess
18 
19 import android.os.Looper
20 import android.view.InputDevice
21 import android.view.MotionEvent
22 import android.view.View
23 import com.android.cts.input.MotionEventBuilder
24 import com.android.cts.input.PointerBuilder
25 import java.util.concurrent.ExecutionException
26 import java.util.concurrent.FutureTask
27 import org.junit.Assert.fail
28 
29 /**
30  * Click on the provided view.
31  * May be invoked on the test thread, or on the UI thread. This is only possible to use when the
32  * test is running in the same process as the Android application.
33  *
34  * @param view the view on which to click.
35  * @param x the x location where to click, relative to the View's top left corner
36  * @param y the y location where to click, relative to the View's top left corner
37  */
clickOnViewnull38 fun clickOnView(view: View, x: Int, y: Int) {
39     val clickTask = FutureTask {
40         clickOnViewOnUiThread(view, x, y)
41     }
42     // If we are already on UI thread, then execute the code directly. Otherwise, post the click
43     // task and wait for it to complete.
44     if (Looper.getMainLooper().isCurrentThread) {
45         clickTask.run()
46     } else {
47         view.post(clickTask)
48     }
49 
50     try {
51         clickTask.get() // this will block until FutureTask completes on the main thread
52     } catch (e: InterruptedException) {
53         fail("Interrupted while waiting for the click to be processed: $e" )
54     } catch (e: ExecutionException) {
55         fail("Execution failed while waiting for the click to be processed: $e" )
56     }
57 }
58 
59 /**
60  * Click on the center of the provided view.
61  * May be invoked on the test thread, or on the UI thread. This is only possible to use when the
62  * test is running in the same process as the Android application.
63  *
64  * @param view the view on which to click.
65  */
clickOnViewCenternull66 fun clickOnViewCenter(view: View) {
67     return clickOnView(view, view.width / 2, view.height / 2)
68 }
69 
getViewLocationInWindownull70 private fun getViewLocationInWindow(view: View): Pair<Int, Int> {
71     val xy = IntArray(2)
72     view.getLocationInWindow(xy)
73     return Pair(xy[0], xy[1])
74 }
75 
clickOnViewOnUiThreadnull76 private fun clickOnViewOnUiThread(view: View, x: Int, y: Int) {
77     val (viewX, viewY) = getViewLocationInWindow(view)
78     val clickX = viewX + x
79     val clickY = viewY + y
80     val event = MotionEventBuilder(
81         MotionEvent.ACTION_DOWN,
82         InputDevice.SOURCE_TOUCHSCREEN
83     ).pointer(
84         PointerBuilder(0, MotionEvent.TOOL_TYPE_FINGER).x(clickX.toFloat()).y(clickY.toFloat())
85     ).build()
86 
87     view.dispatchTouchEvent(event)
88     event.action = MotionEvent.ACTION_UP
89     view.dispatchTouchEvent(event)
90 }
91