appium-android-driver 5.14.7 → 6.0.1

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 (210) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/build/lib/commands/actions.d.ts +6 -224
  3. package/build/lib/commands/actions.d.ts.map +1 -1
  4. package/build/lib/commands/actions.js +306 -405
  5. package/build/lib/commands/actions.js.map +1 -1
  6. package/build/lib/commands/alert.d.ts +7 -9
  7. package/build/lib/commands/alert.d.ts.map +1 -1
  8. package/build/lib/commands/alert.js +24 -18
  9. package/build/lib/commands/alert.js.map +1 -1
  10. package/build/lib/commands/app-management.d.ts +7 -313
  11. package/build/lib/commands/app-management.d.ts.map +1 -1
  12. package/build/lib/commands/app-management.js +135 -293
  13. package/build/lib/commands/app-management.js.map +1 -1
  14. package/build/lib/commands/context.d.ts +8 -92
  15. package/build/lib/commands/context.d.ts.map +1 -1
  16. package/build/lib/commands/context.js +381 -439
  17. package/build/lib/commands/context.js.map +1 -1
  18. package/build/lib/commands/element.d.ts +8 -35
  19. package/build/lib/commands/element.d.ts.map +1 -1
  20. package/build/lib/commands/element.js +153 -136
  21. package/build/lib/commands/element.js.map +1 -1
  22. package/build/lib/commands/emu-console.d.ts +6 -48
  23. package/build/lib/commands/emu-console.d.ts.map +1 -1
  24. package/build/lib/commands/emu-console.js +19 -34
  25. package/build/lib/commands/emu-console.js.map +1 -1
  26. package/build/lib/commands/execute.d.ts +6 -5
  27. package/build/lib/commands/execute.d.ts.map +1 -1
  28. package/build/lib/commands/execute.js +77 -66
  29. package/build/lib/commands/execute.js.map +1 -1
  30. package/build/lib/commands/file-actions.d.ts +7 -128
  31. package/build/lib/commands/file-actions.d.ts.map +1 -1
  32. package/build/lib/commands/file-actions.js +183 -219
  33. package/build/lib/commands/file-actions.js.map +1 -1
  34. package/build/lib/commands/find.d.ts +8 -12
  35. package/build/lib/commands/find.d.ts.map +1 -1
  36. package/build/lib/commands/find.js +19 -23
  37. package/build/lib/commands/find.js.map +1 -1
  38. package/build/lib/commands/general.d.ts +9 -132
  39. package/build/lib/commands/general.d.ts.map +1 -1
  40. package/build/lib/commands/general.js +281 -312
  41. package/build/lib/commands/general.js.map +1 -1
  42. package/build/lib/commands/ime.d.ts +7 -10
  43. package/build/lib/commands/ime.d.ts.map +1 -1
  44. package/build/lib/commands/ime.js +47 -35
  45. package/build/lib/commands/ime.js.map +1 -1
  46. package/build/lib/commands/index.d.ts +27 -2
  47. package/build/lib/commands/index.d.ts.map +1 -1
  48. package/build/lib/commands/index.js +41 -19
  49. package/build/lib/commands/index.js.map +1 -1
  50. package/build/lib/commands/intent.d.ts +7 -417
  51. package/build/lib/commands/intent.d.ts.map +1 -1
  52. package/build/lib/commands/intent.js +104 -216
  53. package/build/lib/commands/intent.js.map +1 -1
  54. package/build/lib/commands/keyboard.d.ts +6 -5
  55. package/build/lib/commands/keyboard.d.ts.map +1 -1
  56. package/build/lib/commands/keyboard.js +16 -8
  57. package/build/lib/commands/keyboard.js.map +1 -1
  58. package/build/lib/commands/log.d.ts +7 -44
  59. package/build/lib/commands/log.d.ts.map +1 -1
  60. package/build/lib/commands/log.js +146 -108
  61. package/build/lib/commands/log.js.map +1 -1
  62. package/build/lib/commands/media-projection.d.ts +7 -143
  63. package/build/lib/commands/media-projection.d.ts.map +1 -1
  64. package/build/lib/commands/media-projection.js +113 -140
  65. package/build/lib/commands/media-projection.js.map +1 -1
  66. package/build/lib/commands/mixins.d.ts +740 -0
  67. package/build/lib/commands/mixins.d.ts.map +1 -0
  68. package/build/lib/commands/mixins.js +19 -0
  69. package/build/lib/commands/mixins.js.map +1 -0
  70. package/build/lib/commands/network.d.ts +7 -138
  71. package/build/lib/commands/network.d.ts.map +1 -1
  72. package/build/lib/commands/network.js +212 -254
  73. package/build/lib/commands/network.js.map +1 -1
  74. package/build/lib/commands/performance.d.ts +24 -70
  75. package/build/lib/commands/performance.d.ts.map +1 -1
  76. package/build/lib/commands/performance.js +144 -100
  77. package/build/lib/commands/performance.js.map +1 -1
  78. package/build/lib/commands/permissions.d.ts +8 -92
  79. package/build/lib/commands/permissions.d.ts.map +1 -1
  80. package/build/lib/commands/permissions.js +75 -87
  81. package/build/lib/commands/permissions.js.map +1 -1
  82. package/build/lib/commands/recordscreen.d.ts +7 -193
  83. package/build/lib/commands/recordscreen.d.ts.map +1 -1
  84. package/build/lib/commands/recordscreen.js +151 -182
  85. package/build/lib/commands/recordscreen.js.map +1 -1
  86. package/build/lib/commands/shell.d.ts +7 -7
  87. package/build/lib/commands/shell.d.ts.map +1 -1
  88. package/build/lib/commands/shell.js +40 -33
  89. package/build/lib/commands/shell.js.map +1 -1
  90. package/build/lib/commands/streamscreen.d.ts +9 -103
  91. package/build/lib/commands/streamscreen.d.ts.map +1 -1
  92. package/build/lib/commands/streamscreen.js +261 -218
  93. package/build/lib/commands/streamscreen.js.map +1 -1
  94. package/build/lib/commands/system-bars.d.ts +22 -90
  95. package/build/lib/commands/system-bars.d.ts.map +1 -1
  96. package/build/lib/commands/system-bars.js +76 -74
  97. package/build/lib/commands/system-bars.js.map +1 -1
  98. package/build/lib/commands/touch.d.ts +10 -29
  99. package/build/lib/commands/touch.d.ts.map +1 -1
  100. package/build/lib/commands/touch.js +301 -285
  101. package/build/lib/commands/touch.js.map +1 -1
  102. package/build/lib/commands/types.d.ts +978 -0
  103. package/build/lib/commands/types.d.ts.map +1 -0
  104. package/build/lib/commands/types.js +3 -0
  105. package/build/lib/commands/types.js.map +1 -0
  106. package/build/lib/constraints.d.ts +291 -0
  107. package/build/lib/constraints.d.ts.map +1 -0
  108. package/build/lib/{desired-caps.js → constraints.js} +103 -102
  109. package/build/lib/constraints.js.map +1 -0
  110. package/build/lib/driver.d.ts +68 -37
  111. package/build/lib/driver.d.ts.map +1 -1
  112. package/build/lib/driver.js +123 -80
  113. package/build/lib/driver.js.map +1 -1
  114. package/build/lib/helpers/android.d.ts +164 -0
  115. package/build/lib/helpers/android.d.ts.map +1 -0
  116. package/build/lib/helpers/android.js +819 -0
  117. package/build/lib/helpers/android.js.map +1 -0
  118. package/build/lib/helpers/index.d.ts +7 -0
  119. package/build/lib/helpers/index.d.ts.map +1 -0
  120. package/build/lib/helpers/index.js +29 -0
  121. package/build/lib/helpers/index.js.map +1 -0
  122. package/build/lib/helpers/types.d.ts +121 -0
  123. package/build/lib/helpers/types.d.ts.map +1 -0
  124. package/build/lib/helpers/types.js +3 -0
  125. package/build/lib/helpers/types.js.map +1 -0
  126. package/build/lib/helpers/unlock.d.ts +32 -0
  127. package/build/lib/helpers/unlock.d.ts.map +1 -0
  128. package/build/lib/helpers/unlock.js +273 -0
  129. package/build/lib/helpers/unlock.js.map +1 -0
  130. package/build/lib/helpers/webview.d.ts +74 -0
  131. package/build/lib/helpers/webview.d.ts.map +1 -0
  132. package/build/lib/helpers/webview.js +421 -0
  133. package/build/lib/helpers/webview.js.map +1 -0
  134. package/build/lib/index.d.ts +9 -0
  135. package/build/lib/index.d.ts.map +1 -0
  136. package/build/lib/index.js +37 -0
  137. package/build/lib/index.js.map +1 -0
  138. package/build/lib/method-map.d.ts +0 -8
  139. package/build/lib/method-map.d.ts.map +1 -1
  140. package/build/lib/method-map.js +63 -74
  141. package/build/lib/method-map.js.map +1 -1
  142. package/build/lib/stubs.d.ts +0 -1
  143. package/build/lib/stubs.d.ts.map +1 -1
  144. package/build/lib/stubs.js +1 -0
  145. package/build/lib/stubs.js.map +1 -1
  146. package/build/lib/utils.d.ts +1 -1
  147. package/build/lib/utils.d.ts.map +1 -1
  148. package/lib/commands/actions.js +351 -464
  149. package/lib/commands/alert.js +27 -17
  150. package/lib/commands/app-management.js +156 -314
  151. package/lib/commands/context.js +457 -441
  152. package/lib/commands/element.js +201 -157
  153. package/lib/commands/emu-console.js +25 -45
  154. package/lib/commands/execute.js +106 -90
  155. package/lib/commands/file-actions.js +222 -240
  156. package/lib/commands/find.ts +103 -0
  157. package/lib/commands/general.js +327 -339
  158. package/lib/commands/ime.js +50 -34
  159. package/lib/commands/{index.js → index.ts} +20 -24
  160. package/lib/commands/intent.js +108 -249
  161. package/lib/commands/keyboard.js +20 -8
  162. package/lib/commands/log.js +172 -116
  163. package/lib/commands/media-projection.js +134 -161
  164. package/lib/commands/mixins.ts +966 -0
  165. package/lib/commands/network.js +252 -281
  166. package/lib/commands/performance.js +203 -132
  167. package/lib/commands/permissions.js +108 -109
  168. package/lib/commands/recordscreen.js +212 -209
  169. package/lib/commands/shell.js +51 -40
  170. package/lib/commands/streamscreen.js +355 -289
  171. package/lib/commands/system-bars.js +92 -83
  172. package/lib/commands/touch.js +357 -294
  173. package/lib/commands/types.ts +1097 -0
  174. package/lib/{desired-caps.js → constraints.ts} +106 -103
  175. package/lib/{driver.js → driver.ts} +278 -132
  176. package/lib/helpers/android.ts +1143 -0
  177. package/lib/helpers/index.ts +6 -0
  178. package/lib/helpers/types.ts +134 -0
  179. package/lib/helpers/unlock.ts +329 -0
  180. package/lib/helpers/webview.ts +582 -0
  181. package/lib/index.ts +18 -0
  182. package/lib/method-map.js +87 -98
  183. package/lib/stubs.ts +0 -1
  184. package/package.json +26 -19
  185. package/build/index.js +0 -51
  186. package/build/lib/android-helpers.d.ts +0 -136
  187. package/build/lib/android-helpers.d.ts.map +0 -1
  188. package/build/lib/android-helpers.js +0 -855
  189. package/build/lib/android-helpers.js.map +0 -1
  190. package/build/lib/commands/coverage.d.ts +0 -5
  191. package/build/lib/commands/coverage.d.ts.map +0 -1
  192. package/build/lib/commands/coverage.js +0 -19
  193. package/build/lib/commands/coverage.js.map +0 -1
  194. package/build/lib/desired-caps.d.ts +0 -353
  195. package/build/lib/desired-caps.d.ts.map +0 -1
  196. package/build/lib/desired-caps.js.map +0 -1
  197. package/build/lib/unlock-helpers.d.ts +0 -38
  198. package/build/lib/unlock-helpers.d.ts.map +0 -1
  199. package/build/lib/unlock-helpers.js +0 -266
  200. package/build/lib/unlock-helpers.js.map +0 -1
  201. package/build/lib/webview-helpers.d.ts +0 -224
  202. package/build/lib/webview-helpers.d.ts.map +0 -1
  203. package/build/lib/webview-helpers.js +0 -528
  204. package/build/lib/webview-helpers.js.map +0 -1
  205. package/index.js +0 -24
  206. package/lib/android-helpers.js +0 -983
  207. package/lib/commands/coverage.js +0 -18
  208. package/lib/commands/find.js +0 -82
  209. package/lib/unlock-helpers.js +0 -278
  210. package/lib/webview-helpers.js +0 -602
@@ -1,361 +1,349 @@
1
+ // @ts-check
2
+ import {util} from '@appium/support';
3
+ import {longSleep} from 'asyncbox';
1
4
  import _ from 'lodash';
2
- import androidHelpers from '../android-helpers';
3
- import { util } from '@appium/support';
4
5
  import moment from 'moment';
5
- import { longSleep } from 'asyncbox';
6
- import { errors } from 'appium/driver';
6
+ import androidHelpers from '../helpers/android';
7
+ import {requireArgs} from '../utils';
8
+ import {mixin} from './mixins';
7
9
 
8
10
  const MOMENT_FORMAT_ISO8601 = 'YYYY-MM-DDTHH:mm:ssZ';
9
11
 
10
- let commands = {}, helpers = {}, extensions = {};
11
-
12
- commands.keys = async function keys (keys) {
13
- // Protocol sends an array; rethink approach
14
- keys = _.isArray(keys) ? keys.join('') : keys;
15
- let params = {
16
- text: keys,
17
- replace: false
18
- };
19
- if (this.opts.unicodeKeyboard) {
20
- params.unicodeKeyboard = true;
21
- }
22
- await this.doSendKeys(params);
23
- };
24
-
25
- commands.doSendKeys = async function doSendKeys (params) {
26
- return await this.bootstrap.sendAction('setText', params);
27
- };
28
-
29
- /**
30
- * Retrieves the current device's timestamp.
31
- *
32
- * @param {string} format - The set of format specifiers. Read
33
- * https://momentjs.com/docs/ to get the full list of supported
34
- * datetime format specifiers. The default format is
35
- * `YYYY-MM-DDTHH:mm:ssZ`, which complies to ISO-8601
36
- * @return {string} Formatted datetime string or the raw command output if formatting fails
37
- */
38
- commands.getDeviceTime = async function getDeviceTime (format = MOMENT_FORMAT_ISO8601) {
39
- this.log.debug('Attempting to capture android device date and time. ' +
40
- `The format specifier is '${format}'`);
41
- const deviceTimestamp = (await this.adb.shell(['date', '+%Y-%m-%dT%T%z'])).trim();
42
- this.log.debug(`Got device timestamp: ${deviceTimestamp}`);
43
- const parsedTimestamp = moment.utc(deviceTimestamp, 'YYYY-MM-DDTHH:mm:ssZZ');
44
- if (!parsedTimestamp.isValid()) {
45
- this.log.warn('Cannot parse the returned timestamp. Returning as is');
46
- return deviceTimestamp;
47
- }
48
- return parsedTimestamp.utcOffset(parsedTimestamp._tzm || 0).format(format);
49
- };
50
-
51
- /**
52
- * @typedef {Object} DeviceTimeOptions
53
- * @property {string} format [YYYY-MM-DDTHH:mm:ssZ] - See getDeviceTime#format
54
- */
55
-
56
12
  /**
57
- * Retrieves the current device time
58
- *
59
- * @param {DeviceTimeOptions} opts
60
- * @return {string} Formatted datetime string or the raw command output if formatting fails
13
+ * @type {import('./mixins').GeneralMixin & ThisType<import('../driver').AndroidDriver>}
14
+ * @satisfies {import('@appium/types').ExternalDriver}
61
15
  */
62
- commands.mobileGetDeviceTime = async function mobileGetDeviceTime (opts = {}) {
63
- return await this.getDeviceTime(opts.format);
64
- };
65
-
66
- commands.getPageSource = async function getPageSource () {
67
- return await this.bootstrap.sendAction('source');
68
- };
69
-
70
- commands.back = async function back () {
71
- return await this.bootstrap.sendAction('pressBack');
72
- };
73
-
74
- commands.openSettingsActivity = async function openSettingsActivity (setting) {
75
- let {appPackage, appActivity} = await this.adb.getFocusedPackageAndActivity();
76
- await this.adb.shell(['am', 'start', '-a', `android.settings.${setting}`]);
77
- await this.adb.waitForNotActivity(appPackage, appActivity, 5000);
78
- };
79
-
80
- commands.getWindowSize = async function getWindowSize () {
81
- return await this.bootstrap.sendAction('getDeviceSize');
82
- };
83
-
84
- // For W3C
85
- commands.getWindowRect = async function getWindowRect () {
86
- const { width, height } = await this.getWindowSize();
87
- return {
88
- width,
89
- height,
90
- x: 0,
91
- y: 0
92
- };
93
- };
94
-
95
- commands.getCurrentActivity = async function getCurrentActivity () {
96
- return (await this.adb.getFocusedPackageAndActivity()).appActivity;
97
- };
98
-
99
- commands.getCurrentPackage = async function getCurrentPackage () {
100
- return (await this.adb.getFocusedPackageAndActivity()).appPackage;
101
- };
102
-
103
- commands.background = async function background (seconds) {
104
- if (seconds < 0) {
105
- // if user passes in a negative seconds value, interpret that as the instruction
106
- // to not bring the app back at all
107
- await this.adb.goToHome();
108
- return true;
109
- }
110
- let {appPackage, appActivity} = await this.adb.getFocusedPackageAndActivity();
111
- await this.adb.goToHome();
112
-
113
- // people can wait for a long time, so to be safe let's use the longSleep function and log
114
- // progress periodically.
115
- const sleepMs = seconds * 1000;
116
- const thresholdMs = 30 * 1000; // use the spin-wait for anything over this threshold
117
- // for our spin interval, use 1% of the total wait time, but nothing bigger than 30s
118
- const intervalMs = _.min([30 * 1000, parseInt(sleepMs / 100, 10)]);
119
- const progressCb = ({elapsedMs, progress}) => {
120
- const waitSecs = (elapsedMs / 1000).toFixed(0);
121
- const progressPct = (progress * 100).toFixed(2);
122
- this.log.debug(`Waited ${waitSecs}s so far (${progressPct}%)`);
123
- };
124
- await longSleep(sleepMs, {thresholdMs, intervalMs, progressCb});
125
-
126
- let args;
127
- if (this._cachedActivityArgs && this._cachedActivityArgs[`${appPackage}/${appActivity}`]) {
128
- // the activity was started with `startActivity`, so use those args to restart
129
- args = this._cachedActivityArgs[`${appPackage}/${appActivity}`];
130
- } else {
131
- try {
132
- this.log.debug(`Activating app '${appPackage}' in order to restore it`);
133
- await this.activateApp(appPackage);
16
+ const GeneralMixin = {
17
+ _cachedActivityArgs: {},
18
+ async keys(keys) {
19
+ // Protocol sends an array; rethink approach
20
+ keys = _.isArray(keys) ? keys.join('') : keys;
21
+ /**
22
+ * @type {import('./types').SendKeysOpts}
23
+ */
24
+ const params = {
25
+ text: keys,
26
+ replace: false,
27
+ };
28
+ if (this.opts.unicodeKeyboard) {
29
+ params.unicodeKeyboard = true;
30
+ }
31
+ await this.doSendKeys(params);
32
+ },
33
+
34
+ async doSendKeys(params) {
35
+ return await /** @type {AndroidBootstrap} */ (this.bootstrap).sendAction('setText', params);
36
+ },
37
+
38
+ async getDeviceTime(format = MOMENT_FORMAT_ISO8601) {
39
+ this.log.debug(
40
+ 'Attempting to capture android device date and time. ' + `The format specifier is '${format}'`
41
+ );
42
+ const deviceTimestamp = (
43
+ await /** @type {ADB} */ (this.adb).shell(['date', '+%Y-%m-%dT%T%z'])
44
+ ).trim();
45
+ this.log.debug(`Got device timestamp: ${deviceTimestamp}`);
46
+ const parsedTimestamp = moment.utc(deviceTimestamp, 'YYYY-MM-DDTHH:mm:ssZZ');
47
+ if (!parsedTimestamp.isValid()) {
48
+ this.log.warn('Cannot parse the returned timestamp. Returning as is');
49
+ return deviceTimestamp;
50
+ }
51
+ // @ts-expect-error private API
52
+ return parsedTimestamp.utcOffset(parsedTimestamp._tzm || 0).format(format);
53
+ },
54
+
55
+ async mobileGetDeviceTime(opts = {}) {
56
+ return await this.getDeviceTime(opts.format);
57
+ },
58
+
59
+ async getPageSource() {
60
+ return await /** @type {AndroidBootstrap} */ (this.bootstrap).sendAction('source');
61
+ },
62
+
63
+ async back() {
64
+ return await /** @type {AndroidBootstrap} */ (this.bootstrap).sendAction('pressBack');
65
+ },
66
+
67
+ async openSettingsActivity(setting) {
68
+ const adb = /** @type {ADB} */ (this.adb);
69
+ let {appPackage, appActivity} = await adb.getFocusedPackageAndActivity();
70
+ await adb.shell(['am', 'start', '-a', `android.settings.${setting}`]);
71
+ await adb.waitForNotActivity(/** @type {string} */ (appPackage), appActivity, 5000);
72
+ },
73
+
74
+ async getWindowSize() {
75
+ return await /** @type {AndroidBootstrap} */ (this.bootstrap).sendAction('getDeviceSize');
76
+ },
77
+
78
+ // For W3C
79
+ async getWindowRect() {
80
+ const {width, height} = await this.getWindowSize();
81
+ return {
82
+ width,
83
+ height,
84
+ x: 0,
85
+ y: 0,
86
+ };
87
+ },
88
+
89
+ async getCurrentActivity() {
90
+ return (await /** @type {ADB} */ (this.adb).getFocusedPackageAndActivity()).appActivity;
91
+ },
92
+
93
+ async getCurrentPackage() {
94
+ return (await /** @type {ADB} */ (this.adb).getFocusedPackageAndActivity()).appPackage;
95
+ },
96
+
97
+ async background(seconds) {
98
+ const adb = /** @type {ADB} */ (this.adb);
99
+ if (seconds < 0) {
100
+ // if user passes in a negative seconds value, interpret that as the instruction
101
+ // to not bring the app back at all
102
+ await adb.goToHome();
134
103
  return true;
135
- } catch (ign) {}
136
- args = ((appPackage === this.opts.appPackage && appActivity === this.opts.appActivity) ||
137
- (appPackage === this.opts.appWaitPackage && (this.opts.appWaitActivity || '').split(',').includes(appActivity)))
138
- ? {// the activity is the original session activity, so use the original args
139
- pkg: this.opts.appPackage,
140
- activity: this.opts.appActivity,
141
- action: this.opts.intentAction,
142
- category: this.opts.intentCategory,
143
- flags: this.opts.intentFlags,
144
- waitPkg: this.opts.appWaitPackage,
145
- waitActivity: this.opts.appWaitActivity,
146
- waitForLaunch: this.opts.appWaitForLaunch,
147
- waitDuration: this.opts.appWaitDuration,
148
- optionalIntentArguments: this.opts.optionalIntentArguments,
149
- stopApp: false,
150
- user: this.opts.userProfile}
151
- : {// the activity was started some other way, so use defaults
152
- pkg: appPackage,
153
- activity: appActivity,
154
- waitPkg: appPackage,
155
- waitActivity: appActivity,
156
- stopApp: false};
157
- }
158
- args = await util.filterObject(args);
159
- this.log.debug(`Bringing application back to foreground with arguments: ${JSON.stringify(args)}`);
160
- return await this.adb.startApp(args);
161
- };
162
-
163
- commands.getStrings = async function getStrings (language) {
164
- if (!language) {
165
- language = await this.adb.getDeviceLanguage();
166
- this.log.info(`No language specified, returning strings for: ${language}`);
167
- }
168
-
169
- // Clients require the resulting mapping to have both keys
170
- // and values of type string
171
- const preprocessStringsMap = (mapping) => {
172
- const result = {};
173
- for (const [key, value] of _.toPairs(mapping)) {
174
- result[key] = _.isString(value) ? value : JSON.stringify(value);
175
104
  }
176
- return result;
177
- };
178
-
179
- if (this.apkStrings[language]) {
180
- // Return cached strings
181
- return preprocessStringsMap(this.apkStrings[language]);
182
- }
183
-
184
- this.apkStrings[language] = await androidHelpers.pushStrings(language, this.adb, this.opts);
185
- if (this.bootstrap) {
186
- // TODO: This is mutating the current language, but it's how appium currently works
187
- await this.bootstrap.sendAction('updateStrings');
188
- }
105
+ let {appPackage, appActivity} = await adb.getFocusedPackageAndActivity();
106
+ await adb.goToHome();
107
+
108
+ // people can wait for a long time, so to be safe let's use the longSleep function and log
109
+ // progress periodically.
110
+ const sleepMs = seconds * 1000;
111
+ const thresholdMs = 30 * 1000; // use the spin-wait for anything over this threshold
112
+ // for our spin interval, use 1% of the total wait time, but nothing bigger than 30s
113
+ const intervalMs = _.min([30 * 1000, parseInt(String(sleepMs / 100), 10)]);
114
+ /**
115
+ *
116
+ * @param {{elapsedMs: number, progress: number}} param0
117
+ */
118
+ const progressCb = ({elapsedMs, progress}) => {
119
+ const waitSecs = (elapsedMs / 1000).toFixed(0);
120
+ const progressPct = (progress * 100).toFixed(2);
121
+ this.log.debug(`Waited ${waitSecs}s so far (${progressPct}%)`);
122
+ };
123
+ await longSleep(sleepMs, {thresholdMs, intervalMs, progressCb});
124
+
125
+ /** @type {import('appium-adb').StartAppOptions} */
126
+ let args;
127
+ if (this._cachedActivityArgs && this._cachedActivityArgs[`${appPackage}/${appActivity}`]) {
128
+ // the activity was started with `startActivity`, so use those args to restart
129
+ args = this._cachedActivityArgs[`${appPackage}/${appActivity}`];
130
+ } else {
131
+ try {
132
+ this.log.debug(`Activating app '${appPackage}' in order to restore it`);
133
+ await this.activateApp(/** @type {string} */ (appPackage));
134
+ return true;
135
+ } catch (ign) {}
136
+ args =
137
+ (appPackage === this.opts.appPackage && appActivity === this.opts.appActivity) ||
138
+ (appPackage === this.opts.appWaitPackage &&
139
+ (this.opts.appWaitActivity || '').split(',').includes(String(appActivity)))
140
+ ? {
141
+ // the activity is the original session activity, so use the original args
142
+ pkg: /** @type {string} */ (this.opts.appPackage),
143
+ activity: this.opts.appActivity,
144
+ action: this.opts.intentAction,
145
+ category: this.opts.intentCategory,
146
+ flags: this.opts.intentFlags,
147
+ waitPkg: this.opts.appWaitPackage,
148
+ waitActivity: this.opts.appWaitActivity,
149
+ waitForLaunch: this.opts.appWaitForLaunch,
150
+ waitDuration: this.opts.appWaitDuration,
151
+ optionalIntentArguments: this.opts.optionalIntentArguments,
152
+ stopApp: false,
153
+ user: this.opts.userProfile,
154
+ }
155
+ : {
156
+ // the activity was started some other way, so use defaults
157
+ pkg: /** @type {string} */ (appPackage),
158
+ activity: appActivity,
159
+ waitPkg: appPackage,
160
+ waitActivity: appActivity,
161
+ stopApp: false,
162
+ };
163
+ }
164
+ args = /** @type {import('appium-adb').StartAppOptions} */ (
165
+ _.pickBy(args, (value) => !_.isUndefined(value))
166
+ );
167
+ this.log.debug(
168
+ `Bringing application back to foreground with arguments: ${JSON.stringify(args)}`
169
+ );
170
+ return await adb.startApp(args);
171
+ },
172
+
173
+ async getStrings(language) {
174
+ const adb = /** @type {ADB} */ (this.adb);
175
+ if (!language) {
176
+ language = await adb.getDeviceLanguage();
177
+ this.log.info(`No language specified, returning strings for: ${language}`);
178
+ }
189
179
 
190
- return preprocessStringsMap(this.apkStrings[language]);
191
- };
180
+ // Clients require the resulting mapping to have both keys
181
+ // and values of type string
182
+ /** @param {StringRecord} mapping */
183
+ const preprocessStringsMap = (mapping) => {
184
+ /** @type {StringRecord} */
185
+ const result = {};
186
+ for (const [key, value] of _.toPairs(mapping)) {
187
+ result[key] = _.isString(value) ? value : JSON.stringify(value);
188
+ }
189
+ return result;
190
+ };
191
+
192
+ if (this.apkStrings[language]) {
193
+ // Return cached strings
194
+ return preprocessStringsMap(this.apkStrings[language]);
195
+ }
192
196
 
193
- commands.launchApp = async function launchApp () {
194
- await this.initAUT();
195
- await this.startAUT();
196
- };
197
+ this.apkStrings[language] = await androidHelpers.pushStrings(language, adb, this.opts);
198
+ if (this.bootstrap) {
199
+ // TODO: This is mutating the current language, but it's how appium currently works
200
+ await this.bootstrap.sendAction('updateStrings');
201
+ }
197
202
 
198
- commands.startActivity = async function startActivity (appPackage, appActivity,
199
- appWaitPackage, appWaitActivity, intentAction, intentCategory, intentFlags,
200
- optionalIntentArguments, dontStopAppOnReset) {
201
- this.log.debug(`Starting package '${appPackage}' and activity '${appActivity}'`);
202
-
203
- // dontStopAppOnReset is both an argument here, and a desired capability
204
- // if the argument is set, use it, otherwise use the cap
205
- if (!util.hasValue(dontStopAppOnReset)) {
206
- dontStopAppOnReset = !!this.opts.dontStopAppOnReset;
207
- }
208
-
209
- let args = {
210
- pkg: appPackage,
211
- activity: appActivity,
212
- waitPkg: appWaitPackage || appPackage,
213
- waitActivity: appWaitActivity || appActivity,
214
- action: intentAction,
215
- category: intentCategory,
216
- flags: intentFlags,
203
+ return preprocessStringsMap(this.apkStrings[language]);
204
+ },
205
+
206
+ async launchApp() {
207
+ await this.initAUT();
208
+ await this.startAUT();
209
+ },
210
+
211
+ async startActivity(
212
+ appPackage,
213
+ appActivity,
214
+ appWaitPackage,
215
+ appWaitActivity,
216
+ intentAction,
217
+ intentCategory,
218
+ intentFlags,
217
219
  optionalIntentArguments,
218
- stopApp: !dontStopAppOnReset
219
- };
220
- this._cachedActivityArgs = this._cachedActivityArgs || {};
221
- this._cachedActivityArgs[`${args.waitPkg}/${args.waitActivity}`] = args;
222
- await this.adb.startApp(args);
223
- };
224
-
225
- commands.reset = async function reset () {
226
- await androidHelpers.resetApp(this.adb, Object.assign({}, this.opts, {fastReset: true}));
227
- // reset context since we don't know what kind on context we will end up after app launch.
228
- await this.setContext();
229
- return await this.isChromeSession ? this.startChromeSession() : this.startAUT();
230
- };
231
-
232
- commands.startAUT = async function startAUT () {
233
- await this.adb.startApp({
234
- pkg: this.opts.appPackage,
235
- activity: this.opts.appActivity,
236
- action: this.opts.intentAction,
237
- category: this.opts.intentCategory,
238
- flags: this.opts.intentFlags,
239
- waitPkg: this.opts.appWaitPackage,
240
- waitActivity: this.opts.appWaitActivity,
241
- waitForLaunch: this.opts.appWaitForLaunch,
242
- waitDuration: this.opts.appWaitDuration,
243
- optionalIntentArguments: this.opts.optionalIntentArguments,
244
- stopApp: !this.opts.dontStopAppOnReset,
245
- user: this.opts.userProfile,
246
- });
247
- };
248
-
249
- // we override setUrl to take an android URI which can be used for deep-linking
250
- // inside an app, similar to starting an intent
251
- commands.setUrl = async function setUrl (uri) {
252
- await this.adb.startUri(uri, this.opts.appPackage);
253
- };
254
-
255
- // closing app using force stop
256
- commands.closeApp = async function closeApp () {
257
- await this.adb.forceStop(this.opts.appPackage);
258
- // reset context since we don't know what kind on context we will end up after app launch.
259
- await this.setContext();
260
- };
220
+ dontStopAppOnReset
221
+ ) {
222
+ this.log.debug(`Starting package '${appPackage}' and activity '${appActivity}'`);
223
+
224
+ // dontStopAppOnReset is both an argument here, and a desired capability
225
+ // if the argument is set, use it, otherwise use the cap
226
+ if (!util.hasValue(dontStopAppOnReset)) {
227
+ dontStopAppOnReset = !!this.opts.dontStopAppOnReset;
228
+ }
261
229
 
262
- commands.getDisplayDensity = async function getDisplayDensity () {
263
- // first try the property for devices
264
- let out = await this.adb.shell(['getprop', 'ro.sf.lcd_density']);
265
- if (out) {
266
- let val = parseInt(out, 10);
267
- // if the value is NaN, try getting the emulator property
268
- if (!isNaN(val)) {
269
- return val;
230
+ /** @type {import('appium-adb').StartAppOptions} */
231
+ let args = {
232
+ pkg: appPackage,
233
+ activity: appActivity,
234
+ waitPkg: appWaitPackage || appPackage,
235
+ waitActivity: appWaitActivity || appActivity,
236
+ action: intentAction,
237
+ category: intentCategory,
238
+ flags: intentFlags,
239
+ optionalIntentArguments,
240
+ stopApp: !dontStopAppOnReset,
241
+ };
242
+ this._cachedActivityArgs = this._cachedActivityArgs || {};
243
+ this._cachedActivityArgs[`${args.waitPkg}/${args.waitActivity}`] = args;
244
+ await /** @type {ADB} */ (this.adb).startApp(args);
245
+ },
246
+
247
+ async reset() {
248
+ await androidHelpers.resetApp(
249
+ /** @type {ADB} */ (this.adb),
250
+ Object.assign({}, this.opts, {fastReset: true})
251
+ );
252
+ // reset context since we don't know what kind on context we will end up after app launch.
253
+ await this.setContext();
254
+ return this.isChromeSession ? this.startChromeSession() : this.startAUT();
255
+ },
256
+
257
+ async startAUT() {
258
+ await /** @type {ADB} */ (this.adb).startApp({
259
+ pkg: /** @type {string} */ (this.opts.appPackage),
260
+ activity: this.opts.appActivity,
261
+ action: this.opts.intentAction,
262
+ category: this.opts.intentCategory,
263
+ flags: this.opts.intentFlags,
264
+ waitPkg: this.opts.appWaitPackage,
265
+ waitActivity: this.opts.appWaitActivity,
266
+ waitForLaunch: this.opts.appWaitForLaunch,
267
+ waitDuration: this.opts.appWaitDuration,
268
+ optionalIntentArguments: this.opts.optionalIntentArguments,
269
+ stopApp: !this.opts.dontStopAppOnReset,
270
+ user: this.opts.userProfile,
271
+ });
272
+ },
273
+
274
+ // we override setUrl to take an android URI which can be used for deep-linking
275
+ // inside an app, similar to starting an intent
276
+ async setUrl(uri) {
277
+ await /** @type {ADB} */ (this.adb).startUri(uri, /** @type {string} */ (this.opts.appPackage));
278
+ },
279
+
280
+ // closing app using force stop
281
+ async closeApp() {
282
+ await /** @type {ADB} */ (this.adb).forceStop(/** @type {string} */ (this.opts.appPackage));
283
+ // reset context since we don't know what kind on context we will end up after app launch.
284
+ await this.setContext();
285
+ },
286
+
287
+ async getDisplayDensity() {
288
+ // first try the property for devices
289
+ let out = await /** @type {ADB} */ (this.adb).shell(['getprop', 'ro.sf.lcd_density']);
290
+ if (out) {
291
+ let val = parseInt(out, 10);
292
+ // if the value is NaN, try getting the emulator property
293
+ if (!isNaN(val)) {
294
+ return val;
295
+ }
296
+ this.log.debug(`Parsed density value was NaN: "${out}"`);
270
297
  }
271
- this.log.debug(`Parsed density value was NaN: "${out}"`);
272
- }
273
- // fallback to trying property for emulators
274
- out = await this.adb.shell(['getprop', 'qemu.sf.lcd_density']);
275
- if (out) {
276
- let val = parseInt(out, 10);
277
- if (!isNaN(val)) {
278
- return val;
298
+ // fallback to trying property for emulators
299
+ out = await /** @type {ADB} */ (this.adb).shell(['getprop', 'qemu.sf.lcd_density']);
300
+ if (out) {
301
+ let val = parseInt(out, 10);
302
+ if (!isNaN(val)) {
303
+ return val;
304
+ }
305
+ this.log.debug(`Parsed density value was NaN: "${out}"`);
279
306
  }
280
- this.log.debug(`Parsed density value was NaN: "${out}"`);
281
- }
282
- // couldn't get anything, so error out
283
- this.log.errorAndThrow('Failed to get display density property.');
284
- };
285
-
286
- commands.mobilePerformEditorAction = async function mobilePerformEditorAction (opts = {}) {
287
- const {action} = opts;
288
- if (!util.hasValue(action)) {
289
- throw new errors.InvalidArgumentError(`'action' argument is required`);
290
- }
291
-
292
- await this.adb.performEditorAction(action);
307
+ // couldn't get anything, so error out
308
+ this.log.errorAndThrow('Failed to get display density property.');
309
+ throw new Error(); // unreachable
310
+ },
311
+
312
+ async mobilePerformEditorAction(opts) {
313
+ const {action} = requireArgs('action', opts);
314
+ await /** @type {ADB} */ (this.adb).performEditorAction(action);
315
+ },
316
+
317
+ async mobileGetNotifications() {
318
+ return await /** @type {ADB} */ (this.adb).getNotifications();
319
+ },
320
+
321
+ async mobileListSms(opts) {
322
+ return await /** @type {ADB} */ (this.adb).getSmsList(opts);
323
+ },
324
+
325
+ async mobileUnlock(opts = {}) {
326
+ const {key, type, strategy, timeoutMs} = opts;
327
+ if (!key && !type) {
328
+ await this.unlock();
329
+ } else {
330
+ // @ts-expect-error XXX: these caps should be defined in the constraints!!
331
+ await androidHelpers.unlock(this, /** @type {ADB} */ (this.adb), {
332
+ unlockKey: key,
333
+ unlockType: type,
334
+ unlockStrategy: strategy,
335
+ unlockSuccessTimeout: timeoutMs,
336
+ });
337
+ }
338
+ },
293
339
  };
294
340
 
295
- /**
296
- * Retrieves the list of recent system notifications.
297
- *
298
- * @returns {Object} See the documentation on `adb.getNotifications` for
299
- * more details
300
- */
301
- commands.mobileGetNotifications = async function mobileGetNotifications () {
302
- return await this.adb.getNotifications();
303
- };
341
+ mixin(GeneralMixin);
304
342
 
305
- /**
306
- * @typedef {Object} SmsListOptions
307
- * @property {number} max [100] - The maximum count of recent SMS messages
308
- * to retrieve
309
- */
343
+ export default GeneralMixin;
310
344
 
311
345
  /**
312
- * Retrieves the list of recent SMS messages with their properties.
313
- *
314
- * @param {SmsListOptions} opts
315
- * @returns {Object} See the documentation on `adb.getSmsList` for
316
- * more details
346
+ * @typedef {import('../bootstrap').AndroidBootstrap} AndroidBootstrap
347
+ * @typedef {import('appium-adb').ADB} ADB
348
+ * @typedef {import('@appium/types').StringRecord} StringRecord
317
349
  */
318
- commands.mobileListSms = async function mobileListSms (opts = {}) {
319
- return await this.adb.getSmsList(opts);
320
- };
321
-
322
- /**
323
- * @typedef {Object} UnlockOptions
324
- * @property {string?} key The unlock key. The value of this key depends
325
- * on the actual unlock type and could be a pin/password/pattern value or
326
- * a biometric finger id.
327
- * If not provided then the corresponding value from session capabilities is used.
328
- * @property {string?} type The unlock type. The following unlock types
329
- * are supported: `pin`, `pinWithKeyEvent`, `password`, `pattern` and `fingerprint`.
330
- * If not provided then the corresponding value from session capabilities is used.
331
- * @property {string?} strategy Either 'locksettings' (default) or 'uiautomator'.
332
- * Setting it to 'uiautomator' will enforce the driver to avoid using special
333
- * ADB shortcuts in order to speed up the unlock procedure.
334
- * @property {number?} timeoutMs [2000] The maximum time in milliseconds
335
- * to wait until the screen gets unlocked
336
- */
337
-
338
- /**
339
- * Unlocks the device if it is locked. Noop if the device's screen is not locked.
340
- *
341
- * @param {UnlockOptions} opts
342
- * @throws {Error} if unlock operation fails or the provided
343
- * arguments are not valid
344
- */
345
- commands.mobileUnlock = async function mobileUnlock (opts = {}) {
346
- const { key, type, strategy, timeoutMs } = opts;
347
- if (!key && !type) {
348
- await this.unlock();
349
- } else {
350
- await androidHelpers.unlock(this, this.adb, {
351
- unlockKey: key,
352
- unlockType: type,
353
- unlockStrategy: strategy,
354
- unlockSuccessTimeout: timeoutMs,
355
- });
356
- }
357
- };
358
-
359
- Object.assign(extensions, commands, helpers);
360
- export { commands, helpers };
361
- export default extensions;