appium-adb 14.0.4 → 14.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/build/lib/tools/apk-utils.d.ts +44 -67
- package/build/lib/tools/apk-utils.d.ts.map +1 -1
- package/build/lib/tools/apk-utils.js +74 -76
- package/build/lib/tools/apk-utils.js.map +1 -1
- package/build/lib/tools/app-commands.d.ts +125 -150
- package/build/lib/tools/app-commands.d.ts.map +1 -1
- package/build/lib/tools/app-commands.js +194 -234
- package/build/lib/tools/app-commands.js.map +1 -1
- package/build/lib/tools/device-settings.d.ts +103 -142
- package/build/lib/tools/device-settings.d.ts.map +1 -1
- package/build/lib/tools/device-settings.js +84 -124
- package/build/lib/tools/device-settings.js.map +1 -1
- package/lib/tools/{apk-utils.js → apk-utils.ts} +118 -105
- package/lib/tools/{app-commands.js → app-commands.ts} +279 -294
- package/lib/tools/{device-settings.js → device-settings.ts} +136 -172
- package/package.json +1 -1
|
@@ -3,30 +3,32 @@ import _ from 'lodash';
|
|
|
3
3
|
import { retryInterval } from 'asyncbox';
|
|
4
4
|
import { util } from '@appium/support';
|
|
5
5
|
import B from 'bluebird';
|
|
6
|
+
import type { ADB } from '../adb.js';
|
|
7
|
+
import type { SetPropOpts } from './types.js';
|
|
8
|
+
import type { ExecError } from 'teen_process';
|
|
6
9
|
|
|
7
10
|
const ANIMATION_SCALE_KEYS = [
|
|
8
11
|
'animator_duration_scale',
|
|
9
12
|
'transition_animation_scale',
|
|
10
13
|
'window_animation_scale'
|
|
11
|
-
];
|
|
14
|
+
] as const;
|
|
12
15
|
const HIDDEN_API_POLICY_KEYS = [
|
|
13
16
|
'hidden_api_policy_pre_p_apps',
|
|
14
17
|
'hidden_api_policy_p_apps',
|
|
15
18
|
'hidden_api_policy'
|
|
16
|
-
];
|
|
19
|
+
] as const;
|
|
17
20
|
|
|
18
21
|
/**
|
|
19
22
|
* Get the particular property of the device under test.
|
|
20
23
|
*
|
|
21
|
-
* @
|
|
22
|
-
* @param {string} property - The name of the property. This name should
|
|
24
|
+
* @param property - The name of the property. This name should
|
|
23
25
|
* be known to _adb shell getprop_ tool.
|
|
24
26
|
*
|
|
25
|
-
* @
|
|
27
|
+
* @returns The value of the given property.
|
|
26
28
|
*/
|
|
27
|
-
export async function getDeviceProperty (property) {
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
export async function getDeviceProperty (this: ADB, property: string): Promise<string> {
|
|
30
|
+
const stdout = await this.shell(['getprop', property]);
|
|
31
|
+
const val = stdout.trim();
|
|
30
32
|
log.debug(`Current device property '${property}': ${val}`);
|
|
31
33
|
return val;
|
|
32
34
|
}
|
|
@@ -34,15 +36,14 @@ export async function getDeviceProperty (property) {
|
|
|
34
36
|
/**
|
|
35
37
|
* Set the particular property of the device under test.
|
|
36
38
|
*
|
|
37
|
-
* @
|
|
38
|
-
* @param {string} prop - The name of the property. This name should
|
|
39
|
+
* @param prop - The name of the property. This name should
|
|
39
40
|
* be known to _adb shell setprop_ tool.
|
|
40
|
-
* @param
|
|
41
|
-
* @param
|
|
41
|
+
* @param val - The new property value.
|
|
42
|
+
* @param opts
|
|
42
43
|
*
|
|
43
|
-
* @throws
|
|
44
|
+
* @throws If _setprop_ utility fails to change property value.
|
|
44
45
|
*/
|
|
45
|
-
export async function setDeviceProperty (prop, val, opts = {}) {
|
|
46
|
+
export async function setDeviceProperty (this: ADB, prop: string, val: string, opts: SetPropOpts = {}): Promise<void> {
|
|
46
47
|
const {privileged = true} = opts;
|
|
47
48
|
log.debug(`Setting device property '${prop}' to '${val}'`);
|
|
48
49
|
await this.shell(['setprop', prop, val], {
|
|
@@ -51,79 +52,70 @@ export async function setDeviceProperty (prop, val, opts = {}) {
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
/**
|
|
54
|
-
* @
|
|
55
|
-
* @return {Promise<string>} Current system language on the device under test.
|
|
55
|
+
* @returns Current system language on the device under test.
|
|
56
56
|
*/
|
|
57
|
-
export async function getDeviceSysLanguage () {
|
|
57
|
+
export async function getDeviceSysLanguage (this: ADB): Promise<string> {
|
|
58
58
|
return await this.getDeviceProperty('persist.sys.language');
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
|
-
* @
|
|
63
|
-
* @return {Promise<string>} Current country name on the device under test.
|
|
62
|
+
* @returns Current country name on the device under test.
|
|
64
63
|
*/
|
|
65
|
-
export async function getDeviceSysCountry () {
|
|
64
|
+
export async function getDeviceSysCountry (this: ADB): Promise<string> {
|
|
66
65
|
return await this.getDeviceProperty('persist.sys.country');
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
/**
|
|
70
|
-
* @
|
|
71
|
-
* @return {Promise<string>} Current system locale name on the device under test.
|
|
69
|
+
* @returns Current system locale name on the device under test.
|
|
72
70
|
*/
|
|
73
|
-
export async function getDeviceSysLocale () {
|
|
71
|
+
export async function getDeviceSysLocale (this: ADB): Promise<string> {
|
|
74
72
|
return await this.getDeviceProperty('persist.sys.locale');
|
|
75
73
|
}
|
|
76
74
|
|
|
77
75
|
/**
|
|
78
|
-
* @
|
|
79
|
-
* @return {Promise<string>} Current product language name on the device under test.
|
|
76
|
+
* @returns Current product language name on the device under test.
|
|
80
77
|
*/
|
|
81
|
-
export async function getDeviceProductLanguage () {
|
|
78
|
+
export async function getDeviceProductLanguage (this: ADB): Promise<string> {
|
|
82
79
|
return await this.getDeviceProperty('ro.product.locale.language');
|
|
83
80
|
}
|
|
84
81
|
|
|
85
82
|
/**
|
|
86
|
-
* @
|
|
87
|
-
* @return {Promise<string>} Current product country name on the device under test.
|
|
83
|
+
* @returns Current product country name on the device under test.
|
|
88
84
|
*/
|
|
89
|
-
export async function getDeviceProductCountry () {
|
|
85
|
+
export async function getDeviceProductCountry (this: ADB): Promise<string> {
|
|
90
86
|
return await this.getDeviceProperty('ro.product.locale.region');
|
|
91
87
|
}
|
|
92
88
|
|
|
93
89
|
/**
|
|
94
|
-
* @
|
|
95
|
-
* @return {Promise<string>} Current product locale name on the device under test.
|
|
90
|
+
* @returns Current product locale name on the device under test.
|
|
96
91
|
*/
|
|
97
|
-
export async function getDeviceProductLocale () {
|
|
92
|
+
export async function getDeviceProductLocale (this: ADB): Promise<string> {
|
|
98
93
|
return await this.getDeviceProperty('ro.product.locale');
|
|
99
94
|
}
|
|
100
95
|
|
|
101
96
|
/**
|
|
102
|
-
* @
|
|
103
|
-
* @return {Promise<string>} The model name of the device under test.
|
|
97
|
+
* @returns The model name of the device under test.
|
|
104
98
|
*/
|
|
105
|
-
export async function getModel () {
|
|
99
|
+
export async function getModel (this: ADB): Promise<string> {
|
|
106
100
|
return await this.getDeviceProperty('ro.product.model');
|
|
107
101
|
}
|
|
108
102
|
|
|
109
103
|
/**
|
|
110
|
-
* @
|
|
111
|
-
* @return {Promise<string>} The manufacturer name of the device under test.
|
|
104
|
+
* @returns The manufacturer name of the device under test.
|
|
112
105
|
*/
|
|
113
|
-
export async function getManufacturer () {
|
|
106
|
+
export async function getManufacturer (this: ADB): Promise<string> {
|
|
114
107
|
return await this.getDeviceProperty('ro.product.manufacturer');
|
|
115
108
|
}
|
|
116
109
|
|
|
117
110
|
/**
|
|
118
111
|
* Get the current screen size.
|
|
119
112
|
*
|
|
120
|
-
* @
|
|
121
|
-
* @return {Promise<string?>} Device screen size as string in format 'WxH' or
|
|
113
|
+
* @returns Device screen size as string in format 'WxH' or
|
|
122
114
|
* _null_ if it cannot be determined.
|
|
123
115
|
*/
|
|
124
|
-
export async function getScreenSize () {
|
|
125
|
-
|
|
126
|
-
|
|
116
|
+
export async function getScreenSize (this: ADB): Promise<string | null> {
|
|
117
|
+
const stdout = await this.shell(['wm', 'size']);
|
|
118
|
+
const size = new RegExp(/Physical size: ([^\r?\n]+)*/g).exec(stdout);
|
|
127
119
|
if (size && size.length >= 2) {
|
|
128
120
|
return size[1].trim();
|
|
129
121
|
}
|
|
@@ -133,15 +125,14 @@ export async function getScreenSize () {
|
|
|
133
125
|
/**
|
|
134
126
|
* Get the current screen density in dpi
|
|
135
127
|
*
|
|
136
|
-
* @
|
|
137
|
-
* @return {Promise<number?>} Device screen density as a number or _null_ if it
|
|
128
|
+
* @returns Device screen density as a number or _null_ if it
|
|
138
129
|
* cannot be determined
|
|
139
130
|
*/
|
|
140
|
-
export async function getScreenDensity () {
|
|
141
|
-
|
|
142
|
-
|
|
131
|
+
export async function getScreenDensity (this: ADB): Promise<number | null> {
|
|
132
|
+
const stdout = await this.shell(['wm', 'density']);
|
|
133
|
+
const density = new RegExp(/Physical density: ([^\r?\n]+)*/g).exec(stdout);
|
|
143
134
|
if (density && density.length >= 2) {
|
|
144
|
-
|
|
135
|
+
const densityNumber = parseInt(density[1].trim(), 10);
|
|
145
136
|
return isNaN(densityNumber) ? null : densityNumber;
|
|
146
137
|
}
|
|
147
138
|
return null;
|
|
@@ -151,12 +142,11 @@ export async function getScreenDensity () {
|
|
|
151
142
|
* Setup HTTP proxy in device global settings.
|
|
152
143
|
* Read https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r21/core/java/android/provider/Settings.java for each property
|
|
153
144
|
*
|
|
154
|
-
* @
|
|
155
|
-
* @param
|
|
156
|
-
* @param {string|number} proxyPort - The port number to be set.
|
|
145
|
+
* @param proxyHost - The host name of the proxy.
|
|
146
|
+
* @param proxyPort - The port number to be set.
|
|
157
147
|
*/
|
|
158
|
-
export async function setHttpProxy (proxyHost, proxyPort) {
|
|
159
|
-
|
|
148
|
+
export async function setHttpProxy (this: ADB, proxyHost: string, proxyPort: string | number): Promise<void> {
|
|
149
|
+
const proxy = `${proxyHost}:${proxyPort}`;
|
|
160
150
|
if (_.isUndefined(proxyHost)) {
|
|
161
151
|
throw new Error(`Call to setHttpProxy method with undefined proxy_host: ${proxy}`);
|
|
162
152
|
}
|
|
@@ -165,7 +155,7 @@ export async function setHttpProxy (proxyHost, proxyPort) {
|
|
|
165
155
|
}
|
|
166
156
|
|
|
167
157
|
/** @type {[string, string][]} */
|
|
168
|
-
const httpProxySettins = [
|
|
158
|
+
const httpProxySettins: [string, string][] = [
|
|
169
159
|
['http_proxy', proxy],
|
|
170
160
|
['global_http_proxy_host', proxyHost],
|
|
171
161
|
['global_http_proxy_port', `${proxyPort}`]
|
|
@@ -178,9 +168,8 @@ export async function setHttpProxy (proxyHost, proxyPort) {
|
|
|
178
168
|
/**
|
|
179
169
|
* Delete HTTP proxy in device global settings.
|
|
180
170
|
* Rebooting the test device is necessary to apply the change.
|
|
181
|
-
* @this {import('../adb.js').ADB}
|
|
182
171
|
*/
|
|
183
|
-
export async function deleteHttpProxy () {
|
|
172
|
+
export async function deleteHttpProxy (this: ADB): Promise<void> {
|
|
184
173
|
const httpProxySettins = [
|
|
185
174
|
'http_proxy',
|
|
186
175
|
'global_http_proxy_host',
|
|
@@ -196,13 +185,12 @@ export async function deleteHttpProxy () {
|
|
|
196
185
|
* Set device property.
|
|
197
186
|
* [android.provider.Settings]{@link https://developer.android.com/reference/android/provider/Settings.html}
|
|
198
187
|
*
|
|
199
|
-
* @
|
|
200
|
-
* @param
|
|
201
|
-
* @param
|
|
202
|
-
* @
|
|
203
|
-
* @return {Promise<string>} command output.
|
|
188
|
+
* @param namespace - one of {system, secure, global}, case-insensitive.
|
|
189
|
+
* @param setting - property name.
|
|
190
|
+
* @param value - property value.
|
|
191
|
+
* @returns command output.
|
|
204
192
|
*/
|
|
205
|
-
export async function setSetting (namespace, setting, value) {
|
|
193
|
+
export async function setSetting (this: ADB, namespace: string, setting: string, value: string | number): Promise<string> {
|
|
206
194
|
return await this.shell(['settings', 'put', namespace, setting, `${value}`]);
|
|
207
195
|
}
|
|
208
196
|
|
|
@@ -210,46 +198,45 @@ export async function setSetting (namespace, setting, value) {
|
|
|
210
198
|
* Get device property.
|
|
211
199
|
* [android.provider.Settings]{@link https://developer.android.com/reference/android/provider/Settings.html}
|
|
212
200
|
*
|
|
213
|
-
* @
|
|
214
|
-
* @param
|
|
215
|
-
* @
|
|
216
|
-
* @return {Promise<string>} property value.
|
|
201
|
+
* @param namespace - one of {system, secure, global}, case-insensitive.
|
|
202
|
+
* @param setting - property name.
|
|
203
|
+
* @returns property value.
|
|
217
204
|
*/
|
|
218
|
-
export async function getSetting (namespace, setting) {
|
|
205
|
+
export async function getSetting (this: ADB, namespace: string, setting: string): Promise<string> {
|
|
219
206
|
return await this.shell(['settings', 'get', namespace, setting]);
|
|
220
207
|
}
|
|
221
208
|
|
|
222
209
|
/**
|
|
223
210
|
* Get tz database time zone formatted timezone
|
|
224
211
|
*
|
|
225
|
-
* @
|
|
226
|
-
* @
|
|
227
|
-
* @throws {Error} If any exception is reported by adb shell.
|
|
212
|
+
* @returns TZ database Time Zones format
|
|
213
|
+
* @throws If any exception is reported by adb shell.
|
|
228
214
|
*/
|
|
229
|
-
export async function getTimeZone () {
|
|
215
|
+
export async function getTimeZone (this: ADB): Promise<string> {
|
|
230
216
|
log.debug('Getting current timezone');
|
|
231
217
|
try {
|
|
232
218
|
return await this.getDeviceProperty('persist.sys.timezone');
|
|
233
219
|
} catch (e) {
|
|
234
|
-
|
|
220
|
+
const err = e as Error;
|
|
221
|
+
throw new Error(`Error getting timezone. Original error: ${err.message}`);
|
|
235
222
|
}
|
|
236
223
|
}
|
|
237
224
|
|
|
238
225
|
/**
|
|
239
226
|
* Retrieve the platform version of the device under test.
|
|
240
227
|
*
|
|
241
|
-
* @
|
|
242
|
-
* @return {Promise<string>} The platform version as a string, for example '5.0' for
|
|
228
|
+
* @returns The platform version as a string, for example '5.0' for
|
|
243
229
|
* Android Lollipop.
|
|
244
230
|
*/
|
|
245
|
-
export async function getPlatformVersion () {
|
|
231
|
+
export async function getPlatformVersion (this: ADB): Promise<string> {
|
|
246
232
|
log.info('Getting device platform version');
|
|
247
233
|
try {
|
|
248
234
|
return await this.getDeviceProperty('ro.build.version.release');
|
|
249
235
|
} catch (e) {
|
|
236
|
+
const err = e as Error;
|
|
250
237
|
throw new Error(
|
|
251
238
|
`Error getting device platform version. ` +
|
|
252
|
-
`Original error: ${
|
|
239
|
+
`Original error: ${err.message}`
|
|
253
240
|
);
|
|
254
241
|
}
|
|
255
242
|
}
|
|
@@ -258,10 +245,9 @@ export async function getPlatformVersion () {
|
|
|
258
245
|
/**
|
|
259
246
|
* Retrieve the list of location providers for the device under test.
|
|
260
247
|
*
|
|
261
|
-
* @
|
|
262
|
-
* @return {Promise<string[]>} The list of available location providers or an empty list.
|
|
248
|
+
* @returns The list of available location providers or an empty list.
|
|
263
249
|
*/
|
|
264
|
-
export async function getLocationProviders () {
|
|
250
|
+
export async function getLocationProviders (this: ADB): Promise<string[]> {
|
|
265
251
|
if (await this.getApiLevel() < 31) {
|
|
266
252
|
// https://stackoverflow.com/questions/70939503/settings-secure-location-providers-allowed-returns-null-in-android-12
|
|
267
253
|
const stdout = await this.getSetting('secure', 'location_providers_allowed');
|
|
@@ -279,10 +265,9 @@ export async function getLocationProviders () {
|
|
|
279
265
|
/**
|
|
280
266
|
* Toggle the state of GPS location provider.
|
|
281
267
|
*
|
|
282
|
-
* @
|
|
283
|
-
* @param {boolean} enabled - Whether to enable (true) or disable (false) the GPS provider.
|
|
268
|
+
* @param enabled - Whether to enable (true) or disable (false) the GPS provider.
|
|
284
269
|
*/
|
|
285
|
-
export async function toggleGPSLocationProvider (enabled) {
|
|
270
|
+
export async function toggleGPSLocationProvider (this: ADB, enabled: boolean): Promise<void> {
|
|
286
271
|
if (await this.getApiLevel() < 31) {
|
|
287
272
|
// https://stackoverflow.com/questions/70939503/settings-secure-location-providers-allowed-returns-null-in-android-12
|
|
288
273
|
await this.setSetting('secure', 'location_providers_allowed', `${enabled ? '+' : '-'}gps`);
|
|
@@ -294,10 +279,10 @@ export async function toggleGPSLocationProvider (enabled) {
|
|
|
294
279
|
/**
|
|
295
280
|
* Decorates an exception message with a solution link
|
|
296
281
|
*
|
|
297
|
-
* @param
|
|
298
|
-
* @returns
|
|
282
|
+
* @param e The error object to be decorated
|
|
283
|
+
* @returns Either the same error or the decorated one
|
|
299
284
|
*/
|
|
300
|
-
function decorateWriteSecureSettingsException (e) {
|
|
285
|
+
function decorateWriteSecureSettingsException (e: Error): Error {
|
|
301
286
|
if (_.includes(e.message, 'requires:android.permission.WRITE_SECURE_SETTINGS')) {
|
|
302
287
|
e.message = `Check https://github.com/appium/appium/issues/13802 for throubleshooting. ${e.message}`;
|
|
303
288
|
}
|
|
@@ -308,8 +293,7 @@ function decorateWriteSecureSettingsException (e) {
|
|
|
308
293
|
* Set hidden api policy to manage access to non-SDK APIs.
|
|
309
294
|
* https://developer.android.com/preview/restrictions-non-sdk-interfaces
|
|
310
295
|
*
|
|
311
|
-
* @
|
|
312
|
-
* @param {number|string} value - The API enforcement policy.
|
|
296
|
+
* @param value - The API enforcement policy.
|
|
313
297
|
* For Android P
|
|
314
298
|
* 0: Disable non-SDK API usage detection. This will also disable logging, and also break the strict mode API,
|
|
315
299
|
* detectNonSdkApiUsage(). Not recommended.
|
|
@@ -327,15 +311,15 @@ function decorateWriteSecureSettingsException (e) {
|
|
|
327
311
|
* 2: Disallow usage of non-SDK interfaces that belong to either the black list
|
|
328
312
|
* or to a restricted greylist for your target API level.
|
|
329
313
|
*
|
|
330
|
-
* @param
|
|
331
|
-
* @throws
|
|
314
|
+
* @param ignoreError - Whether to ignore an exception in 'adb shell settings put global' command
|
|
315
|
+
* @throws If there was an error and ignoreError was true while executing 'adb shell settings put global'
|
|
332
316
|
* command on the device under test.
|
|
333
317
|
*/
|
|
334
|
-
export async function setHiddenApiPolicy (value, ignoreError = false) {
|
|
318
|
+
export async function setHiddenApiPolicy (this: ADB, value: number | string, ignoreError = false): Promise<void> {
|
|
335
319
|
try {
|
|
336
320
|
await this.shell(HIDDEN_API_POLICY_KEYS.map((k) => `settings put global ${k} ${value}`).join(';'));
|
|
337
321
|
} catch (e) {
|
|
338
|
-
const err =
|
|
322
|
+
const err = e as Error;
|
|
339
323
|
if (!ignoreError) {
|
|
340
324
|
throw decorateWriteSecureSettingsException(err);
|
|
341
325
|
}
|
|
@@ -350,16 +334,15 @@ export async function setHiddenApiPolicy (value, ignoreError = false) {
|
|
|
350
334
|
* Reset access to non-SDK APIs to its default setting.
|
|
351
335
|
* https://developer.android.com/preview/restrictions-non-sdk-interfaces
|
|
352
336
|
*
|
|
353
|
-
* @
|
|
354
|
-
* @
|
|
355
|
-
* @throws {error} If there was an error and ignoreError was true while executing 'adb shell settings delete global'
|
|
337
|
+
* @param ignoreError - Whether to ignore an exception in 'adb shell settings delete global' command
|
|
338
|
+
* @throws If there was an error and ignoreError was true while executing 'adb shell settings delete global'
|
|
356
339
|
* command on the device under test.
|
|
357
340
|
*/
|
|
358
|
-
export async function setDefaultHiddenApiPolicy (ignoreError = false) {
|
|
341
|
+
export async function setDefaultHiddenApiPolicy (this: ADB, ignoreError = false): Promise<void> {
|
|
359
342
|
try {
|
|
360
343
|
await this.shell(HIDDEN_API_POLICY_KEYS.map((k) => `settings delete global ${k}`).join(';'));
|
|
361
344
|
} catch (e) {
|
|
362
|
-
const err =
|
|
345
|
+
const err = e as Error;
|
|
363
346
|
if (!ignoreError) {
|
|
364
347
|
throw decorateWriteSecureSettingsException(err);
|
|
365
348
|
}
|
|
@@ -371,10 +354,9 @@ export async function setDefaultHiddenApiPolicy (ignoreError = false) {
|
|
|
371
354
|
/**
|
|
372
355
|
* Get the language name of the device under test.
|
|
373
356
|
*
|
|
374
|
-
* @
|
|
375
|
-
* @return {Promise<string>} The name of device language.
|
|
357
|
+
* @returns The name of device language.
|
|
376
358
|
*/
|
|
377
|
-
export async function getDeviceLanguage () {
|
|
359
|
+
export async function getDeviceLanguage (this: ADB): Promise<string> {
|
|
378
360
|
return await this.getApiLevel() < 23
|
|
379
361
|
? (await this.getDeviceSysLanguage() || await this.getDeviceProductLanguage())
|
|
380
362
|
: (await this.getDeviceLocale()).split('-')[0];
|
|
@@ -384,10 +366,9 @@ export async function getDeviceLanguage () {
|
|
|
384
366
|
* Get the country name of the device under test.
|
|
385
367
|
*
|
|
386
368
|
* @summary Could only be used for Android API < 23
|
|
387
|
-
* @
|
|
388
|
-
* @return {Promise<string>} The name of device country.
|
|
369
|
+
* @returns The name of device country.
|
|
389
370
|
*/
|
|
390
|
-
export async function getDeviceCountry () {
|
|
371
|
+
export async function getDeviceCountry (this: ADB): Promise<string> {
|
|
391
372
|
return await this.getDeviceSysCountry() || await this.getDeviceProductCountry();
|
|
392
373
|
}
|
|
393
374
|
|
|
@@ -395,25 +376,23 @@ export async function getDeviceCountry () {
|
|
|
395
376
|
* Get the locale name of the device under test.
|
|
396
377
|
*
|
|
397
378
|
* @summary Could only be used for Android API >= 23
|
|
398
|
-
* @
|
|
399
|
-
* @return {Promise<string>} The name of device locale.
|
|
379
|
+
* @returns The name of device locale.
|
|
400
380
|
*/
|
|
401
|
-
export async function getDeviceLocale () {
|
|
381
|
+
export async function getDeviceLocale (this: ADB): Promise<string> {
|
|
402
382
|
return await this.getDeviceSysLocale() || await this.getDeviceProductLocale();
|
|
403
383
|
}
|
|
404
384
|
|
|
405
385
|
/**
|
|
406
386
|
* Make sure current device locale is expected or not.
|
|
407
387
|
*
|
|
408
|
-
* @this {import('../adb.js').ADB}
|
|
409
388
|
* @privateRemarks FIXME: language or country is required
|
|
410
|
-
* @param
|
|
411
|
-
* @param
|
|
412
|
-
* @param
|
|
389
|
+
* @param language - Language. The language field is case insensitive, but Locale always canonicalizes to lower case.
|
|
390
|
+
* @param country - Country. The language field is case insensitive, but Locale always canonicalizes to lower case.
|
|
391
|
+
* @param script - Script. The script field is case insensitive but Locale always canonicalizes to title case.
|
|
413
392
|
*
|
|
414
|
-
* @
|
|
393
|
+
* @returns If current locale is language and country as arguments, return true.
|
|
415
394
|
*/
|
|
416
|
-
export async function ensureCurrentLocale (language, country, script) {
|
|
395
|
+
export async function ensureCurrentLocale (this: ADB, language?: string, country?: string, script?: string): Promise<boolean> {
|
|
417
396
|
const hasLanguage = _.isString(language);
|
|
418
397
|
const hasCountry = _.isString(country);
|
|
419
398
|
if (!hasLanguage && !hasCountry) {
|
|
@@ -424,10 +403,10 @@ export async function ensureCurrentLocale (language, country, script) {
|
|
|
424
403
|
const lcLanguage = (language || '').toLowerCase();
|
|
425
404
|
const lcCountry = (country || '').toLowerCase();
|
|
426
405
|
const apiLevel = await this.getApiLevel();
|
|
427
|
-
return
|
|
406
|
+
return (await retryInterval(5, 1000, async () => {
|
|
428
407
|
if (apiLevel < 23) {
|
|
429
408
|
log.debug(`Requested locale: ${lcLanguage}-${lcCountry}`);
|
|
430
|
-
let actualLanguage;
|
|
409
|
+
let actualLanguage: string | undefined;
|
|
431
410
|
if (hasLanguage) {
|
|
432
411
|
actualLanguage = (await this.getDeviceLanguage()).toLowerCase();
|
|
433
412
|
log.debug(`Actual language: ${actualLanguage}`);
|
|
@@ -435,7 +414,7 @@ export async function ensureCurrentLocale (language, country, script) {
|
|
|
435
414
|
return true;
|
|
436
415
|
}
|
|
437
416
|
}
|
|
438
|
-
let actualCountry;
|
|
417
|
+
let actualCountry: string | undefined;
|
|
439
418
|
if (hasCountry) {
|
|
440
419
|
actualCountry = (await this.getDeviceCountry()).toLowerCase();
|
|
441
420
|
log.debug(`Actual country: ${actualCountry}`);
|
|
@@ -452,7 +431,7 @@ export async function ensureCurrentLocale (language, country, script) {
|
|
|
452
431
|
: `${lcLanguage}-${lcCountry}`;
|
|
453
432
|
log.debug(`Requested locale: ${expectedLocale}. Actual locale: '${actualLocale}'`);
|
|
454
433
|
const languagePattern = `^${_.escapeRegExp(lcLanguage)}-${script ? (_.escapeRegExp(script) + '-') : ''}`;
|
|
455
|
-
const checkLocalePattern = (
|
|
434
|
+
const checkLocalePattern = (p: string) => new RegExp(p, 'i').test(actualLocale);
|
|
456
435
|
if (hasLanguage && !hasCountry) {
|
|
457
436
|
return checkLocalePattern(languagePattern);
|
|
458
437
|
}
|
|
@@ -461,19 +440,18 @@ export async function ensureCurrentLocale (language, country, script) {
|
|
|
461
440
|
return checkLocalePattern(countryPattern);
|
|
462
441
|
}
|
|
463
442
|
return [languagePattern, countryPattern].every(checkLocalePattern);
|
|
464
|
-
}));
|
|
443
|
+
})) ?? false;
|
|
465
444
|
}
|
|
466
445
|
|
|
467
446
|
/**
|
|
468
447
|
* Change the state of WiFi on the device under test.
|
|
469
448
|
* Only works for real devices since API 30
|
|
470
449
|
*
|
|
471
|
-
* @
|
|
472
|
-
* @param
|
|
473
|
-
* @param {boolean} [isEmulator=false] - Set it to true if the device under test
|
|
450
|
+
* @param on - True to enable and false to disable it.
|
|
451
|
+
* @param isEmulator - Set it to true if the device under test
|
|
474
452
|
* is an emulator rather than a real device.
|
|
475
453
|
*/
|
|
476
|
-
export async function setWifiState (on, isEmulator = false) {
|
|
454
|
+
export async function setWifiState (this: ADB, on: boolean, isEmulator = false): Promise<void> {
|
|
477
455
|
if (isEmulator) {
|
|
478
456
|
// The svc command does not require to be root since API 26
|
|
479
457
|
await this.shell(['svc', 'wifi', on ? 'enable' : 'disable'], {
|
|
@@ -489,12 +467,11 @@ export async function setWifiState (on, isEmulator = false) {
|
|
|
489
467
|
* Change the state of Data transfer on the device under test.
|
|
490
468
|
* Only works for real devices since API 30
|
|
491
469
|
*
|
|
492
|
-
* @
|
|
493
|
-
* @param
|
|
494
|
-
* @param {boolean} [isEmulator=false] - Set it to true if the device under test
|
|
470
|
+
* @param on - True to enable and false to disable it.
|
|
471
|
+
* @param isEmulator - Set it to true if the device under test
|
|
495
472
|
* is an emulator rather than a real device.
|
|
496
473
|
*/
|
|
497
|
-
export async function setDataState (on, isEmulator = false) {
|
|
474
|
+
export async function setDataState (this: ADB, on: boolean, isEmulator = false): Promise<void> {
|
|
498
475
|
if (isEmulator) {
|
|
499
476
|
// The svc command does not require to be root since API 26
|
|
500
477
|
await this.shell(['svc', 'data', on ? 'enable' : 'disable'], {
|
|
@@ -510,13 +487,12 @@ export async function setDataState (on, isEmulator = false) {
|
|
|
510
487
|
/**
|
|
511
488
|
* Retrieves the list of packages from Doze whitelist on Android 8+
|
|
512
489
|
*
|
|
513
|
-
* @
|
|
514
|
-
* @returns {Promise<string[]>} The list of whitelisted packages. An example output:
|
|
490
|
+
* @returns The list of whitelisted packages. An example output:
|
|
515
491
|
* system,com.android.shell,2000
|
|
516
492
|
* system,com.google.android.cellbroadcastreceiver,10143
|
|
517
493
|
* user,io.appium.settings,10157
|
|
518
494
|
*/
|
|
519
|
-
export async function getDeviceIdleWhitelist () {
|
|
495
|
+
export async function getDeviceIdleWhitelist (this: ADB): Promise<string[]> {
|
|
520
496
|
if (await this.getApiLevel() < 23) {
|
|
521
497
|
// Doze mode has only been added since Android 6
|
|
522
498
|
return [];
|
|
@@ -532,14 +508,13 @@ export async function getDeviceIdleWhitelist () {
|
|
|
532
508
|
/**
|
|
533
509
|
* Adds an existing package(s) into the Doze whitelist on Android 8+
|
|
534
510
|
*
|
|
535
|
-
* @
|
|
536
|
-
* @param {...string} packages One or more packages to add. If the package
|
|
511
|
+
* @param packages One or more packages to add. If the package
|
|
537
512
|
* already exists in the whitelist then it is only going to be added once.
|
|
538
513
|
* If the package with the given name is not installed/not known then an error
|
|
539
514
|
* will be thrown.
|
|
540
|
-
* @returns
|
|
515
|
+
* @returns `true` if the command to add package(s) has been executed
|
|
541
516
|
*/
|
|
542
|
-
export async function addToDeviceIdleWhitelist (...packages) {
|
|
517
|
+
export async function addToDeviceIdleWhitelist (this: ADB, ...packages: string[]): Promise<boolean> {
|
|
543
518
|
if (_.isEmpty(packages) || await this.getApiLevel() < 23) {
|
|
544
519
|
// Doze mode has only been added since Android 6
|
|
545
520
|
return false;
|
|
@@ -554,10 +529,9 @@ export async function addToDeviceIdleWhitelist (...packages) {
|
|
|
554
529
|
/**
|
|
555
530
|
* Check the state of Airplane mode on the device under test.
|
|
556
531
|
*
|
|
557
|
-
* @
|
|
558
|
-
* @return {Promise<boolean>} True if Airplane mode is enabled.
|
|
532
|
+
* @returns True if Airplane mode is enabled.
|
|
559
533
|
*/
|
|
560
|
-
export async function isAirplaneModeOn () {
|
|
534
|
+
export async function isAirplaneModeOn (this: ADB): Promise<boolean> {
|
|
561
535
|
const stdout = await this.getSetting('global', 'airplane_mode_on');
|
|
562
536
|
return parseInt(stdout, 10) !== 0;
|
|
563
537
|
// Alternatively for Android 11+:
|
|
@@ -567,10 +541,9 @@ export async function isAirplaneModeOn () {
|
|
|
567
541
|
/**
|
|
568
542
|
* Change the state of Airplane mode in Settings on the device under test.
|
|
569
543
|
*
|
|
570
|
-
* @
|
|
571
|
-
* @param {boolean} on - True to enable the Airplane mode in Settings and false to disable it.
|
|
544
|
+
* @param on - True to enable the Airplane mode in Settings and false to disable it.
|
|
572
545
|
*/
|
|
573
|
-
export async function setAirplaneMode (on) {
|
|
546
|
+
export async function setAirplaneMode (this: ADB, on: boolean): Promise<void> {
|
|
574
547
|
if (await this.getApiLevel() < 30) {
|
|
575
548
|
// This requires to call broadcastAirplaneMode afterwards to apply
|
|
576
549
|
await this.setSetting('global', 'airplane_mode_on', on ? 1 : 0);
|
|
@@ -583,10 +556,9 @@ export async function setAirplaneMode (on) {
|
|
|
583
556
|
/**
|
|
584
557
|
* Change the state of the bluetooth service on the device under test.
|
|
585
558
|
*
|
|
586
|
-
* @
|
|
587
|
-
* @param {boolean} on - True to enable bluetooth service and false to disable it.
|
|
559
|
+
* @param on - True to enable bluetooth service and false to disable it.
|
|
588
560
|
*/
|
|
589
|
-
export async function setBluetoothOn (on) {
|
|
561
|
+
export async function setBluetoothOn (this: ADB, on: boolean): Promise<void> {
|
|
590
562
|
if (await this.getApiLevel() < 30) {
|
|
591
563
|
throw new Error('Changing of the bluetooth state is not supported on your device');
|
|
592
564
|
}
|
|
@@ -597,14 +569,13 @@ export async function setBluetoothOn (on) {
|
|
|
597
569
|
/**
|
|
598
570
|
* Change the state of the NFC service on the device under test.
|
|
599
571
|
*
|
|
600
|
-
* @
|
|
601
|
-
* @
|
|
602
|
-
* @throws {Error} If there was an error while changing the service state
|
|
572
|
+
* @param on - True to enable NFC service and false to disable it.
|
|
573
|
+
* @throws If there was an error while changing the service state
|
|
603
574
|
*/
|
|
604
|
-
export async function setNfcOn (on) {
|
|
575
|
+
export async function setNfcOn (this: ADB, on: boolean): Promise<void> {
|
|
605
576
|
const {stdout, stderr} = await this.shell(['svc', 'nfc', on ? 'enable' : 'disable'], {
|
|
606
577
|
outputFormat: 'full'
|
|
607
|
-
});
|
|
578
|
+
}) as {stdout: string; stderr: string};
|
|
608
579
|
const output = stderr || stdout;
|
|
609
580
|
log.debug(output);
|
|
610
581
|
if (output.includes('null NfcAdapter')) {
|
|
@@ -622,10 +593,9 @@ export async function setNfcOn (on) {
|
|
|
622
593
|
* there is a dedicated adb command to change airplane mode state, which
|
|
623
594
|
* does not require to call this one afterwards.
|
|
624
595
|
*
|
|
625
|
-
* @
|
|
626
|
-
* @param {boolean} on - True to broadcast enable and false to broadcast disable.
|
|
596
|
+
* @param on - True to broadcast enable and false to broadcast disable.
|
|
627
597
|
*/
|
|
628
|
-
export async function broadcastAirplaneMode (on) {
|
|
598
|
+
export async function broadcastAirplaneMode (this: ADB, on: boolean): Promise<void> {
|
|
629
599
|
const args = [
|
|
630
600
|
'am', 'broadcast',
|
|
631
601
|
'-a', 'android.intent.action.AIRPLANE_MODE',
|
|
@@ -634,7 +604,7 @@ export async function broadcastAirplaneMode (on) {
|
|
|
634
604
|
try {
|
|
635
605
|
await this.shell(args);
|
|
636
606
|
} catch (e) {
|
|
637
|
-
const err =
|
|
607
|
+
const err = e as ExecError;
|
|
638
608
|
// https://github.com/appium/appium/issues/17422
|
|
639
609
|
if (_.includes(err.stderr, 'SecurityException')) {
|
|
640
610
|
try {
|
|
@@ -649,10 +619,9 @@ export async function broadcastAirplaneMode (on) {
|
|
|
649
619
|
/**
|
|
650
620
|
* Check the state of WiFi on the device under test.
|
|
651
621
|
*
|
|
652
|
-
* @
|
|
653
|
-
* @return {Promise<boolean>} True if WiFi is enabled.
|
|
622
|
+
* @returns True if WiFi is enabled.
|
|
654
623
|
*/
|
|
655
|
-
export async function isWifiOn () {
|
|
624
|
+
export async function isWifiOn (this: ADB): Promise<boolean> {
|
|
656
625
|
const stdout = await this.getSetting('global', 'wifi_on');
|
|
657
626
|
return (parseInt(stdout, 10) !== 0);
|
|
658
627
|
// Alternative for Android 11+:
|
|
@@ -662,10 +631,9 @@ export async function isWifiOn () {
|
|
|
662
631
|
/**
|
|
663
632
|
* Check the state of Data transfer on the device under test.
|
|
664
633
|
*
|
|
665
|
-
* @
|
|
666
|
-
* @return {Promise<boolean>} True if Data transfer is enabled.
|
|
634
|
+
* @returns True if Data transfer is enabled.
|
|
667
635
|
*/
|
|
668
|
-
export async function isDataOn () {
|
|
636
|
+
export async function isDataOn (this: ADB): Promise<boolean> {
|
|
669
637
|
const stdout = await this.getSetting('global', 'mobile_data');
|
|
670
638
|
return (parseInt(stdout, 10) !== 0);
|
|
671
639
|
}
|
|
@@ -676,11 +644,10 @@ export async function isDataOn () {
|
|
|
676
644
|
* - transition_animation_scale
|
|
677
645
|
* - window_animation_scale
|
|
678
646
|
*
|
|
679
|
-
* @
|
|
680
|
-
* @return {Promise<boolean>} True if at least one of animation scale settings
|
|
647
|
+
* @returns True if at least one of animation scale settings
|
|
681
648
|
* is not equal to '0.0'.
|
|
682
649
|
*/
|
|
683
|
-
export async function isAnimationOn () {
|
|
650
|
+
export async function isAnimationOn (this: ADB): Promise<boolean> {
|
|
684
651
|
return (await B.all(ANIMATION_SCALE_KEYS.map(
|
|
685
652
|
async (k) => (await this.getSetting('global', k)) !== '0.0'))
|
|
686
653
|
).includes(true);
|
|
@@ -695,26 +662,23 @@ export async function isAnimationOn () {
|
|
|
695
662
|
* API level 28+ real devices checked this worked, but we haven't checked older ones
|
|
696
663
|
* with real devices.
|
|
697
664
|
*
|
|
698
|
-
* @
|
|
699
|
-
* @param {number} value Animation scale value (int or float) to set.
|
|
665
|
+
* @param value Animation scale value (int or float) to set.
|
|
700
666
|
* The minimum value of zero disables animations.
|
|
701
667
|
* By increasing the value, animations become slower.
|
|
702
668
|
* '1' is the system default animation scale.
|
|
703
|
-
* @
|
|
704
|
-
* @throws {Error} If the adb setting command raises an exception.
|
|
669
|
+
* @throws If the adb setting command raises an exception.
|
|
705
670
|
*/
|
|
706
|
-
export async function setAnimationScale (value) {
|
|
671
|
+
export async function setAnimationScale (this: ADB, value: number): Promise<void> {
|
|
707
672
|
await B.all(ANIMATION_SCALE_KEYS.map((k) => this.setSetting('global', k, value)));
|
|
708
673
|
}
|
|
709
674
|
|
|
710
675
|
/**
|
|
711
676
|
* Retrieve current screen orientation of the device under test.
|
|
712
677
|
*
|
|
713
|
-
* @
|
|
714
|
-
* @return {Promise<number?>} The current orientation encoded as an integer number.
|
|
678
|
+
* @returns The current orientation encoded as an integer number.
|
|
715
679
|
*/
|
|
716
|
-
export async function getScreenOrientation () {
|
|
717
|
-
|
|
680
|
+
export async function getScreenOrientation (this: ADB): Promise<number | null> {
|
|
681
|
+
const stdout = await this.shell(['dumpsys', 'input']);
|
|
718
682
|
return getSurfaceOrientation(stdout);
|
|
719
683
|
}
|
|
720
684
|
|
|
@@ -723,12 +687,12 @@ export async function getScreenOrientation () {
|
|
|
723
687
|
/**
|
|
724
688
|
* Reads SurfaceOrientation in dumpsys output
|
|
725
689
|
*
|
|
726
|
-
* @param
|
|
727
|
-
* @returns {number | null}
|
|
690
|
+
* @param dumpsys
|
|
728
691
|
*/
|
|
729
|
-
function getSurfaceOrientation (dumpsys) {
|
|
692
|
+
function getSurfaceOrientation (dumpsys: string): number | null {
|
|
730
693
|
const m = /SurfaceOrientation: \d/gi.exec(dumpsys);
|
|
731
|
-
return m
|
|
694
|
+
return m ? parseInt(m[0].split(':')[1], 10) : null;
|
|
732
695
|
}
|
|
733
696
|
|
|
734
697
|
// #endregion
|
|
698
|
+
|