appium-android-driver 12.4.7 → 12.4.9

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.
Files changed (39) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/build/lib/commands/file-actions.d.ts +37 -19
  3. package/build/lib/commands/file-actions.d.ts.map +1 -1
  4. package/build/lib/commands/file-actions.js +44 -58
  5. package/build/lib/commands/file-actions.js.map +1 -1
  6. package/build/lib/commands/geolocation.d.ts +44 -36
  7. package/build/lib/commands/geolocation.d.ts.map +1 -1
  8. package/build/lib/commands/geolocation.js +38 -32
  9. package/build/lib/commands/geolocation.js.map +1 -1
  10. package/build/lib/commands/intent.d.ts +103 -107
  11. package/build/lib/commands/intent.d.ts.map +1 -1
  12. package/build/lib/commands/intent.js +103 -97
  13. package/build/lib/commands/intent.js.map +1 -1
  14. package/build/lib/commands/log.d.ts +44 -48
  15. package/build/lib/commands/log.d.ts.map +1 -1
  16. package/build/lib/commands/log.js +30 -54
  17. package/build/lib/commands/log.js.map +1 -1
  18. package/build/lib/commands/network.d.ts +59 -39
  19. package/build/lib/commands/network.d.ts.map +1 -1
  20. package/build/lib/commands/network.js +65 -45
  21. package/build/lib/commands/network.js.map +1 -1
  22. package/build/lib/commands/recordscreen.d.ts +25 -40
  23. package/build/lib/commands/recordscreen.d.ts.map +1 -1
  24. package/build/lib/commands/recordscreen.js +46 -63
  25. package/build/lib/commands/recordscreen.js.map +1 -1
  26. package/build/lib/commands/types.d.ts.map +1 -1
  27. package/build/lib/driver.d.ts +11 -10
  28. package/build/lib/driver.d.ts.map +1 -1
  29. package/build/lib/driver.js.map +1 -1
  30. package/lib/commands/{file-actions.js → file-actions.ts} +88 -74
  31. package/lib/commands/{geolocation.js → geolocation.ts} +85 -54
  32. package/lib/commands/intent.ts +422 -0
  33. package/lib/commands/{log.js → log.ts} +68 -73
  34. package/lib/commands/{network.js → network.ts} +106 -59
  35. package/lib/commands/{recordscreen.js → recordscreen.ts} +77 -73
  36. package/lib/commands/types.ts +17 -0
  37. package/lib/driver.ts +2 -1
  38. package/package.json +1 -1
  39. package/lib/commands/intent.js +0 -409
@@ -1,7 +1,9 @@
1
1
  import _ from 'lodash';
2
2
  import {errors} from 'appium/driver';
3
- import {util} from 'appium/support';
3
+ import {util} from '@appium/support';
4
4
  import B from 'bluebird';
5
+ import type {AndroidDriver} from '../driver';
6
+ import type {ServiceType, GetConnectivityResult} from './types';
5
7
 
6
8
  const AIRPLANE_MODE_MASK = 0b001;
7
9
  const WIFI_MASK = 0b010;
@@ -9,26 +11,33 @@ const DATA_MASK = 0b100;
9
11
  const WIFI_KEY_NAME = 'wifi';
10
12
  const DATA_KEY_NAME = 'data';
11
13
  const AIRPLANE_MODE_KEY_NAME = 'airplaneMode';
12
- const SUPPORTED_SERVICE_NAMES = /** @type {import('./types').ServiceType[]} */ ([
14
+ const SUPPORTED_SERVICE_NAMES: ServiceType[] = [
13
15
  WIFI_KEY_NAME,
14
16
  DATA_KEY_NAME,
15
17
  AIRPLANE_MODE_KEY_NAME,
16
- ]);
18
+ ];
17
19
 
18
20
  /**
19
- * @this {import('../driver').AndroidDriver}
20
- * @returns {Promise<number>}
21
+ * Gets the current network connection state.
22
+ *
23
+ * @returns Promise that resolves to a number representing the network connection state.
24
+ * The value is a bitmask where:
25
+ * - Bit 0 (0b001) = Airplane mode
26
+ * - Bit 1 (0b010) = Wi-Fi
27
+ * - Bit 2 (0b100) = Data connection
21
28
  */
22
- export async function getNetworkConnection() {
29
+ export async function getNetworkConnection(
30
+ this: AndroidDriver,
31
+ ): Promise<number> {
23
32
  this.log.info('Getting network connection');
24
- let airplaneModeOn = await this.adb.isAirplaneModeOn();
33
+ const airplaneModeOn = await this.adb.isAirplaneModeOn();
25
34
  let connection = airplaneModeOn ? AIRPLANE_MODE_MASK : 0;
26
35
 
27
36
  // no need to check anything else if we are in airplane mode
28
37
  if (!airplaneModeOn) {
29
- let wifiOn = await this.isWifiOn();
38
+ const wifiOn = await this.isWifiOn();
30
39
  connection |= wifiOn ? WIFI_MASK : 0;
31
- let dataOn = await this.adb.isDataOn();
40
+ const dataOn = await this.adb.isDataOn();
32
41
  connection |= dataOn ? DATA_MASK : 0;
33
42
  }
34
43
 
@@ -36,40 +45,53 @@ export async function getNetworkConnection() {
36
45
  }
37
46
 
38
47
  /**
39
- * @this {import('../driver').AndroidDriver}
40
- * @returns {Promise<boolean>}
48
+ * Checks if Wi-Fi is enabled.
49
+ *
50
+ * @returns Promise that resolves to `true` if Wi-Fi is enabled, `false` otherwise.
41
51
  */
42
- export async function isWifiOn() {
52
+ export async function isWifiOn(
53
+ this: AndroidDriver,
54
+ ): Promise<boolean> {
43
55
  return await this.adb.isWifiOn();
44
56
  }
45
57
 
46
58
  /**
59
+ * Sets the connectivity state for Wi-Fi, data, and/or airplane mode.
60
+ *
47
61
  * @since Android 12 (only real devices, emulators work in all APIs)
48
- * @this {import('../driver').AndroidDriver}
49
- * @param {boolean} [wifi] Either to enable or disable Wi-Fi.
62
+ * @param wifi Either to enable or disable Wi-Fi.
50
63
  * An unset value means to not change the state for the given service.
51
- * @param {boolean} [data] Either to enable or disable mobile data connection.
64
+ * @param data Either to enable or disable mobile data connection.
52
65
  * An unset value means to not change the state for the given service.
53
- * @param {boolean} [airplaneMode] Either to enable to disable the Airplane Mode
66
+ * @param airplaneMode Either to enable to disable the Airplane Mode.
54
67
  * An unset value means to not change the state for the given service.
55
- * @returns {Promise<void>}
68
+ * @returns Promise that resolves when the connectivity state is set.
69
+ * @throws {errors.InvalidArgumentError} If none of the options are provided.
56
70
  */
57
- export async function mobileSetConnectivity(wifi, data, airplaneMode) {
71
+ export async function mobileSetConnectivity(
72
+ this: AndroidDriver,
73
+ wifi?: boolean,
74
+ data?: boolean,
75
+ airplaneMode?: boolean,
76
+ ): Promise<void> {
58
77
  if (_.every([wifi, data, airplaneMode], _.isUndefined)) {
59
78
  throw new errors.InvalidArgumentError(
60
79
  `Either one of ${JSON.stringify(SUPPORTED_SERVICE_NAMES)} options must be provided`,
61
80
  );
62
81
  }
63
82
 
64
- const currentState = await this.mobileGetConnectivity(
65
- /** @type {import('./types').ServiceType[]} */ ([
66
- ...(_.isUndefined(wifi) ? [] : [WIFI_KEY_NAME]),
67
- ...(_.isUndefined(data) ? [] : [DATA_KEY_NAME]),
68
- ...(_.isUndefined(airplaneMode) ? [] : [AIRPLANE_MODE_KEY_NAME]),
69
- ]),
70
- );
71
- /** @type {(Promise<any>|(() => Promise<any>))[]} */
72
- const setters = [];
83
+ const services: ServiceType[] = [
84
+ [wifi, WIFI_KEY_NAME],
85
+ [data, DATA_KEY_NAME],
86
+ [airplaneMode, AIRPLANE_MODE_KEY_NAME],
87
+ ].reduce<ServiceType[]>((acc, [value, key]: [boolean | undefined, ServiceType]) => {
88
+ if (!_.isUndefined(value)) {
89
+ acc.push(key);
90
+ }
91
+ return acc;
92
+ }, []);
93
+ const currentState = await this.mobileGetConnectivity(services);
94
+ const setters: Array<Promise<any> | (() => Promise<any>)> = [];
73
95
  if (!_.isUndefined(wifi) && currentState.wifi !== Boolean(wifi)) {
74
96
  setters.push(this.setWifiState(wifi));
75
97
  }
@@ -90,12 +112,16 @@ export async function mobileSetConnectivity(wifi, data, airplaneMode) {
90
112
  }
91
113
 
92
114
  /**
93
- * @this {import('../driver').AndroidDriver}
94
- * @param {import('./types').ServiceType[] | import('./types').ServiceType} [services] one or more
95
- * services to get the connectivity for.
96
- * @returns {Promise<import('./types').GetConnectivityResult>}
115
+ * Gets the connectivity state for one or more services.
116
+ *
117
+ * @param services One or more services to get the connectivity for.
118
+ * @returns Promise that resolves to an object containing the connectivity state for the requested services.
119
+ * @throws {errors.InvalidArgumentError} If any of the provided service names are not supported.
97
120
  */
98
- export async function mobileGetConnectivity(services = SUPPORTED_SERVICE_NAMES) {
121
+ export async function mobileGetConnectivity(
122
+ this: AndroidDriver,
123
+ services: ServiceType[] | ServiceType = SUPPORTED_SERVICE_NAMES,
124
+ ): Promise<GetConnectivityResult> {
99
125
  const svcs = _.castArray(services);
100
126
  const unsupportedServices = _.difference(svcs, SUPPORTED_SERVICE_NAMES);
101
127
  if (!_.isEmpty(unsupportedServices)) {
@@ -123,17 +149,25 @@ export async function mobileGetConnectivity(services = SUPPORTED_SERVICE_NAMES)
123
149
  return _.reduce(
124
150
  statePromises,
125
151
  (state, v, k) => _.isUndefined(v.value()) ? state : {...state, [k]: Boolean(v.value())},
126
- {}
152
+ {} as GetConnectivityResult,
127
153
  );
128
154
  }
129
155
 
130
156
  /**
157
+ * Sets the network connection state using a bitmask.
158
+ *
131
159
  * @since Android 12 (only real devices, emulators work in all APIs)
132
- * @this {import('../driver').AndroidDriver}
133
- * @param {number} type
134
- * @returns {Promise<number>}
160
+ * @param type A number representing the desired network connection state.
161
+ * The value is a bitmask where:
162
+ * - Bit 0 (0b001) = Airplane mode
163
+ * - Bit 1 (0b010) = Wi-Fi
164
+ * - Bit 2 (0b100) = Data connection
165
+ * @returns Promise that resolves to the current network connection state after the change.
135
166
  */
136
- export async function setNetworkConnection(type) {
167
+ export async function setNetworkConnection(
168
+ this: AndroidDriver,
169
+ type: number,
170
+ ): Promise<number> {
137
171
  this.log.info('Setting network connection');
138
172
  // decode the input
139
173
  const shouldEnableAirplaneMode = (type & AIRPLANE_MODE_MASK) !== 0;
@@ -192,61 +226,74 @@ export async function setNetworkConnection(type) {
192
226
  }
193
227
 
194
228
  /**
229
+ * Sets the Wi-Fi state.
230
+ *
195
231
  * @since Android 12 (only real devices, emulators work in all APIs)
196
- * @this {import('../driver').AndroidDriver}
197
- * @param {boolean} isOn
198
- * @returns {Promise<void>}
232
+ * @param isOn `true` to enable Wi-Fi, `false` to disable it.
233
+ * @returns Promise that resolves when the Wi-Fi state is set.
199
234
  */
200
- export async function setWifiState(isOn) {
235
+ export async function setWifiState(
236
+ this: AndroidDriver,
237
+ isOn: boolean,
238
+ ): Promise<void> {
201
239
  await this.settingsApp.setWifiState(isOn, this.isEmulator());
202
240
  }
203
241
 
204
242
  /**
243
+ * Sets the mobile data connection state.
244
+ *
205
245
  * @since Android 12 (only real devices, emulators work in all APIs)
206
- * @this {import('../driver').AndroidDriver}
207
- * @param {boolean} isOn
208
- * @returns {Promise<void>}
246
+ * @param isOn `true` to enable mobile data, `false` to disable it.
247
+ * @returns Promise that resolves when the data connection state is set.
209
248
  */
210
- export async function setDataState(isOn) {
249
+ export async function setDataState(
250
+ this: AndroidDriver,
251
+ isOn: boolean,
252
+ ): Promise<void> {
211
253
  await this.settingsApp.setDataState(isOn, this.isEmulator());
212
254
  }
213
255
 
214
256
  /**
257
+ * Toggles the mobile data connection state.
258
+ *
215
259
  * @since Android 12 (only real devices, emulators work in all APIs)
216
- * @this {import('../driver').AndroidDriver}
217
- * @returns {Promise<void>}
260
+ * @returns Promise that resolves when the data connection state is toggled.
218
261
  */
219
- export async function toggleData() {
262
+ export async function toggleData(
263
+ this: AndroidDriver,
264
+ ): Promise<void> {
220
265
  const isOn = await this.adb.isDataOn();
221
266
  this.log.info(`Turning network data ${!isOn ? 'on' : 'off'}`);
222
267
  await this.setDataState(!isOn);
223
268
  }
224
269
 
225
270
  /**
271
+ * Toggles the Wi-Fi state.
272
+ *
226
273
  * @since Android 12 (only real devices, emulators work in all APIs)
227
- * @this {import('../driver').AndroidDriver}
228
- * @returns {Promise<void>}
274
+ * @returns Promise that resolves when the Wi-Fi state is toggled.
229
275
  */
230
- export async function toggleWiFi() {
276
+ export async function toggleWiFi(
277
+ this: AndroidDriver,
278
+ ): Promise<void> {
231
279
  const isOn = await this.adb.isWifiOn();
232
280
  this.log.info(`Turning WiFi ${!isOn ? 'on' : 'off'}`);
233
281
  await this.setWifiState(!isOn);
234
282
  }
235
283
 
236
284
  /**
285
+ * Toggles the airplane mode state.
286
+ *
237
287
  * @since Android 12 (only real devices, emulators work in all APIs)
238
- * @this {import('../driver').AndroidDriver}
239
- * @returns {Promise<void>}
288
+ * @returns Promise that resolves when the airplane mode state is toggled.
240
289
  */
241
- export async function toggleFlightMode() {
242
- let flightMode = !(await this.adb.isAirplaneModeOn());
290
+ export async function toggleFlightMode(
291
+ this: AndroidDriver,
292
+ ): Promise<void> {
293
+ const flightMode = !(await this.adb.isAirplaneModeOn());
243
294
  this.log.info(`Turning flight mode ${flightMode ? 'on' : 'off'}`);
244
295
  await this.adb.setAirplaneMode(flightMode);
245
296
  if ((await this.adb.getApiLevel()) < 30) {
246
297
  await this.adb.broadcastAirplaneMode(flightMode);
247
298
  }
248
299
  }
249
-
250
- /**
251
- * @typedef {import('appium-adb').ADB} ADB
252
- */
@@ -1,8 +1,12 @@
1
1
  import {fs, net, system, tempDir, timing, util} from '@appium/support';
2
+ import type {NetOptions, HttpUploadOptions} from '@appium/support';
2
3
  import {waitForCondition} from 'asyncbox';
3
4
  import _ from 'lodash';
4
5
  import path from 'path';
5
6
  import {exec} from 'teen_process';
7
+ import type {AndroidDriver} from '../driver';
8
+ import type {ADB} from 'appium-adb';
9
+ import type {StartScreenRecordingOpts, StopScreenRecordingOpts, ScreenRecordingProperties} from './types';
6
10
 
7
11
  const RETRY_PAUSE = 300;
8
12
  const RETRY_TIMEOUT = 5000;
@@ -17,12 +21,22 @@ const FFMPEG_BINARY = `ffmpeg${system.isWindows() ? '.exe' : ''}`;
17
21
  const ADB_PULL_TIMEOUT = 5 * 60 * 1000;
18
22
 
19
23
  /**
24
+ * Starts screen recording on the Android device.
20
25
  *
21
- * @this {import('../driver').AndroidDriver}
22
- * @param {import('./types').StartScreenRecordingOpts} [options={}]
23
- * @returns {Promise<string>}
26
+ * This method uses Android's `screenrecord` command to capture the screen.
27
+ * The recording can be configured with various options such as video size,
28
+ * bit rate, time limit, and more.
29
+ *
30
+ * @param options Recording options. See {@link StartScreenRecordingOpts} for details.
31
+ * @returns Promise that resolves to the result of stopping any previous recording,
32
+ * or an empty string if no previous recording was active.
33
+ * @throws {Error} If screen recording is not supported on the device or emulator,
34
+ * or if the time limit is invalid.
24
35
  */
25
- export async function startRecordingScreen(options = {}) {
36
+ export async function startRecordingScreen(
37
+ this: AndroidDriver,
38
+ options: StartScreenRecordingOpts = {},
39
+ ): Promise<string> {
26
40
  await verifyScreenRecordIsSupported(this.adb, this.isEmulator());
27
41
 
28
42
  let result = '';
@@ -47,7 +61,8 @@ export async function startRecordingScreen(options = {}) {
47
61
 
48
62
  if (!_.isEmpty(this._screenRecordingProperties)) {
49
63
  // XXX: this doesn't need to be done in serial, does it?
50
- for (const record of this._screenRecordingProperties.records || []) {
64
+ const props = this._screenRecordingProperties;
65
+ for (const record of props.records || []) {
51
66
  await this.adb.rimraf(record);
52
67
  }
53
68
  this._screenRecordingProperties = undefined;
@@ -61,7 +76,7 @@ export async function startRecordingScreen(options = {}) {
61
76
  );
62
77
  }
63
78
 
64
- this._screenRecordingProperties = {
79
+ const recordingProps: ScreenRecordingProperties = {
65
80
  timer: new timing.Timer().start(),
66
81
  videoSize,
67
82
  timeLimit,
@@ -72,42 +87,55 @@ export async function startRecordingScreen(options = {}) {
72
87
  recordingProcess: null,
73
88
  stopped: false,
74
89
  };
75
- await scheduleScreenRecord.bind(this)(this._screenRecordingProperties);
90
+ this._screenRecordingProperties = recordingProps;
91
+ await scheduleScreenRecord.bind(this)(recordingProps);
76
92
  return result;
77
93
  }
78
94
 
79
95
  /**
96
+ * Stops screen recording and returns the recorded video.
97
+ *
98
+ * This method stops any active screen recording session and returns the recorded
99
+ * video as a base64-encoded string or uploads it to a remote location if specified.
100
+ * If multiple recording chunks were created (for long recordings), they will be
101
+ * merged using ffmpeg if available.
80
102
  *
81
- * @this {import('../driver').AndroidDriver}
82
- * @param {import('./types').StopScreenRecordingOpts} [options={}]
83
- * @returns {Promise<string>}
103
+ * @param options Stop recording options. See {@link StopScreenRecordingOpts} for details.
104
+ * @returns Promise that resolves to the recorded video as a base64-encoded string
105
+ * if `remotePath` is not provided, or an empty string if the video was uploaded to a remote location.
106
+ * @throws {Error} If screen recording is not supported, no recording was active,
107
+ * or if the recording process cannot be stopped.
84
108
  */
85
- export async function stopRecordingScreen(options = {}) {
109
+ export async function stopRecordingScreen(
110
+ this: AndroidDriver,
111
+ options: StopScreenRecordingOpts = {},
112
+ ): Promise<string> {
86
113
  await verifyScreenRecordIsSupported(this.adb, this.isEmulator());
87
114
 
88
- if (!_.isEmpty(this._screenRecordingProperties)) {
89
- this._screenRecordingProperties.stopped = true;
115
+ const props = this._screenRecordingProperties;
116
+ if (!_.isEmpty(props)) {
117
+ props.stopped = true;
90
118
  }
91
119
 
92
120
  try {
93
121
  await terminateBackgroundScreenRecording(this.adb, false);
94
122
  } catch (err) {
95
- this.log.warn(/** @type {Error} */ (err).message);
96
- if (!_.isEmpty(this._screenRecordingProperties)) {
123
+ this.log.warn((err as Error).message);
124
+ if (!_.isEmpty(props)) {
97
125
  this.log.warn('The resulting video might be corrupted');
98
126
  }
99
127
  }
100
128
 
101
- if (_.isEmpty(this._screenRecordingProperties)) {
129
+ if (_.isEmpty(props)) {
102
130
  this.log.info(
103
131
  `Screen recording has not been previously started by Appium. There is nothing to stop`,
104
132
  );
105
133
  return '';
106
134
  }
107
135
 
108
- if (this._screenRecordingProperties.recordingProcess?.isRunning) {
136
+ if (props.recordingProcess?.isRunning) {
109
137
  try {
110
- await this._screenRecordingProperties.recordingProcess.stop(
138
+ await props.recordingProcess.stop(
111
139
  'SIGINT',
112
140
  PROCESS_SHUTDOWN_TIMEOUT,
113
141
  );
@@ -116,10 +144,10 @@ export async function stopRecordingScreen(options = {}) {
116
144
  `Unable to stop screen recording within ${PROCESS_SHUTDOWN_TIMEOUT}ms`,
117
145
  );
118
146
  }
119
- this._screenRecordingProperties.recordingProcess = null;
147
+ props.recordingProcess = null;
120
148
  }
121
149
 
122
- if (_.isEmpty(this._screenRecordingProperties.records)) {
150
+ if (_.isEmpty(props.records)) {
123
151
  throw this.log.errorWithException(
124
152
  `No screen recordings have been stored on the device so far. ` +
125
153
  `Are you sure the ${SCREENRECORD_BINARY} utility works as expected?`,
@@ -128,14 +156,14 @@ export async function stopRecordingScreen(options = {}) {
128
156
 
129
157
  const tmpRoot = await tempDir.openDir();
130
158
  try {
131
- const localRecords = [];
132
- for (const pathOnDevice of this._screenRecordingProperties.records) {
159
+ const localRecords: string[] = [];
160
+ for (const pathOnDevice of props.records) {
133
161
  const relativePath = path.resolve(tmpRoot, path.posix.basename(pathOnDevice));
134
162
  localRecords.push(relativePath);
135
163
  await this.adb.pull(pathOnDevice, relativePath, { timeout: ADB_PULL_TIMEOUT });
136
164
  await this.adb.rimraf(pathOnDevice);
137
165
  }
138
- let resultFilePath = /** @type {string} */ (_.last(localRecords));
166
+ let resultFilePath = _.last(localRecords) as string;
139
167
  if (localRecords.length > 1) {
140
168
  this.log.info(`Got ${localRecords.length} screen recordings. Trying to merge them`);
141
169
  try {
@@ -143,7 +171,7 @@ export async function stopRecordingScreen(options = {}) {
143
171
  } catch (e) {
144
172
  this.log.warn(
145
173
  `Cannot merge the recorded files. The most recent screen recording is going to be returned as the result. ` +
146
- `Original error: ${/** @type {Error} */ (e).message}`,
174
+ `Original error: ${(e as Error).message}`,
147
175
  );
148
176
  }
149
177
  }
@@ -162,23 +190,17 @@ export async function stopRecordingScreen(options = {}) {
162
190
 
163
191
  // #region Internal helpers
164
192
 
165
- /**
166
- *
167
- * @param {string} localFile
168
- * @param {string} [remotePath]
169
- * @param {import('./types').StopScreenRecordingOpts} uploadOptions
170
- * @returns {Promise<string>}
171
- */
172
- async function uploadRecordedMedia(localFile, remotePath, uploadOptions = {}) {
193
+ async function uploadRecordedMedia(
194
+ localFile: string,
195
+ remotePath?: string,
196
+ uploadOptions: StopScreenRecordingOpts = {},
197
+ ): Promise<string> {
173
198
  if (_.isEmpty(remotePath)) {
174
199
  return (await util.toInMemoryBase64(localFile)).toString();
175
200
  }
176
201
 
177
202
  const {user, pass, method, headers, fileFieldName, formFields} = uploadOptions;
178
- /**
179
- * @type {import('@appium/support').NetOptions & import('@appium/support').HttpUploadOptions}
180
- */
181
- const options = {
203
+ const options: NetOptions & HttpUploadOptions = {
182
204
  method: method || 'PUT',
183
205
  headers,
184
206
  fileFieldName,
@@ -187,16 +209,11 @@ async function uploadRecordedMedia(localFile, remotePath, uploadOptions = {}) {
187
209
  if (user && pass) {
188
210
  options.auth = {user, pass};
189
211
  }
190
- await net.uploadFile(localFile, /** @type {string} */ (remotePath), options);
212
+ await net.uploadFile(localFile, remotePath as string, options);
191
213
  return '';
192
214
  }
193
215
 
194
- /**
195
- *
196
- * @param {ADB} adb
197
- * @param {boolean} isEmulator
198
- */
199
- async function verifyScreenRecordIsSupported(adb, isEmulator) {
216
+ async function verifyScreenRecordIsSupported(adb: ADB, isEmulator: boolean): Promise<void> {
200
217
  const apiLevel = await adb.getApiLevel();
201
218
  if (isEmulator && apiLevel < MIN_EMULATOR_API_LEVEL) {
202
219
  throw new Error(
@@ -205,12 +222,10 @@ async function verifyScreenRecordIsSupported(adb, isEmulator) {
205
222
  }
206
223
  }
207
224
 
208
- /**
209
- * @this {import('../driver').AndroidDriver}
210
- * @param {import('@appium/types').StringRecord} recordingProperties
211
- * @returns {Promise<void>}
212
- */
213
- async function scheduleScreenRecord(recordingProperties) {
225
+ async function scheduleScreenRecord(
226
+ this: AndroidDriver,
227
+ recordingProperties: ScreenRecordingProperties,
228
+ ): Promise<void> {
214
229
  if (recordingProperties.stopped) {
215
230
  return;
216
231
  }
@@ -219,7 +234,7 @@ async function scheduleScreenRecord(recordingProperties) {
219
234
 
220
235
  let currentTimeLimit = MAX_RECORDING_TIME_SEC;
221
236
  if (util.hasValue(recordingProperties.currentTimeLimit)) {
222
- const currentTimeLimitInt = parseInt(recordingProperties.currentTimeLimit, 10);
237
+ const currentTimeLimitInt = parseInt(String(recordingProperties.currentTimeLimit), 10);
223
238
  if (!isNaN(currentTimeLimitInt) && currentTimeLimitInt < MAX_RECORDING_TIME_SEC) {
224
239
  currentTimeLimit = currentTimeLimitInt;
225
240
  }
@@ -238,13 +253,13 @@ async function scheduleScreenRecord(recordingProperties) {
238
253
  }
239
254
  const currentDuration = timer.getDuration().asSeconds.toFixed(0);
240
255
  this.log.debug(`The overall screen recording duration is ${currentDuration}s so far`);
241
- const timeLimitInt = parseInt(timeLimit, 10);
242
- if (isNaN(timeLimitInt) || currentDuration >= timeLimitInt) {
256
+ const timeLimitInt = parseInt(String(timeLimit), 10);
257
+ if (isNaN(timeLimitInt) || Number(currentDuration) >= timeLimitInt) {
243
258
  this.log.debug('There is no need to start the next recording chunk');
244
259
  return;
245
260
  }
246
261
 
247
- recordingProperties.currentTimeLimit = timeLimitInt - currentDuration;
262
+ recordingProperties.currentTimeLimit = timeLimitInt - Number(currentDuration);
248
263
  const chunkDuration =
249
264
  recordingProperties.currentTimeLimit < MAX_RECORDING_TIME_SEC
250
265
  ? recordingProperties.currentTimeLimit
@@ -257,7 +272,7 @@ async function scheduleScreenRecord(recordingProperties) {
257
272
  try {
258
273
  await scheduleScreenRecord.bind(this)(recordingProperties);
259
274
  } catch (e) {
260
- this.log.error(/** @type {Error} */ (e).stack);
275
+ this.log.error((e as Error).stack);
261
276
  recordingProperties.stopped = true;
262
277
  }
263
278
  })();
@@ -280,13 +295,10 @@ async function scheduleScreenRecord(recordingProperties) {
280
295
  recordingProperties.recordingProcess = recordingProc;
281
296
  }
282
297
 
283
- /**
284
- *
285
- * @this {import('../driver').AndroidDriver}
286
- * @param {string[]} mediaFiles
287
- * @returns {Promise<string>}
288
- */
289
- async function mergeScreenRecords(mediaFiles) {
298
+ async function mergeScreenRecords(
299
+ this: AndroidDriver,
300
+ mediaFiles: string[],
301
+ ): Promise<string> {
290
302
  try {
291
303
  await fs.which(FFMPEG_BINARY);
292
304
  } catch {
@@ -310,14 +322,9 @@ async function mergeScreenRecords(mediaFiles) {
310
322
  return result;
311
323
  }
312
324
 
313
- /**
314
- *
315
- * @param {ADB} adb
316
- * @param {boolean} force
317
- * @returns {Promise<boolean>}
318
- */
319
- async function terminateBackgroundScreenRecording(adb, force = true) {
320
- const isScreenrecordRunning = async () => _.includes(await adb.listProcessStatus(), SCREENRECORD_BINARY);
325
+ async function terminateBackgroundScreenRecording(adb: ADB, force = true): Promise<boolean> {
326
+ const isScreenrecordRunning = async (): Promise<boolean> =>
327
+ _.includes(await adb.listProcessStatus(), SCREENRECORD_BINARY);
321
328
  if (!await isScreenrecordRunning()) {
322
329
  return false;
323
330
  }
@@ -331,13 +338,10 @@ async function terminateBackgroundScreenRecording(adb, force = true) {
331
338
  return true;
332
339
  } catch (err) {
333
340
  throw new Error(
334
- `Unable to stop the background screen recording: ${/** @type {Error} */ (err).message}`,
341
+ `Unable to stop the background screen recording: ${(err as Error).message}`,
335
342
  );
336
343
  }
337
344
  }
338
345
 
339
346
  // #endregion
340
347
 
341
- /**
342
- * @typedef {import('appium-adb').ADB} ADB
343
- */
@@ -1,5 +1,7 @@
1
1
  import type {HTTPMethod, StringRecord} from '@appium/types';
2
2
  import type {AndroidDriverCaps} from '../driver';
3
+ import type {SubProcess} from 'teen_process';
4
+ import {timing} from '@appium/support';
3
5
 
4
6
  /**
5
7
  * @privateRemarks probably better defined in `appium-adb`
@@ -618,3 +620,18 @@ export interface InjectedImageProperties {
618
620
  position?: InjectedImagePosition;
619
621
  rotation?: InjectedImageRotation;
620
622
  }
623
+
624
+ /**
625
+ * @internal
626
+ */
627
+ export interface ScreenRecordingProperties {
628
+ timer: timing.Timer;
629
+ videoSize?: string;
630
+ timeLimit: string | number;
631
+ currentTimeLimit?: string | number;
632
+ bitRate?: string | number;
633
+ bugReport?: boolean;
634
+ records: string[];
635
+ recordingProcess: SubProcess | null;
636
+ stopped: boolean;
637
+ }
package/lib/driver.ts CHANGED
@@ -210,6 +210,7 @@ import {getSystemBars, mobilePerformStatusBarCommand} from './commands/system-ba
210
210
  import {getDeviceTime, mobileGetDeviceTime} from './commands/time';
211
211
  import { executeMethodMap } from './execute-method-map';
212
212
  import { LRUCache } from 'lru-cache';
213
+ import type {ScreenRecordingProperties} from './commands/types';
213
214
 
214
215
  export type AndroidDriverCaps = DriverCaps<AndroidDriverConstraints>;
215
216
  export type W3CAndroidDriverCaps = W3CDriverCaps<AndroidDriverConstraints>;
@@ -240,7 +241,7 @@ class AndroidDriver
240
241
  _wasWindowAnimationDisabled?: boolean;
241
242
  _cachedActivityArgs: StringRecord;
242
243
  _screenStreamingProps?: StringRecord;
243
- _screenRecordingProperties?: StringRecord;
244
+ _screenRecordingProperties?: ScreenRecordingProperties;
244
245
  _logcatWebsocketListener?: LogcatListener;
245
246
  _bidiServerLogListener?: (...args: any[]) => void;
246
247
  _bidiProxyUrl: string | null = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appium-android-driver",
3
- "version": "12.4.7",
3
+ "version": "12.4.9",
4
4
  "description": "Android UiAutomator and Chrome support for Appium",
5
5
  "keywords": [
6
6
  "appium",