appium-adb 9.5.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/build/lib/helpers.js +88 -3
- package/build/lib/tools/adb-commands.js +18 -7
- package/lib/helpers.js +91 -4
- package/lib/tools/adb-commands.js +14 -7
- package/package.json +1 -1
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
|
-
|
|
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,7 @@ methods.mkdir = async function mkdir (remotePath) {
|
|
|
207
208
|
*/
|
|
208
209
|
methods.isValidClass = function isValidClass (classString) {
|
|
209
210
|
// some.package/some.package.Activity
|
|
210
|
-
return
|
|
211
|
+
return matchComponentName(classString);
|
|
211
212
|
};
|
|
212
213
|
|
|
213
214
|
/**
|
|
@@ -215,20 +216,26 @@ methods.isValidClass = function isValidClass (classString) {
|
|
|
215
216
|
* given package. It is expected the package is already installed on
|
|
216
217
|
* the device under test.
|
|
217
218
|
*
|
|
218
|
-
* ! This method only works since Android 7 (API level 24)
|
|
219
|
-
*
|
|
220
219
|
* @param {string} pkg - The target package identifier
|
|
221
220
|
* @return {string} Fully qualified name of the launchable activity
|
|
222
221
|
* @throws {Error} If there was an error while resolving the activity name
|
|
223
222
|
*/
|
|
224
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
|
+
}
|
|
225
233
|
const {stdout, stderr} = await this.shell(['cmd', 'package', 'resolve-activity', '--brief', pkg], {
|
|
226
234
|
outputFormat: this.EXEC_OUTPUT_FORMAT.FULL
|
|
227
235
|
});
|
|
228
|
-
for (const line of (stdout || '').split('\n')) {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
return trimmedLine;
|
|
236
|
+
for (const line of (stdout || '').split('\n').map(_.trim)) {
|
|
237
|
+
if (this.isValidClass(line)) {
|
|
238
|
+
return line;
|
|
232
239
|
}
|
|
233
240
|
}
|
|
234
241
|
throw new Error(`Unable to resolve the launchable activity of '${pkg}'. Original error: ${stderr || stdout}`);
|