1 /*
2 * Copyright 2025 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 androidx.test.uiautomator.internal
18
19 import android.content.Context.DISPLAY_SERVICE
20 import android.graphics.Bitmap
21 import android.graphics.Rect
22 import android.hardware.display.DisplayManager
23 import android.os.Environment
24 import android.os.SystemClock
25 import android.view.KeyCharacterMap
26 import android.view.accessibility.AccessibilityNodeInfo
27 import androidx.test.platform.app.InstrumentationRegistry
28 import androidx.test.uiautomator.UiDevice
29
30 internal val instrumentation = InstrumentationRegistry.getInstrumentation()
31 internal val uiAutomation = instrumentation.uiAutomation
32 internal val uiDevice = UiDevice.getInstance(instrumentation)
33 internal val keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD)
34 internal val displayManager =
35 instrumentation.context.getSystemService(DISPLAY_SERVICE) as DisplayManager
36
nowMsnull37 internal fun nowMs() = SystemClock.uptimeMillis()
38
39 /**
40 * Asserts that the receiver is not null, otherwise throws the given [t] exception.
41 *
42 * @param t the exception to throw if this receiver is null.
43 */
44 internal fun <T> T?.notNull(t: Throwable): T = this ?: throw t
45
46 /**
47 * Deprecation is suppressed because this is the folder Android Studio uses to read instrumentation
48 * test results.
49 */
50 @Suppress("DEPRECATION")
51 internal val instrumentationPackageMediaDir =
52 InstrumentationRegistry.getInstrumentation().targetContext.externalMediaDirs.firstOrNull {
53 Environment.getExternalStorageState(it) == Environment.MEDIA_MOUNTED
54 } ?: throw IllegalStateException("Cannot get external storage because it's not mounted")
55
joinLinesnull56 internal fun joinLines(vararg lines: String) =
57 lines.joinToString(separator = System.lineSeparator())
58
59 internal fun takeScreenshotBitmap(bounds: Rect): Bitmap {
60 val bitmap = uiAutomation.takeScreenshot()
61 val actualBounds =
62 Rect().apply {
63 left = 0
64 top = 0
65 right = bitmap.width
66 bottom = bitmap.height
67 }
68 bounds.intersect(actualBounds)
69 val cropped =
70 Bitmap.createBitmap(
71 bitmap,
72 bounds.left,
73 bounds.top,
74 bounds.width(),
75 bounds.height(),
76 )
77 return cropped
78 }
79
takeViewNodeTreenull80 internal fun takeViewNodeTree(
81 root: AccessibilityNodeInfo,
82 displayRect: Rect,
83 ) =
84 ViewNode.fromAccessibilityNodeInfo(
85 depth = 0,
86 node = root,
87 displayRect = displayRect,
88 )
89