appium-adb 9.3.0 → 9.6.0

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/lib/helpers.js CHANGED
@@ -12,6 +12,8 @@ const APK_EXTENSION = '.apk';
12
12
  const APK_INSTALL_TIMEOUT = 60000;
13
13
  const APKS_INSTALL_TIMEOUT = APK_INSTALL_TIMEOUT * 2;
14
14
  const DEFAULT_ADB_EXEC_TIMEOUT = 20000; // in milliseconds
15
+ const MAIN_ACTION = 'android.intent.action.MAIN';
16
+ const LAUNCHER_CATEGORY = 'android.intent.category.LAUNCHER';
15
17
 
16
18
  /**
17
19
  * Retrieves the actual path to SDK root folder from the system environment
@@ -570,10 +572,8 @@ function parseManifest (manifest) {
570
572
  continue;
571
573
  }
572
574
 
573
- const isMainAction = actions
574
- .some(({name}) => name === 'android.intent.action.MAIN');
575
- const isLauncherCategory = categories
576
- .some(({name}) => name === 'android.intent.category.LAUNCHER');
575
+ const isMainAction = actions.some(({name}) => name === MAIN_ACTION);
576
+ const isLauncherCategory = categories.some(({name}) => name === LAUNCHER_CATEGORY);
577
577
  if (isMainAction && isLauncherCategory) {
578
578
  result.activity = activity.name;
579
579
  return result;
@@ -930,6 +930,92 @@ function escapeShellArg (arg) {
930
930
  return arg.replace(/&/g, '\\&');
931
931
  }
932
932
 
933
+ /**
934
+ * Parses the name of launchable package activity
935
+ * from dumpsys output.
936
+ *
937
+ * @param {string} dumpsys the actual dumpsys output
938
+ * @returns {?string} Either the fully qualified
939
+ * activity name or null if it cannot be parsed
940
+ */
941
+ function parseLaunchableActivityName (dumpsys) {
942
+ const mainActivityNameRe = new RegExp(`^\\s*${_.escapeRegExp(MAIN_ACTION)}:$`);
943
+ const categoryNameRe = /^\s*Category:\s+"([a-zA-Z0-9._/-]+)"$/;
944
+ const blocks = [];
945
+ let blockStartIndent;
946
+ let block = [];
947
+ for (const line of dumpsys.split('\n').map(_.trimEnd)) {
948
+ const currentIndent = line.length - _.trimStart(line).length;
949
+ if (mainActivityNameRe.test(line)) {
950
+ blockStartIndent = currentIndent;
951
+ if (!_.isEmpty(block)) {
952
+ blocks.push(block);
953
+ block = [];
954
+ }
955
+ continue;
956
+ }
957
+ if (_.isNil(blockStartIndent)) {
958
+ continue;
959
+ }
960
+
961
+ if (currentIndent > blockStartIndent) {
962
+ block.push(line);
963
+ } else {
964
+ if (!_.isEmpty(block)) {
965
+ blocks.push(block);
966
+ block = [];
967
+ }
968
+ blockStartIndent = null;
969
+ }
970
+ }
971
+ if (!_.isEmpty(block)) {
972
+ blocks.push(block);
973
+ }
974
+
975
+ for (const item of blocks) {
976
+ let hasCategory = false;
977
+ let isLauncherCategory = false;
978
+ for (const line of item) {
979
+ const match = categoryNameRe.exec(line);
980
+ if (!match) {
981
+ continue;
982
+ }
983
+
984
+ hasCategory = true;
985
+ isLauncherCategory = match[1] === LAUNCHER_CATEGORY;
986
+ break;
987
+ }
988
+ // On older Android versions the category name
989
+ // might not be listed, so we just try to fetch
990
+ // the first match instead
991
+ if (hasCategory && !isLauncherCategory) {
992
+ continue;
993
+ }
994
+
995
+ const activityNameStr = _.trim(item[0]);
996
+ if (!activityNameStr) {
997
+ continue;
998
+ }
999
+ const fqActivityName = activityNameStr.split(/\s+/)[1];
1000
+ if (matchComponentName(fqActivityName)) {
1001
+ return fqActivityName;
1002
+ }
1003
+ }
1004
+ return null;
1005
+ }
1006
+
1007
+ /**
1008
+ * Check if the given string is a valid component name
1009
+ *
1010
+ * @param {string} classString The string to verify
1011
+ * @return {?Array<Match>} The result of Regexp.exec operation
1012
+ * or _null_ if no matches are found
1013
+ */
1014
+ function matchComponentName (classString) {
1015
+ // some.package/some.package.Activity
1016
+ return /^[a-z0-9./_]+$/i.exec(classString);
1017
+ }
1018
+
933
1019
  export {
934
1020
  getAndroidPlatformAndPath, unzipFile,
935
1021
  getIMEListFromOutput, getJavaForOs, isShowingLockscreen, isCurrentFocusOnKeyguard,
@@ -940,4 +1026,5 @@ export {
940
1026
  DEFAULT_ADB_EXEC_TIMEOUT, parseManifest, parseAaptStrings, parseAapt2Strings,
941
1027
  formatConfigMarker, parseJsonData, unsignApk, toAvdLocaleArgs, requireSdkRoot,
942
1028
  getSdkRootFromEnv, getAndroidPrefsRoot, dirExists, escapeShellArg,
1029
+ parseLaunchableActivityName, matchComponentName,
943
1030
  };
@@ -1,6 +1,7 @@
1
1
  import log from '../logger.js';
2
2
  import {
3
3
  getIMEListFromOutput, getSurfaceOrientation, extractMatchingPermissions,
4
+ parseLaunchableActivityName, matchComponentName,
4
5
  } from '../helpers.js';
5
6
  import path from 'path';
6
7
  import _ from 'lodash';
@@ -207,7 +208,37 @@ methods.mkdir = async function mkdir (remotePath) {
207
208
  */
208
209
  methods.isValidClass = function isValidClass (classString) {
209
210
  // some.package/some.package.Activity
210
- return new RegExp(/^[a-zA-Z0-9./_]+$/).exec(classString);
211
+ return matchComponentName(classString);
212
+ };
213
+
214
+ /**
215
+ * Fetches the fully qualified name of the launchable activity for the
216
+ * given package. It is expected the package is already installed on
217
+ * the device under test.
218
+ *
219
+ * @param {string} pkg - The target package identifier
220
+ * @return {string} Fully qualified name of the launchable activity
221
+ * @throws {Error} If there was an error while resolving the activity name
222
+ */
223
+ methods.resolveLaunchableActivity = async function resolveLaunchableActivity (pkg) {
224
+ if (await this.getApiLevel() < 24) {
225
+ const stdout = await this.shell(['dumpsys', 'package', pkg]);
226
+ const result = parseLaunchableActivityName(stdout);
227
+ if (result) {
228
+ return result;
229
+ }
230
+ log.debug(stdout);
231
+ throw new Error(`Unable to resolve the launchable activity of '${pkg}'. Is it installed on the device?`);
232
+ }
233
+ const {stdout, stderr} = await this.shell(['cmd', 'package', 'resolve-activity', '--brief', pkg], {
234
+ outputFormat: this.EXEC_OUTPUT_FORMAT.FULL
235
+ });
236
+ for (const line of (stdout || '').split('\n').map(_.trim)) {
237
+ if (this.isValidClass(line)) {
238
+ return line;
239
+ }
240
+ }
241
+ throw new Error(`Unable to resolve the launchable activity of '${pkg}'. Original error: ${stderr || stdout}`);
211
242
  };
212
243
 
213
244
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appium-adb",
3
- "version": "9.3.0",
3
+ "version": "9.6.0",
4
4
  "description": "Android Debug Bridge interface",
5
5
  "main": "./build/index.js",
6
6
  "scripts": {