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
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [14.0.6](https://github.com/appium/appium-adb/compare/v14.0.5...v14.0.6) (2025-12-05)
|
|
2
|
+
|
|
3
|
+
### Miscellaneous Chores
|
|
4
|
+
|
|
5
|
+
* Migrate apk-utils and device-settings to typescript ([#844](https://github.com/appium/appium-adb/issues/844)) ([d5bd207](https://github.com/appium/appium-adb/commit/d5bd20751e1f69b1f54db173241d42a55ffb596c))
|
|
6
|
+
|
|
7
|
+
## [14.0.5](https://github.com/appium/appium-adb/compare/v14.0.4...v14.0.5) (2025-12-04)
|
|
8
|
+
|
|
9
|
+
### Miscellaneous Chores
|
|
10
|
+
|
|
11
|
+
* Migrate app commands to typescript ([#843](https://github.com/appium/appium-adb/issues/843)) ([8993e7c](https://github.com/appium/appium-adb/commit/8993e7c5fcbabda3796b8246ecac39046bf6fe3f))
|
|
12
|
+
|
|
1
13
|
## [14.0.4](https://github.com/appium/appium-adb/compare/v14.0.3...v14.0.4) (2025-12-04)
|
|
2
14
|
|
|
3
15
|
### Miscellaneous Chores
|
|
@@ -1,122 +1,99 @@
|
|
|
1
|
+
import type { ADB } from '../adb.js';
|
|
2
|
+
import type { UninstallOptions, ShellExecOptions, CachingOptions, InstallOptions, InstallOrUpgradeOptions, InstallOrUpgradeResult, ApkStrings, AppInfo, InstallState, StringRecord } from './types.js';
|
|
3
|
+
export declare const REMOTE_CACHE_ROOT = "/data/local/tmp/appium_cache";
|
|
1
4
|
/**
|
|
2
5
|
* Uninstall the given package from the device under test.
|
|
3
6
|
*
|
|
4
|
-
* @
|
|
5
|
-
* @param
|
|
6
|
-
* @
|
|
7
|
-
* @return {Promise<boolean>} True if the package was found on the device and
|
|
7
|
+
* @param pkg - The name of the package to be uninstalled.
|
|
8
|
+
* @param options - The set of uninstall options.
|
|
9
|
+
* @returns True if the package was found on the device and
|
|
8
10
|
* successfully uninstalled.
|
|
9
11
|
*/
|
|
10
|
-
export function uninstallApk(this:
|
|
12
|
+
export declare function uninstallApk(this: ADB, pkg: string, options?: UninstallOptions): Promise<boolean>;
|
|
11
13
|
/**
|
|
12
14
|
* Install the package after it was pushed to the device under test.
|
|
13
15
|
*
|
|
14
|
-
* @
|
|
15
|
-
* @param
|
|
16
|
-
* @
|
|
17
|
-
* @throws {error} If there was a failure during application install.
|
|
16
|
+
* @param apkPathOnDevice - The full path to the package on the device file system.
|
|
17
|
+
* @param opts - Additional exec options.
|
|
18
|
+
* @throws If there was a failure during application install.
|
|
18
19
|
*/
|
|
19
|
-
export function installFromDevicePath(this:
|
|
20
|
+
export declare function installFromDevicePath(this: ADB, apkPathOnDevice: string, opts?: ShellExecOptions): Promise<void>;
|
|
20
21
|
/**
|
|
21
22
|
* Caches the given APK at a remote location to speed up further APK deployments.
|
|
22
23
|
*
|
|
23
|
-
* @
|
|
24
|
-
* @param
|
|
25
|
-
* @
|
|
26
|
-
* @
|
|
27
|
-
* @throws {Error} if there was a failure while caching the app
|
|
24
|
+
* @param apkPath - Full path to the apk on the local FS
|
|
25
|
+
* @param options - Caching options
|
|
26
|
+
* @returns Full path to the cached apk on the remote file system
|
|
27
|
+
* @throws if there was a failure while caching the app
|
|
28
28
|
*/
|
|
29
|
-
export function cacheApk(this:
|
|
30
|
-
export class cacheApk {
|
|
31
|
-
/**
|
|
32
|
-
* Caches the given APK at a remote location to speed up further APK deployments.
|
|
33
|
-
*
|
|
34
|
-
* @this {import('../adb.js').ADB}
|
|
35
|
-
* @param {string} apkPath - Full path to the apk on the local FS
|
|
36
|
-
* @param {import('./types').CachingOptions} [options={}] - Caching options
|
|
37
|
-
* @returns {Promise<string>} - Full path to the cached apk on the remote file system
|
|
38
|
-
* @throws {Error} if there was a failure while caching the app
|
|
39
|
-
*/
|
|
40
|
-
constructor(this: import("../adb.js").ADB, apkPath: string, options?: import("./types").CachingOptions);
|
|
41
|
-
_areExtendedLsOptionsSupported: boolean | undefined;
|
|
42
|
-
remoteAppsCache: LRUCache<string, string, unknown>;
|
|
43
|
-
}
|
|
29
|
+
export declare function cacheApk(this: ADB, apkPath: string, options?: CachingOptions): Promise<string>;
|
|
44
30
|
/**
|
|
45
31
|
* Install the package from the local file system.
|
|
46
32
|
*
|
|
47
|
-
* @
|
|
48
|
-
* @param
|
|
49
|
-
* @
|
|
50
|
-
* @throws {Error} If an unexpected error happens during install.
|
|
33
|
+
* @param appPath - The full path to the local package.
|
|
34
|
+
* @param options - The set of installation options.
|
|
35
|
+
* @throws If an unexpected error happens during install.
|
|
51
36
|
*/
|
|
52
|
-
export function install(this:
|
|
37
|
+
export declare function install(this: ADB, appPath: string, options?: InstallOptions): Promise<void>;
|
|
53
38
|
/**
|
|
54
39
|
* Retrieves the current installation state of the particular application
|
|
55
40
|
*
|
|
56
|
-
* @
|
|
57
|
-
* @param
|
|
58
|
-
* @param {string?} [pkg=null] - Package identifier. If omitted then the script will
|
|
41
|
+
* @param appPath - Full path to the application
|
|
42
|
+
* @param pkg - Package identifier. If omitted then the script will
|
|
59
43
|
* try to extract it on its own
|
|
60
|
-
* @returns
|
|
44
|
+
* @returns One of `APP_INSTALL_STATE` constants
|
|
61
45
|
*/
|
|
62
|
-
export function getApplicationInstallState(this:
|
|
46
|
+
export declare function getApplicationInstallState(this: ADB, appPath: string, pkg?: string | null): Promise<InstallState>;
|
|
63
47
|
/**
|
|
64
48
|
* Install the package from the local file system or upgrade it if an older
|
|
65
49
|
* version of the same package is already installed.
|
|
66
50
|
*
|
|
67
|
-
* @
|
|
68
|
-
* @param
|
|
69
|
-
* @param {string?} [pkg=null] - The name of the installed package. The method will
|
|
51
|
+
* @param appPath - The full path to the local package.
|
|
52
|
+
* @param pkg - The name of the installed package. The method will
|
|
70
53
|
* perform faster if it is set.
|
|
71
|
-
* @param
|
|
72
|
-
* @throws
|
|
73
|
-
* @returns {Promise<import('./types').InstallOrUpgradeResult>}
|
|
54
|
+
* @param options - Set of install options.
|
|
55
|
+
* @throws If an unexpected error happens during install.
|
|
74
56
|
*/
|
|
75
|
-
export function installOrUpgrade(this:
|
|
57
|
+
export declare function installOrUpgrade(this: ADB, appPath: string, pkg?: string | null, options?: InstallOrUpgradeOptions): Promise<InstallOrUpgradeResult>;
|
|
76
58
|
/**
|
|
77
59
|
* Extract string resources from the given package on local file system.
|
|
78
60
|
*
|
|
79
|
-
* @
|
|
80
|
-
* @param
|
|
81
|
-
* @param {string?} [language=null] - The name of the language to extract the resources for.
|
|
61
|
+
* @param appPath - The full path to the .apk(s) package.
|
|
62
|
+
* @param language - The name of the language to extract the resources for.
|
|
82
63
|
* The default language is used if this equals to `null`
|
|
83
|
-
* @param
|
|
64
|
+
* @param outRoot - The name of the destination folder on the local file system to
|
|
84
65
|
* store the extracted file to. If not provided then the `localPath` property in the returned object
|
|
85
66
|
* will be undefined.
|
|
86
|
-
* @return {Promise<import('./types').ApkStrings>}
|
|
87
67
|
*/
|
|
88
|
-
export function extractStringsFromApk(this:
|
|
68
|
+
export declare function extractStringsFromApk(this: ADB, appPath: string, language?: string | null, outRoot?: string | null): Promise<ApkStrings>;
|
|
89
69
|
/**
|
|
90
70
|
* Get the package info from local apk file.
|
|
91
71
|
*
|
|
92
|
-
* @
|
|
93
|
-
* @param {string} appPath - The full path to existing .apk(s) package on the local
|
|
72
|
+
* @param appPath - The full path to existing .apk(s) package on the local
|
|
94
73
|
* file system.
|
|
95
|
-
* @
|
|
74
|
+
* @returns The parsed application information.
|
|
96
75
|
*/
|
|
97
|
-
export function getApkInfo(this:
|
|
76
|
+
export declare function getApkInfo(this: ADB, appPath: string): Promise<AppInfo | {}>;
|
|
98
77
|
/**
|
|
99
78
|
* Parses apk strings from aapt2 tool output
|
|
100
79
|
*
|
|
101
|
-
* @param
|
|
102
|
-
* @param
|
|
80
|
+
* @param rawOutput The actual tool output
|
|
81
|
+
* @param configMarker The config marker. Usually
|
|
103
82
|
* a language abbreviation or an empty string for the default one
|
|
104
|
-
* @returns
|
|
83
|
+
* @returns Strings ids to values mapping. Plural
|
|
105
84
|
* values are represented as arrays. If no config found for the
|
|
106
85
|
* given marker then an empty mapping is returned.
|
|
107
86
|
*/
|
|
108
|
-
export function parseAapt2Strings(rawOutput: string, configMarker: string):
|
|
87
|
+
export declare function parseAapt2Strings(rawOutput: string, configMarker: string): StringRecord;
|
|
109
88
|
/**
|
|
110
89
|
* Parses apk strings from aapt tool output
|
|
111
90
|
*
|
|
112
|
-
* @param
|
|
113
|
-
* @param
|
|
91
|
+
* @param rawOutput The actual tool output
|
|
92
|
+
* @param configMarker The config marker. Usually
|
|
114
93
|
* a language abbreviation or `(default)`
|
|
115
|
-
* @returns
|
|
94
|
+
* @returns Strings ids to values mapping. Plural
|
|
116
95
|
* values are represented as arrays. If no config found for the
|
|
117
96
|
* given marker then an empty mapping is returned.
|
|
118
97
|
*/
|
|
119
|
-
export function parseAaptStrings(rawOutput: string, configMarker: string):
|
|
120
|
-
export const REMOTE_CACHE_ROOT: "/data/local/tmp/appium_cache";
|
|
121
|
-
import { LRUCache } from 'lru-cache';
|
|
98
|
+
export declare function parseAaptStrings(rawOutput: string, configMarker: string): StringRecord;
|
|
122
99
|
//# sourceMappingURL=apk-utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apk-utils.d.ts","sourceRoot":"","sources":["../../../lib/tools/apk-utils.
|
|
1
|
+
{"version":3,"file":"apk-utils.d.ts","sourceRoot":"","sources":["../../../lib/tools/apk-utils.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,uBAAuB,EACvB,sBAAsB,EACtB,UAAU,EACV,OAAO,EACP,YAAY,EACZ,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,iBAAiB,iCAAiC,CAAC;AAEhE;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC,CA4B5G;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CAAE,IAAI,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM,EAAE,IAAI,GAAE,gBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAK3H;AAED;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CA8EzG;AAED;;;;;;GAMG;AACH,wBAAsB,OAAO,CAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkGtG;AAED;;;;;;;GAOG;AACH,wBAAsB,0BAA0B,CAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,GAAG,IAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CA8D9H;AAED;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,GAAG,IAAW,EAAE,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAwErK;AAED;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,GAAG,EACT,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAAM,GAAG,IAAW,EAC9B,OAAO,GAAE,MAAM,GAAG,IAAW,GAC5B,OAAO,CAAC,UAAU,CAAC,CAiErB;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,EAAE,CAAC,CAqBnF;AAiCD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,YAAY,CAwGxF;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,YAAY,CAoEvF"}
|
|
@@ -60,10 +60,9 @@ exports.REMOTE_CACHE_ROOT = '/data/local/tmp/appium_cache';
|
|
|
60
60
|
/**
|
|
61
61
|
* Uninstall the given package from the device under test.
|
|
62
62
|
*
|
|
63
|
-
* @
|
|
64
|
-
* @param
|
|
65
|
-
* @
|
|
66
|
-
* @return {Promise<boolean>} True if the package was found on the device and
|
|
63
|
+
* @param pkg - The name of the package to be uninstalled.
|
|
64
|
+
* @param options - The set of uninstall options.
|
|
65
|
+
* @returns True if the package was found on the device and
|
|
67
66
|
* successfully uninstalled.
|
|
68
67
|
*/
|
|
69
68
|
async function uninstallApk(pkg, options = {}) {
|
|
@@ -83,7 +82,8 @@ async function uninstallApk(pkg, options = {}) {
|
|
|
83
82
|
stdout = (await this.adbExec(cmd, { timeout: options.timeout })).trim();
|
|
84
83
|
}
|
|
85
84
|
catch (e) {
|
|
86
|
-
|
|
85
|
+
const err = e;
|
|
86
|
+
throw new Error(`Unable to uninstall APK. Original error: ${err.message}`);
|
|
87
87
|
}
|
|
88
88
|
logger_js_1.log.debug(`'adb ${cmd.join(' ')}' command output: ${stdout}`);
|
|
89
89
|
if (stdout.includes('Success')) {
|
|
@@ -96,13 +96,12 @@ async function uninstallApk(pkg, options = {}) {
|
|
|
96
96
|
/**
|
|
97
97
|
* Install the package after it was pushed to the device under test.
|
|
98
98
|
*
|
|
99
|
-
* @
|
|
100
|
-
* @param
|
|
101
|
-
* @
|
|
102
|
-
* @throws {error} If there was a failure during application install.
|
|
99
|
+
* @param apkPathOnDevice - The full path to the package on the device file system.
|
|
100
|
+
* @param opts - Additional exec options.
|
|
101
|
+
* @throws If there was a failure during application install.
|
|
103
102
|
*/
|
|
104
103
|
async function installFromDevicePath(apkPathOnDevice, opts = {}) {
|
|
105
|
-
const stdout =
|
|
104
|
+
const stdout = await this.shell(['pm', 'install', '-r', apkPathOnDevice], opts);
|
|
106
105
|
if (stdout.includes('Failure')) {
|
|
107
106
|
throw new Error(`Remote install failed: ${stdout}`);
|
|
108
107
|
}
|
|
@@ -110,11 +109,10 @@ async function installFromDevicePath(apkPathOnDevice, opts = {}) {
|
|
|
110
109
|
/**
|
|
111
110
|
* Caches the given APK at a remote location to speed up further APK deployments.
|
|
112
111
|
*
|
|
113
|
-
* @
|
|
114
|
-
* @param
|
|
115
|
-
* @
|
|
116
|
-
* @
|
|
117
|
-
* @throws {Error} if there was a failure while caching the app
|
|
112
|
+
* @param apkPath - Full path to the apk on the local FS
|
|
113
|
+
* @param options - Caching options
|
|
114
|
+
* @returns Full path to the cached apk on the remote file system
|
|
115
|
+
* @throws if there was a failure while caching the app
|
|
118
116
|
*/
|
|
119
117
|
async function cacheApk(apkPath, options = {}) {
|
|
120
118
|
const appHash = await support_1.fs.hash(apkPath);
|
|
@@ -146,7 +144,8 @@ async function cacheApk(apkPath, options = {}) {
|
|
|
146
144
|
.filter(Boolean)));
|
|
147
145
|
}
|
|
148
146
|
catch (e) {
|
|
149
|
-
|
|
147
|
+
const err = e;
|
|
148
|
+
logger_js_1.log.debug(`Got an error '${err.message.trim()}' while getting the list of files in the cache. ` +
|
|
150
149
|
`Assuming the cache does not exist yet`);
|
|
151
150
|
await this.shell(['mkdir', '-p', exports.REMOTE_CACHE_ROOT]);
|
|
152
151
|
}
|
|
@@ -170,27 +169,28 @@ async function cacheApk(apkPath, options = {}) {
|
|
|
170
169
|
}
|
|
171
170
|
if (!this.remoteAppsCache) {
|
|
172
171
|
this.remoteAppsCache = new lru_cache_1.LRUCache({
|
|
173
|
-
max:
|
|
172
|
+
max: this.remoteAppsCacheLimit,
|
|
174
173
|
});
|
|
175
174
|
}
|
|
176
175
|
// Cleanup the invalid entries from the cache
|
|
177
176
|
lodash_1.default.difference([...this.remoteAppsCache.keys()], remoteCachedFiles.map(toHash))
|
|
178
|
-
.forEach((hash) =>
|
|
177
|
+
.forEach((hash) => this.remoteAppsCache.delete(hash));
|
|
179
178
|
// Bump the cache record for the recently cached item
|
|
180
179
|
this.remoteAppsCache.set(appHash, remotePath);
|
|
181
180
|
// If the remote cache exceeds this.remoteAppsCacheLimit, remove the least recently used entries
|
|
182
181
|
const entriesToCleanup = remoteCachedFiles
|
|
183
182
|
.map((x) => path_1.default.posix.join(exports.REMOTE_CACHE_ROOT, x))
|
|
184
|
-
.filter((x) => !
|
|
185
|
-
.slice(
|
|
183
|
+
.filter((x) => !this.remoteAppsCache.has(toHash(x)))
|
|
184
|
+
.slice(this.remoteAppsCacheLimit - [...this.remoteAppsCache.keys()].length);
|
|
186
185
|
if (!lodash_1.default.isEmpty(entriesToCleanup)) {
|
|
187
186
|
try {
|
|
188
187
|
await this.shell(['rm', '-f', ...entriesToCleanup]);
|
|
189
188
|
logger_js_1.log.debug(`Deleted ${entriesToCleanup.length} expired application cache entries`);
|
|
190
189
|
}
|
|
191
190
|
catch (e) {
|
|
191
|
+
const err = e;
|
|
192
192
|
logger_js_1.log.warn(`Cannot delete ${entriesToCleanup.length} expired application cache entries. ` +
|
|
193
|
-
`Original error: ${
|
|
193
|
+
`Original error: ${err.message}`);
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
return remotePath;
|
|
@@ -198,10 +198,9 @@ async function cacheApk(apkPath, options = {}) {
|
|
|
198
198
|
/**
|
|
199
199
|
* Install the package from the local file system.
|
|
200
200
|
*
|
|
201
|
-
* @
|
|
202
|
-
* @param
|
|
203
|
-
* @
|
|
204
|
-
* @throws {Error} If an unexpected error happens during install.
|
|
201
|
+
* @param appPath - The full path to the local package.
|
|
202
|
+
* @param options - The set of installation options.
|
|
203
|
+
* @throws If an unexpected error happens during install.
|
|
205
204
|
*/
|
|
206
205
|
async function install(appPath, options = {}) {
|
|
207
206
|
if (appPath.endsWith(helpers_js_1.APKS_EXTENSION)) {
|
|
@@ -230,7 +229,7 @@ async function install(appPath, options = {}) {
|
|
|
230
229
|
];
|
|
231
230
|
let performAppInstall = async () => await this.adbExec(installCmd, installOpts);
|
|
232
231
|
// this.remoteAppsCacheLimit <= 0 means no caching should be applied
|
|
233
|
-
let shouldCacheApp =
|
|
232
|
+
let shouldCacheApp = this.remoteAppsCacheLimit > 0;
|
|
234
233
|
if (shouldCacheApp) {
|
|
235
234
|
shouldCacheApp = !(await this.isStreamedInstallSupported());
|
|
236
235
|
if (!shouldCacheApp) {
|
|
@@ -269,15 +268,16 @@ async function install(appPath, options = {}) {
|
|
|
269
268
|
};
|
|
270
269
|
}
|
|
271
270
|
catch (e) {
|
|
272
|
-
|
|
273
|
-
logger_js_1.log.
|
|
271
|
+
const err = e;
|
|
272
|
+
logger_js_1.log.debug(err);
|
|
273
|
+
logger_js_1.log.warn(`There was a failure while caching '${appPath}': ${err.message}`);
|
|
274
274
|
logger_js_1.log.warn('Falling back to the default installation procedure');
|
|
275
275
|
await clearCache();
|
|
276
276
|
}
|
|
277
277
|
}
|
|
278
278
|
try {
|
|
279
279
|
const timer = new support_1.timing.Timer().start();
|
|
280
|
-
const output =
|
|
280
|
+
const output = await performAppInstall();
|
|
281
281
|
logger_js_1.log.info(`The installation of '${path_1.default.basename(appPath)}' took ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
|
|
282
282
|
const truncatedOutput = (!lodash_1.default.isString(output) || output.length <= 300) ?
|
|
283
283
|
output : `${output.substring(0, 150)}...${output.substring(output.length - 150)}`;
|
|
@@ -292,10 +292,11 @@ async function install(appPath, options = {}) {
|
|
|
292
292
|
}
|
|
293
293
|
}
|
|
294
294
|
catch (err) {
|
|
295
|
+
const error = err;
|
|
295
296
|
// on some systems this will throw an error if the app already
|
|
296
297
|
// exists
|
|
297
|
-
if (!
|
|
298
|
-
throw
|
|
298
|
+
if (!error.message.includes('INSTALL_FAILED_ALREADY_EXISTS')) {
|
|
299
|
+
throw error;
|
|
299
300
|
}
|
|
300
301
|
logger_js_1.log.debug(`Application '${appPath}' already installed. Continuing.`);
|
|
301
302
|
}
|
|
@@ -303,18 +304,16 @@ async function install(appPath, options = {}) {
|
|
|
303
304
|
/**
|
|
304
305
|
* Retrieves the current installation state of the particular application
|
|
305
306
|
*
|
|
306
|
-
* @
|
|
307
|
-
* @param
|
|
308
|
-
* @param {string?} [pkg=null] - Package identifier. If omitted then the script will
|
|
307
|
+
* @param appPath - Full path to the application
|
|
308
|
+
* @param pkg - Package identifier. If omitted then the script will
|
|
309
309
|
* try to extract it on its own
|
|
310
|
-
* @returns
|
|
310
|
+
* @returns One of `APP_INSTALL_STATE` constants
|
|
311
311
|
*/
|
|
312
312
|
async function getApplicationInstallState(appPath, pkg = null) {
|
|
313
313
|
let apkInfo = null;
|
|
314
314
|
if (!pkg) {
|
|
315
|
-
apkInfo = await this.getApkInfo(appPath);
|
|
316
|
-
|
|
317
|
-
pkg = apkInfo.name;
|
|
315
|
+
apkInfo = (await this.getApkInfo(appPath));
|
|
316
|
+
pkg = apkInfo?.name;
|
|
318
317
|
}
|
|
319
318
|
if (!pkg) {
|
|
320
319
|
logger_js_1.log.warn(`Cannot read the package name of '${appPath}'`);
|
|
@@ -327,10 +326,10 @@ async function getApplicationInstallState(appPath, pkg = null) {
|
|
|
327
326
|
}
|
|
328
327
|
const pkgVersionName = semver.valid(semver.coerce(pkgVersionNameStr));
|
|
329
328
|
if (!apkInfo) {
|
|
330
|
-
apkInfo = await this.getApkInfo(appPath);
|
|
329
|
+
apkInfo = (await this.getApkInfo(appPath));
|
|
331
330
|
}
|
|
332
331
|
// @ts-ignore We validate the values below
|
|
333
|
-
const { versionCode: apkVersionCode, versionName: apkVersionNameStr } = apkInfo;
|
|
332
|
+
const { versionCode: apkVersionCode, versionName: apkVersionNameStr } = apkInfo || {};
|
|
334
333
|
const apkVersionName = semver.valid(semver.coerce(apkVersionNameStr));
|
|
335
334
|
if (!lodash_1.default.isInteger(apkVersionCode) || !lodash_1.default.isInteger(pkgVersionCode)) {
|
|
336
335
|
logger_js_1.log.warn(`Cannot read version codes of '${appPath}' and/or '${pkg}'`);
|
|
@@ -340,7 +339,7 @@ async function getApplicationInstallState(appPath, pkg = null) {
|
|
|
340
339
|
}
|
|
341
340
|
}
|
|
342
341
|
if (lodash_1.default.isInteger(apkVersionCode) && lodash_1.default.isInteger(pkgVersionCode)) {
|
|
343
|
-
if (
|
|
342
|
+
if (pkgVersionCode > apkVersionCode) {
|
|
344
343
|
logger_js_1.log.debug(`The version code of the installed '${pkg}' is greater than the application version code (${pkgVersionCode} > ${apkVersionCode})`);
|
|
345
344
|
return this.APP_INSTALL_STATE.NEWER_VERSION_INSTALLED;
|
|
346
345
|
}
|
|
@@ -371,13 +370,11 @@ async function getApplicationInstallState(appPath, pkg = null) {
|
|
|
371
370
|
* Install the package from the local file system or upgrade it if an older
|
|
372
371
|
* version of the same package is already installed.
|
|
373
372
|
*
|
|
374
|
-
* @
|
|
375
|
-
* @param
|
|
376
|
-
* @param {string?} [pkg=null] - The name of the installed package. The method will
|
|
373
|
+
* @param appPath - The full path to the local package.
|
|
374
|
+
* @param pkg - The name of the installed package. The method will
|
|
377
375
|
* perform faster if it is set.
|
|
378
|
-
* @param
|
|
379
|
-
* @throws
|
|
380
|
-
* @returns {Promise<import('./types').InstallOrUpgradeResult>}
|
|
376
|
+
* @param options - Set of install options.
|
|
377
|
+
* @throws If an unexpected error happens during install.
|
|
381
378
|
*/
|
|
382
379
|
async function installOrUpgrade(appPath, pkg = null, options = {}) {
|
|
383
380
|
if (!pkg) {
|
|
@@ -394,7 +391,7 @@ async function installOrUpgrade(appPath, pkg = null, options = {}) {
|
|
|
394
391
|
const appState = await this.getApplicationInstallState(appPath, pkg);
|
|
395
392
|
let wasUninstalled = false;
|
|
396
393
|
const uninstallPackage = async () => {
|
|
397
|
-
if (!await this.uninstallApk(
|
|
394
|
+
if (!await this.uninstallApk(pkg, { skipInstallCheck: true })) {
|
|
398
395
|
throw new Error(`'${pkg}' package cannot be uninstalled`);
|
|
399
396
|
}
|
|
400
397
|
wasUninstalled = true;
|
|
@@ -438,7 +435,8 @@ async function installOrUpgrade(appPath, pkg = null, options = {}) {
|
|
|
438
435
|
await this.install(appPath, { ...options, replace: true });
|
|
439
436
|
}
|
|
440
437
|
catch (err) {
|
|
441
|
-
|
|
438
|
+
const error = err;
|
|
439
|
+
logger_js_1.log.warn(`Cannot install/upgrade '${pkg}' because of '${error.message}'. Trying full reinstall`);
|
|
442
440
|
await uninstallPackage();
|
|
443
441
|
await this.install(appPath, { ...options, replace: false });
|
|
444
442
|
}
|
|
@@ -450,14 +448,12 @@ async function installOrUpgrade(appPath, pkg = null, options = {}) {
|
|
|
450
448
|
/**
|
|
451
449
|
* Extract string resources from the given package on local file system.
|
|
452
450
|
*
|
|
453
|
-
* @
|
|
454
|
-
* @param
|
|
455
|
-
* @param {string?} [language=null] - The name of the language to extract the resources for.
|
|
451
|
+
* @param appPath - The full path to the .apk(s) package.
|
|
452
|
+
* @param language - The name of the language to extract the resources for.
|
|
456
453
|
* The default language is used if this equals to `null`
|
|
457
|
-
* @param
|
|
454
|
+
* @param outRoot - The name of the destination folder on the local file system to
|
|
458
455
|
* store the extracted file to. If not provided then the `localPath` property in the returned object
|
|
459
456
|
* will be undefined.
|
|
460
|
-
* @return {Promise<import('./types').ApkStrings>}
|
|
461
457
|
*/
|
|
462
458
|
async function extractStringsFromApk(appPath, language = null, outRoot = null) {
|
|
463
459
|
logger_js_1.log.debug(`Extracting strings from for language: ${language || 'default'}`);
|
|
@@ -470,35 +466,37 @@ async function extractStringsFromApk(appPath, language = null, outRoot = null) {
|
|
|
470
466
|
try {
|
|
471
467
|
await this.initAapt();
|
|
472
468
|
configMarker = await formatConfigMarker(async () => {
|
|
473
|
-
const { stdout } = await (0, teen_process_1.exec)(
|
|
469
|
+
const { stdout } = await (0, teen_process_1.exec)(this.binaries.aapt, [
|
|
474
470
|
'd', 'configurations', appPath,
|
|
475
471
|
]);
|
|
476
472
|
return lodash_1.default.uniq(stdout.split(os_1.default.EOL));
|
|
477
473
|
}, language, '(default)');
|
|
478
|
-
const { stdout } = await (0, teen_process_1.exec)(
|
|
474
|
+
const { stdout } = await (0, teen_process_1.exec)(this.binaries.aapt, [
|
|
479
475
|
'd', '--values', 'resources', appPath,
|
|
480
476
|
]);
|
|
481
477
|
apkStrings = parseAaptStrings(stdout, configMarker);
|
|
482
478
|
}
|
|
483
479
|
catch (e) {
|
|
480
|
+
const err = e;
|
|
484
481
|
logger_js_1.log.debug('Cannot extract resources using aapt. Trying aapt2. ' +
|
|
485
|
-
`Original error: ${
|
|
482
|
+
`Original error: ${err.stderr || err.message}`);
|
|
486
483
|
await this.initAapt2();
|
|
487
484
|
configMarker = await formatConfigMarker(async () => {
|
|
488
|
-
const { stdout } = await (0, teen_process_1.exec)(
|
|
485
|
+
const { stdout } = await (0, teen_process_1.exec)(this.binaries.aapt2, [
|
|
489
486
|
'd', 'configurations', appPath,
|
|
490
487
|
]);
|
|
491
488
|
return lodash_1.default.uniq(stdout.split(os_1.default.EOL));
|
|
492
489
|
}, language, '');
|
|
493
490
|
try {
|
|
494
|
-
const { stdout } = await (0, teen_process_1.exec)(
|
|
491
|
+
const { stdout } = await (0, teen_process_1.exec)(this.binaries.aapt2, [
|
|
495
492
|
'd', 'resources', appPath,
|
|
496
493
|
]);
|
|
497
494
|
apkStrings = parseAapt2Strings(stdout, configMarker);
|
|
498
495
|
}
|
|
499
496
|
catch (e) {
|
|
497
|
+
const error = e;
|
|
500
498
|
throw new Error(`Cannot extract resources from '${originalAppPath}'. ` +
|
|
501
|
-
`Original error: ${
|
|
499
|
+
`Original error: ${error.message}`);
|
|
502
500
|
}
|
|
503
501
|
}
|
|
504
502
|
if (lodash_1.default.isEmpty(apkStrings)) {
|
|
@@ -520,10 +518,9 @@ async function extractStringsFromApk(appPath, language = null, outRoot = null) {
|
|
|
520
518
|
/**
|
|
521
519
|
* Get the package info from local apk file.
|
|
522
520
|
*
|
|
523
|
-
* @
|
|
524
|
-
* @param {string} appPath - The full path to existing .apk(s) package on the local
|
|
521
|
+
* @param appPath - The full path to existing .apk(s) package on the local
|
|
525
522
|
* file system.
|
|
526
|
-
* @
|
|
523
|
+
* @returns The parsed application information.
|
|
527
524
|
*/
|
|
528
525
|
async function getApkInfo(appPath) {
|
|
529
526
|
if (!await support_1.fs.exists(appPath)) {
|
|
@@ -541,7 +538,8 @@ async function getApkInfo(appPath) {
|
|
|
541
538
|
};
|
|
542
539
|
}
|
|
543
540
|
catch (e) {
|
|
544
|
-
|
|
541
|
+
const err = e;
|
|
542
|
+
logger_js_1.log.warn(`Error '${err.message}' while getting badging info`);
|
|
545
543
|
}
|
|
546
544
|
return {};
|
|
547
545
|
}
|
|
@@ -550,11 +548,11 @@ async function getApkInfo(appPath) {
|
|
|
550
548
|
* Formats the config marker, which is then passed to parse.. methods
|
|
551
549
|
* to make it compatible with resource formats generated by aapt(2) tool
|
|
552
550
|
*
|
|
553
|
-
* @param
|
|
551
|
+
* @param configsGetter The function whose result is a list
|
|
554
552
|
* of apk configs
|
|
555
|
-
* @param
|
|
556
|
-
* @param
|
|
557
|
-
* @
|
|
553
|
+
* @param desiredMarker The desired config marker value
|
|
554
|
+
* @param defaultMarker The default config marker value
|
|
555
|
+
* @returns The formatted config marker
|
|
558
556
|
*/
|
|
559
557
|
async function formatConfigMarker(configsGetter, desiredMarker, defaultMarker) {
|
|
560
558
|
let configMarker = desiredMarker || defaultMarker;
|
|
@@ -578,10 +576,10 @@ async function formatConfigMarker(configsGetter, desiredMarker, defaultMarker) {
|
|
|
578
576
|
/**
|
|
579
577
|
* Parses apk strings from aapt2 tool output
|
|
580
578
|
*
|
|
581
|
-
* @param
|
|
582
|
-
* @param
|
|
579
|
+
* @param rawOutput The actual tool output
|
|
580
|
+
* @param configMarker The config marker. Usually
|
|
583
581
|
* a language abbreviation or an empty string for the default one
|
|
584
|
-
* @returns
|
|
582
|
+
* @returns Strings ids to values mapping. Plural
|
|
585
583
|
* values are represented as arrays. If no config found for the
|
|
586
584
|
* given marker then an empty mapping is returned.
|
|
587
585
|
*/
|
|
@@ -669,7 +667,7 @@ function parseAapt2Strings(rawOutput, configMarker) {
|
|
|
669
667
|
lineIndex = idx;
|
|
670
668
|
if (lodash_1.default.isString(content)) {
|
|
671
669
|
apkStrings[currentResourceId] = [
|
|
672
|
-
...(apkStrings[currentResourceId]
|
|
670
|
+
...(Array.isArray(apkStrings[currentResourceId]) ? apkStrings[currentResourceId] : []),
|
|
673
671
|
content,
|
|
674
672
|
];
|
|
675
673
|
}
|
|
@@ -691,10 +689,10 @@ function parseAapt2Strings(rawOutput, configMarker) {
|
|
|
691
689
|
/**
|
|
692
690
|
* Parses apk strings from aapt tool output
|
|
693
691
|
*
|
|
694
|
-
* @param
|
|
695
|
-
* @param
|
|
692
|
+
* @param rawOutput The actual tool output
|
|
693
|
+
* @param configMarker The config marker. Usually
|
|
696
694
|
* a language abbreviation or `(default)`
|
|
697
|
-
* @returns
|
|
695
|
+
* @returns Strings ids to values mapping. Plural
|
|
698
696
|
* values are represented as arrays. If no config found for the
|
|
699
697
|
* given marker then an empty mapping is returned.
|
|
700
698
|
*/
|
|
@@ -752,7 +750,7 @@ function parseAaptStrings(rawOutput, configMarker) {
|
|
|
752
750
|
const match = quotedStringPattern.exec(trimmedLine);
|
|
753
751
|
if (match) {
|
|
754
752
|
apkStrings[currentResourceId] = [
|
|
755
|
-
...(apkStrings[currentResourceId]
|
|
753
|
+
...(Array.isArray(apkStrings[currentResourceId]) ? apkStrings[currentResourceId] : []),
|
|
756
754
|
normalizeStringMatch(match[0]),
|
|
757
755
|
];
|
|
758
756
|
}
|