io.appium.settings 5.5.0 → 5.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/CHANGELOG.md +7 -0
- package/apks/settings_apk-debug.apk +0 -0
- package/build/lib/client.d.ts +4 -0
- package/build/lib/client.d.ts.map +1 -1
- package/build/lib/client.js +3 -0
- package/build/lib/client.js.map +1 -1
- package/build/lib/commands/media-projection.d.ts +91 -0
- package/build/lib/commands/media-projection.d.ts.map +1 -0
- package/build/lib/commands/media-projection.js +165 -0
- package/build/lib/commands/media-projection.js.map +1 -0
- package/build/lib/constants.d.ts +4 -0
- package/build/lib/constants.d.ts.map +1 -1
- package/build/lib/constants.js +5 -1
- package/build/lib/constants.js.map +1 -1
- package/lib/client.js +7 -0
- package/lib/commands/media-projection.js +181 -0
- package/lib/constants.js +5 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [5.6.0](https://github.com/appium/io.appium.settings/compare/v5.5.0...v5.6.0) (2024-01-10)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* Add media-projection-based video recorder ([#138](https://github.com/appium/io.appium.settings/issues/138)) ([ba357e8](https://github.com/appium/io.appium.settings/commit/ba357e8541e2f86e9db902ace4850fd04fb3ec57))
|
|
7
|
+
|
|
1
8
|
## [5.5.0](https://github.com/appium/io.appium.settings/compare/v5.4.2...v5.5.0) (2024-01-10)
|
|
2
9
|
|
|
3
10
|
|
|
Binary file
|
package/build/lib/client.d.ts
CHANGED
|
@@ -73,6 +73,8 @@ export class SettingsApp {
|
|
|
73
73
|
getSmsList: typeof getSmsList;
|
|
74
74
|
performEditorAction: typeof performEditorAction;
|
|
75
75
|
typeUnicode: typeof typeUnicode;
|
|
76
|
+
makeMediaProjectionRecorder: typeof makeMediaProjectionRecorder;
|
|
77
|
+
adjustMediaProjectionServicePermissions: typeof adjustMediaProjectionServicePermissions;
|
|
76
78
|
}
|
|
77
79
|
export type SettingsAppOpts = {
|
|
78
80
|
adb: import('appium-adb').ADB;
|
|
@@ -91,4 +93,6 @@ import { adjustNotificationsPermissions } from './commands/notifications';
|
|
|
91
93
|
import { getSmsList } from './commands/sms';
|
|
92
94
|
import { performEditorAction } from './commands/typing';
|
|
93
95
|
import { typeUnicode } from './commands/typing';
|
|
96
|
+
import { makeMediaProjectionRecorder } from './commands/media-projection';
|
|
97
|
+
import { adjustMediaProjectionServicePermissions } from './commands/media-projection';
|
|
94
98
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../lib/client.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../lib/client.js"],"names":[],"mappings":"AAkBA;;;GAGG;AAGH;IAOE;;OAEG;IACH,kBAFW,eAAe,EAKzB;IAZD,uCAAuC;IACvC,KADW,OAAO,YAAY,EAAE,GAAG,CAC/B;IAEJ,sCAAsC;IACtC,KADW,OAAO,QAAQ,EAAE,MAAM,CAC9B;IAUJ;;;;;;OAMG;IAEH;;;;;;;OAOG;IACH;;;;;;;;;;;oBAFa,QAAQ,WAAW,CAAC,CA4ChC;IAED;;;;;;;OAOG;IACH,yBAFa,QAAQ,OAAO,CAAC,CAY5B;IAED;;;;;;;;;;OAUG;IACH,uBAPW,MAAM,cACN,MAAM,OAgChB;IAED,4CAAsC;IAEtC,kCAA4B;IAE5B,sCAAgC;IAChC,sCAAgC;IAChC,wDAAkD;IAElD,wCAAkC;IAElC,4BAAsB;IAEtB,kCAA4B;IAC5B,kCAA4B;IAE5B,0CAAoC;IACpC,sEAAgE;IAChE,8BAAwB;IAExB,gDAA0C;IAC1C,gCAA0B;IAE1B,gEAA0D;IAC1D,wFAAkF;CACnF;;SAlKa,OAAO,YAAY,EAAE,GAAG;;kCAhBJ,sBAAsB;6BAC3B,sBAAsB;+BACqB,wBAAwB;+BAAxB,wBAAwB;wCAAxB,wBAAwB;gCAChE,mBAAmB;0BACzB,kBAAkB;6BACD,oBAAoB;6BAApB,oBAAoB;iCACE,0BAA0B;+CAA1B,0BAA0B;2BAChE,gBAAgB;oCACM,mBAAmB;4BAAnB,mBAAmB;4CAI7D,6BAA6B;wDAA7B,6BAA6B"}
|
package/build/lib/client.js
CHANGED
|
@@ -17,6 +17,7 @@ const network_1 = require("./commands/network");
|
|
|
17
17
|
const notifications_1 = require("./commands/notifications");
|
|
18
18
|
const sms_1 = require("./commands/sms");
|
|
19
19
|
const typing_1 = require("./commands/typing");
|
|
20
|
+
const media_projection_1 = require("./commands/media-projection");
|
|
20
21
|
/**
|
|
21
22
|
* @typedef {Object} SettingsAppOpts
|
|
22
23
|
* @property {import('appium-adb').ADB} adb
|
|
@@ -40,6 +41,8 @@ class SettingsApp {
|
|
|
40
41
|
this.getSmsList = sms_1.getSmsList;
|
|
41
42
|
this.performEditorAction = typing_1.performEditorAction;
|
|
42
43
|
this.typeUnicode = typing_1.typeUnicode;
|
|
44
|
+
this.makeMediaProjectionRecorder = media_projection_1.makeMediaProjectionRecorder;
|
|
45
|
+
this.adjustMediaProjectionServicePermissions = media_projection_1.adjustMediaProjectionServicePermissions;
|
|
43
46
|
this.adb = opts.adb;
|
|
44
47
|
this.log = logger_1.log;
|
|
45
48
|
}
|
package/build/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../lib/client.js"],"names":[],"mappings":";;;;;;AAAA,qCAA2C;AAC3C,oDAAuB;AACvB,uCAA4C;AAC5C,iDAAmF;AACnF,oDAAyD;AACzD,oDAAoD;AACpD,wDAAiG;AACjG,8CAAoD;AACpD,4CAA6C;AAC7C,gDAAgE;AAChE,4DAA4F;AAC5F,wCAA4C;AAC5C,8CAAqE;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../lib/client.js"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a new instance of the MediaProjection-based recorder
|
|
3
|
+
* The recorder only works since Android API 29+
|
|
4
|
+
*
|
|
5
|
+
* @this {import('../client').SettingsApp}
|
|
6
|
+
* @returns {MediaProjectionRecorder} The recorder instance
|
|
7
|
+
*/
|
|
8
|
+
export function makeMediaProjectionRecorder(this: import("../client").SettingsApp): MediaProjectionRecorder;
|
|
9
|
+
/**
|
|
10
|
+
* Adjusts the necessary permissions for the
|
|
11
|
+
* Media Projection-based recording service
|
|
12
|
+
*
|
|
13
|
+
* @this {import('../client').SettingsApp}
|
|
14
|
+
* @returns {Promise<boolean>} If the permssions adjustment has actually been made
|
|
15
|
+
*/
|
|
16
|
+
export function adjustMediaProjectionServicePermissions(this: import("../client").SettingsApp): Promise<boolean>;
|
|
17
|
+
export type StartMediaProjectionRecordingOpts = {
|
|
18
|
+
/**
|
|
19
|
+
* Maximum supported resolution on-device (Detected automatically by the app
|
|
20
|
+
* itself), which usually equals to Full HD 1920x1080 on most phones however
|
|
21
|
+
* you can change it to following supported resolutions as well: "1920x1080",
|
|
22
|
+
* "1280x720", "720x480", "320x240", "176x144".
|
|
23
|
+
*/
|
|
24
|
+
resolution?: string | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Maximum allowed duration is 15 minutes; you can increase it if your test
|
|
27
|
+
* takes longer than that.
|
|
28
|
+
*/
|
|
29
|
+
maxDurationSec?: number | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Recording thread priority.
|
|
32
|
+
* If you face performance drops during testing with recording enabled, you
|
|
33
|
+
* can reduce recording priority
|
|
34
|
+
*/
|
|
35
|
+
priority?: "high" | "normal" | "low" | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* You can type recording video file name as you want, but recording currently
|
|
38
|
+
* supports only "mp4" format so your filename must end with ".mp4". An
|
|
39
|
+
* invalid file name will fail to start the recording.
|
|
40
|
+
*/
|
|
41
|
+
filename?: string | undefined;
|
|
42
|
+
};
|
|
43
|
+
export type ADB = import('appium-adb').ADB;
|
|
44
|
+
/**
|
|
45
|
+
* @typedef {Object} StartMediaProjectionRecordingOpts
|
|
46
|
+
* @property {string} [resolution] Maximum supported resolution on-device (Detected automatically by the app
|
|
47
|
+
* itself), which usually equals to Full HD 1920x1080 on most phones however
|
|
48
|
+
* you can change it to following supported resolutions as well: "1920x1080",
|
|
49
|
+
* "1280x720", "720x480", "320x240", "176x144".
|
|
50
|
+
* @property {number} [maxDurationSec=900] Maximum allowed duration is 15 minutes; you can increase it if your test
|
|
51
|
+
* takes longer than that.
|
|
52
|
+
* @property {'high' | 'normal' | 'low'} [priority='high'] Recording thread priority.
|
|
53
|
+
* If you face performance drops during testing with recording enabled, you
|
|
54
|
+
* can reduce recording priority
|
|
55
|
+
* @property {string} [filename] You can type recording video file name as you want, but recording currently
|
|
56
|
+
* supports only "mp4" format so your filename must end with ".mp4". An
|
|
57
|
+
* invalid file name will fail to start the recording.
|
|
58
|
+
*/
|
|
59
|
+
declare class MediaProjectionRecorder {
|
|
60
|
+
/**
|
|
61
|
+
* @param {ADB} adb
|
|
62
|
+
*/
|
|
63
|
+
constructor(adb: ADB);
|
|
64
|
+
adb: import("appium-adb").default;
|
|
65
|
+
/**
|
|
66
|
+
*
|
|
67
|
+
* @returns {Promise<boolean>}
|
|
68
|
+
*/
|
|
69
|
+
isRunning(): Promise<boolean>;
|
|
70
|
+
/**
|
|
71
|
+
*
|
|
72
|
+
* @param {StartMediaProjectionRecordingOpts} opts
|
|
73
|
+
* @returns {Promise<boolean>}
|
|
74
|
+
*/
|
|
75
|
+
start(opts?: StartMediaProjectionRecordingOpts): Promise<boolean>;
|
|
76
|
+
/**
|
|
77
|
+
* @returns {Promise<void>}
|
|
78
|
+
*/
|
|
79
|
+
cleanup(): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
*
|
|
82
|
+
* @returns {Promise<string?>}
|
|
83
|
+
*/
|
|
84
|
+
pullRecent(): Promise<string | null>;
|
|
85
|
+
/**
|
|
86
|
+
* @returns {Promise<boolean>}
|
|
87
|
+
*/
|
|
88
|
+
stop(): Promise<boolean>;
|
|
89
|
+
}
|
|
90
|
+
export {};
|
|
91
|
+
//# sourceMappingURL=media-projection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-projection.d.ts","sourceRoot":"","sources":["../../../lib/commands/media-projection.js"],"names":[],"mappings":"AAwJA;;;;;;GAMG;AACH,oFAFa,uBAAuB,CAInC;AAED;;;;;;GAMG;AACH,gGAFa,QAAQ,OAAO,CAAC,CAQ5B;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAGY,OAAO,YAAY,EAAE,GAAG;AAlKrC;;;;;;;;;;;;;;GAcG;AAEH;IACE;;OAEG;IACH,iBAFW,GAAG,EAIb;IADC,kCAAc;IAGhB;;;OAGG;IACH,aAFa,QAAQ,OAAO,CAAC,CAU5B;IAED;;;;OAIG;IACH,aAHW,iCAAiC,GAC/B,QAAQ,OAAO,CAAC,CAqC5B;IAED;;OAEG;IACH,WAFa,QAAQ,IAAI,CAAC,CAIzB;IAED;;;OAGG;IACH,cAFa,QAAQ,MAAM,QAAE,CAa5B;IAED;;OAEG;IACH,QAFa,QAAQ,OAAO,CAAC,CA2B5B;CACF"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.adjustMediaProjectionServicePermissions = exports.makeMediaProjectionRecorder = void 0;
|
|
7
|
+
const asyncbox_1 = require("asyncbox");
|
|
8
|
+
const bluebird_1 = __importDefault(require("bluebird"));
|
|
9
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
10
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
11
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
+
const constants_1 = require("../constants");
|
|
13
|
+
const RECORDING_STARTUP_TIMEOUT_MS = 3 * 1000;
|
|
14
|
+
const RECORDING_STOP_TIMEOUT_MS = 3 * 1000;
|
|
15
|
+
const RECORDINGS_ROOT = `/storage/emulated/0/Android/data/${constants_1.SETTINGS_HELPER_ID}/files`;
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {Object} StartMediaProjectionRecordingOpts
|
|
18
|
+
* @property {string} [resolution] Maximum supported resolution on-device (Detected automatically by the app
|
|
19
|
+
* itself), which usually equals to Full HD 1920x1080 on most phones however
|
|
20
|
+
* you can change it to following supported resolutions as well: "1920x1080",
|
|
21
|
+
* "1280x720", "720x480", "320x240", "176x144".
|
|
22
|
+
* @property {number} [maxDurationSec=900] Maximum allowed duration is 15 minutes; you can increase it if your test
|
|
23
|
+
* takes longer than that.
|
|
24
|
+
* @property {'high' | 'normal' | 'low'} [priority='high'] Recording thread priority.
|
|
25
|
+
* If you face performance drops during testing with recording enabled, you
|
|
26
|
+
* can reduce recording priority
|
|
27
|
+
* @property {string} [filename] You can type recording video file name as you want, but recording currently
|
|
28
|
+
* supports only "mp4" format so your filename must end with ".mp4". An
|
|
29
|
+
* invalid file name will fail to start the recording.
|
|
30
|
+
*/
|
|
31
|
+
class MediaProjectionRecorder {
|
|
32
|
+
/**
|
|
33
|
+
* @param {ADB} adb
|
|
34
|
+
*/
|
|
35
|
+
constructor(adb) {
|
|
36
|
+
this.adb = adb;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
*
|
|
40
|
+
* @returns {Promise<boolean>}
|
|
41
|
+
*/
|
|
42
|
+
async isRunning() {
|
|
43
|
+
const stdout = await this.adb.shell([
|
|
44
|
+
'dumpsys',
|
|
45
|
+
'activity',
|
|
46
|
+
'services',
|
|
47
|
+
constants_1.RECORDING_SERVICE_NAME,
|
|
48
|
+
]);
|
|
49
|
+
return stdout.includes(constants_1.RECORDING_SERVICE_NAME);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
*
|
|
53
|
+
* @param {StartMediaProjectionRecordingOpts} opts
|
|
54
|
+
* @returns {Promise<boolean>}
|
|
55
|
+
*/
|
|
56
|
+
async start(opts = {}) {
|
|
57
|
+
if (await this.isRunning()) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
await this.cleanup();
|
|
61
|
+
const { filename, maxDurationSec, priority, resolution } = opts;
|
|
62
|
+
const args = ['am', 'start', '-n', constants_1.RECORDING_ACTIVITY_NAME, '-a', constants_1.RECORDING_ACTION_START];
|
|
63
|
+
if (filename) {
|
|
64
|
+
args.push('--es', 'filename', filename);
|
|
65
|
+
}
|
|
66
|
+
if (maxDurationSec) {
|
|
67
|
+
args.push('--es', 'max_duration_sec', `${maxDurationSec}`);
|
|
68
|
+
}
|
|
69
|
+
if (priority) {
|
|
70
|
+
args.push('--es', 'priority', priority);
|
|
71
|
+
}
|
|
72
|
+
if (resolution) {
|
|
73
|
+
args.push('--es', 'resolution', resolution);
|
|
74
|
+
}
|
|
75
|
+
await this.adb.shell(args);
|
|
76
|
+
await new bluebird_1.default((resolve, reject) => {
|
|
77
|
+
setTimeout(async () => {
|
|
78
|
+
if (!(await this.isRunning())) {
|
|
79
|
+
return reject(new Error(`The media projection recording is not running after ${RECORDING_STARTUP_TIMEOUT_MS}ms. ` +
|
|
80
|
+
`Please check the logcat output for more details.`));
|
|
81
|
+
}
|
|
82
|
+
resolve();
|
|
83
|
+
}, RECORDING_STARTUP_TIMEOUT_MS);
|
|
84
|
+
});
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* @returns {Promise<void>}
|
|
89
|
+
*/
|
|
90
|
+
async cleanup() {
|
|
91
|
+
await this.adb.shell([`rm -f ${RECORDINGS_ROOT}/*`]);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
*
|
|
95
|
+
* @returns {Promise<string?>}
|
|
96
|
+
*/
|
|
97
|
+
async pullRecent() {
|
|
98
|
+
const recordings = await this.adb.ls(RECORDINGS_ROOT, ['-tr']);
|
|
99
|
+
if (lodash_1.default.isEmpty(recordings)) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
const tmpRoot = await promises_1.default.mkdtemp('recording');
|
|
103
|
+
const dstPath = node_path_1.default.join(tmpRoot, recordings[0]);
|
|
104
|
+
// increase timeout to 5 minutes because it might take a while to pull a large video file
|
|
105
|
+
await this.adb.pull(`${RECORDINGS_ROOT}/${recordings[0]}`, dstPath, { timeout: 300000 });
|
|
106
|
+
return dstPath;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* @returns {Promise<boolean>}
|
|
110
|
+
*/
|
|
111
|
+
async stop() {
|
|
112
|
+
if (!(await this.isRunning())) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
await this.adb.shell([
|
|
116
|
+
'am',
|
|
117
|
+
'start',
|
|
118
|
+
'-n',
|
|
119
|
+
constants_1.RECORDING_ACTIVITY_NAME,
|
|
120
|
+
'-a',
|
|
121
|
+
constants_1.RECORDING_ACTION_STOP,
|
|
122
|
+
]);
|
|
123
|
+
try {
|
|
124
|
+
await (0, asyncbox_1.waitForCondition)(async () => !(await this.isRunning()), {
|
|
125
|
+
waitMs: RECORDING_STOP_TIMEOUT_MS,
|
|
126
|
+
intervalMs: 500,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
catch (e) {
|
|
130
|
+
throw new Error(`The attempt to stop the current media projection recording timed out after ` +
|
|
131
|
+
`${RECORDING_STOP_TIMEOUT_MS}ms`);
|
|
132
|
+
}
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Creates a new instance of the MediaProjection-based recorder
|
|
138
|
+
* The recorder only works since Android API 29+
|
|
139
|
+
*
|
|
140
|
+
* @this {import('../client').SettingsApp}
|
|
141
|
+
* @returns {MediaProjectionRecorder} The recorder instance
|
|
142
|
+
*/
|
|
143
|
+
function makeMediaProjectionRecorder() {
|
|
144
|
+
return new MediaProjectionRecorder(this.adb);
|
|
145
|
+
}
|
|
146
|
+
exports.makeMediaProjectionRecorder = makeMediaProjectionRecorder;
|
|
147
|
+
/**
|
|
148
|
+
* Adjusts the necessary permissions for the
|
|
149
|
+
* Media Projection-based recording service
|
|
150
|
+
*
|
|
151
|
+
* @this {import('../client').SettingsApp}
|
|
152
|
+
* @returns {Promise<boolean>} If the permssions adjustment has actually been made
|
|
153
|
+
*/
|
|
154
|
+
async function adjustMediaProjectionServicePermissions() {
|
|
155
|
+
if (await this.adb.getApiLevel() >= 29) {
|
|
156
|
+
await this.adb.shell(['appops', 'set', constants_1.SETTINGS_HELPER_ID, 'PROJECT_MEDIA', 'allow']);
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
exports.adjustMediaProjectionServicePermissions = adjustMediaProjectionServicePermissions;
|
|
162
|
+
/**
|
|
163
|
+
* @typedef {import('appium-adb').ADB} ADB
|
|
164
|
+
*/
|
|
165
|
+
//# sourceMappingURL=media-projection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-projection.js","sourceRoot":"","sources":["../../../lib/commands/media-projection.js"],"names":[],"mappings":"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"}
|
package/build/lib/constants.d.ts
CHANGED
|
@@ -22,4 +22,8 @@ export const MEDIA_SCAN_RECEIVER: "io.appium.settings/.receivers.MediaScannerRec
|
|
|
22
22
|
export const MEDIA_SCAN_ACTION: "io.appium.settings.scan_media";
|
|
23
23
|
export const SETTING_NOTIFICATIONS_LISTENER_SERVICE: "io.appium.settings/.NLService";
|
|
24
24
|
export const NOTIFICATIONS_RETRIEVAL_ACTION: "io.appium.settings.notifications";
|
|
25
|
+
export const RECORDING_SERVICE_NAME: "io.appium.settings/.recorder.RecorderService";
|
|
26
|
+
export const RECORDING_ACTIVITY_NAME: "io.appium.settings/io.appium.settings.Settings";
|
|
27
|
+
export const RECORDING_ACTION_START: "io.appium.settings.recording.ACTION_START";
|
|
28
|
+
export const RECORDING_ACTION_STOP: "io.appium.settings.recording.ACTION_STOP";
|
|
25
29
|
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../lib/constants.js"],"names":[],"mappings":"AAAA,sDAAuD;AACvD,wDAAyD;AAEzD,mFAAuF;AACvF,4EAAgF;AAEhF,yDAA6D;AAC7D,2DAA+D;AAC/D,uDAA2D;AAE3D,6GAAiH;AACjH,uEAA2E;AAC3E,6GAAiH;AACjH,kFAAsF;AAEtF,kGAAsG;AACtG,sEAA0E;AAE1E,4FAAgG;AAChG,gEAAoE;AAEpE,qEAAyE;AACzE,qFAAyF;AACzF,sEAA0E;AAE1E,0EAA8E;AAC9E,sEAA0E;AAE1E,uFAA2F;AAC3F,gEAAoE;AAEpE,qFAAyF;AACzF,gFAAoF"}
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../lib/constants.js"],"names":[],"mappings":"AAAA,sDAAuD;AACvD,wDAAyD;AAEzD,mFAAuF;AACvF,4EAAgF;AAEhF,yDAA6D;AAC7D,2DAA+D;AAC/D,uDAA2D;AAE3D,6GAAiH;AACjH,uEAA2E;AAC3E,6GAAiH;AACjH,kFAAsF;AAEtF,kGAAsG;AACtG,sEAA0E;AAE1E,4FAAgG;AAChG,gEAAoE;AAEpE,qEAAyE;AACzE,qFAAyF;AACzF,sEAA0E;AAE1E,0EAA8E;AAC9E,sEAA0E;AAE1E,uFAA2F;AAC3F,gEAAoE;AAEpE,qFAAyF;AACzF,gFAAoF;AAEpF,oFAAwF;AACxF,uFAA2F;AAC3F,iFAAqF;AACrF,+EAAmF"}
|
package/build/lib/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.NOTIFICATIONS_RETRIEVAL_ACTION = exports.SETTING_NOTIFICATIONS_LISTENER_SERVICE = exports.MEDIA_SCAN_ACTION = exports.MEDIA_SCAN_RECEIVER = exports.SMS_LIST_RETRIEVAL_ACTION = exports.SMS_LIST_RECEIVER = exports.LOCATION_RETRIEVAL_ACTION = exports.LOCATION_RECEIVER = exports.LOCATION_SERVICE = exports.LOCALE_SETTING_ACTION = exports.LOCALE_SETTING_RECEIVER = exports.ANIMATION_SETTING_ACTION = exports.ANIMATION_SETTING_RECEIVER = exports.DATA_CONNECTION_SETTING_ACTION = exports.DATA_CONNECTION_SETTING_RECEIVER = exports.WIFI_CONNECTION_SETTING_ACTION = exports.WIFI_CONNECTION_SETTING_RECEIVER = exports.EMPTY_IME = exports.UNICODE_IME = exports.APPIUM_IME = exports.CLIPBOARD_RETRIEVAL_ACTION = exports.CLIPBOARD_RECEIVER = exports.SETTINGS_HELPER_MAIN_ACTIVITY = exports.SETTINGS_HELPER_ID = void 0;
|
|
3
|
+
exports.RECORDING_ACTION_STOP = exports.RECORDING_ACTION_START = exports.RECORDING_ACTIVITY_NAME = exports.RECORDING_SERVICE_NAME = exports.NOTIFICATIONS_RETRIEVAL_ACTION = exports.SETTING_NOTIFICATIONS_LISTENER_SERVICE = exports.MEDIA_SCAN_ACTION = exports.MEDIA_SCAN_RECEIVER = exports.SMS_LIST_RETRIEVAL_ACTION = exports.SMS_LIST_RECEIVER = exports.LOCATION_RETRIEVAL_ACTION = exports.LOCATION_RECEIVER = exports.LOCATION_SERVICE = exports.LOCALE_SETTING_ACTION = exports.LOCALE_SETTING_RECEIVER = exports.ANIMATION_SETTING_ACTION = exports.ANIMATION_SETTING_RECEIVER = exports.DATA_CONNECTION_SETTING_ACTION = exports.DATA_CONNECTION_SETTING_RECEIVER = exports.WIFI_CONNECTION_SETTING_ACTION = exports.WIFI_CONNECTION_SETTING_RECEIVER = exports.EMPTY_IME = exports.UNICODE_IME = exports.APPIUM_IME = exports.CLIPBOARD_RETRIEVAL_ACTION = exports.CLIPBOARD_RECEIVER = exports.SETTINGS_HELPER_MAIN_ACTIVITY = exports.SETTINGS_HELPER_ID = void 0;
|
|
4
4
|
exports.SETTINGS_HELPER_ID = 'io.appium.settings';
|
|
5
5
|
exports.SETTINGS_HELPER_MAIN_ACTIVITY = '.Settings';
|
|
6
6
|
exports.CLIPBOARD_RECEIVER = `${exports.SETTINGS_HELPER_ID}/.receivers.ClipboardReceiver`;
|
|
@@ -25,4 +25,8 @@ exports.MEDIA_SCAN_RECEIVER = `${exports.SETTINGS_HELPER_ID}/.receivers.MediaSca
|
|
|
25
25
|
exports.MEDIA_SCAN_ACTION = `${exports.SETTINGS_HELPER_ID}.scan_media`;
|
|
26
26
|
exports.SETTING_NOTIFICATIONS_LISTENER_SERVICE = `${exports.SETTINGS_HELPER_ID}/.NLService`;
|
|
27
27
|
exports.NOTIFICATIONS_RETRIEVAL_ACTION = `${exports.SETTINGS_HELPER_ID}.notifications`;
|
|
28
|
+
exports.RECORDING_SERVICE_NAME = `${exports.SETTINGS_HELPER_ID}/.recorder.RecorderService`;
|
|
29
|
+
exports.RECORDING_ACTIVITY_NAME = `${exports.SETTINGS_HELPER_ID}/io.appium.settings.Settings`;
|
|
30
|
+
exports.RECORDING_ACTION_START = `${exports.SETTINGS_HELPER_ID}.recording.ACTION_START`;
|
|
31
|
+
exports.RECORDING_ACTION_STOP = `${exports.SETTINGS_HELPER_ID}.recording.ACTION_STOP`;
|
|
28
32
|
//# sourceMappingURL=constants.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../lib/constants.js"],"names":[],"mappings":";;;AAAa,QAAA,kBAAkB,GAAG,oBAAoB,CAAC;AAC1C,QAAA,6BAA6B,GAAG,WAAW,CAAC;AAE5C,QAAA,kBAAkB,GAAG,GAAG,0BAAkB,+BAA+B,CAAC;AAC1E,QAAA,0BAA0B,GAAG,GAAG,0BAAkB,gBAAgB,CAAC;AAEnE,QAAA,UAAU,GAAG,GAAG,0BAAkB,aAAa,CAAC;AAChD,QAAA,WAAW,GAAG,GAAG,0BAAkB,cAAc,CAAC;AAClD,QAAA,SAAS,GAAG,GAAG,0BAAkB,YAAY,CAAC;AAE9C,QAAA,gCAAgC,GAAG,GAAG,0BAAkB,2CAA2C,CAAC;AACpG,QAAA,8BAA8B,GAAG,GAAG,0BAAkB,OAAO,CAAC;AAC9D,QAAA,gCAAgC,GAAG,GAAG,0BAAkB,2CAA2C,CAAC;AACpG,QAAA,8BAA8B,GAAG,GAAG,0BAAkB,kBAAkB,CAAC;AAEzE,QAAA,0BAA0B,GAAG,GAAG,0BAAkB,sCAAsC,CAAC;AACzF,QAAA,wBAAwB,GAAG,GAAG,0BAAkB,YAAY,CAAC;AAE7D,QAAA,uBAAuB,GAAG,GAAG,0BAAkB,mCAAmC,CAAC;AACnF,QAAA,qBAAqB,GAAG,GAAG,0BAAkB,SAAS,CAAC;AAEvD,QAAA,gBAAgB,GAAG,GAAG,0BAAkB,mBAAmB,CAAC;AAC5D,QAAA,iBAAiB,GAAG,GAAG,0BAAkB,kCAAkC,CAAC;AAC5E,QAAA,yBAAyB,GAAG,GAAG,0BAAkB,WAAW,CAAC;AAE7D,QAAA,iBAAiB,GAAG,GAAG,0BAAkB,uBAAuB,CAAC;AACjE,QAAA,yBAAyB,GAAG,GAAG,0BAAkB,WAAW,CAAC;AAE7D,QAAA,mBAAmB,GAAG,GAAG,0BAAkB,kCAAkC,CAAC;AAC9E,QAAA,iBAAiB,GAAG,GAAG,0BAAkB,aAAa,CAAC;AAEvD,QAAA,sCAAsC,GAAG,GAAG,0BAAkB,aAAa,CAAC;AAC5E,QAAA,8BAA8B,GAAG,GAAG,0BAAkB,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../lib/constants.js"],"names":[],"mappings":";;;AAAa,QAAA,kBAAkB,GAAG,oBAAoB,CAAC;AAC1C,QAAA,6BAA6B,GAAG,WAAW,CAAC;AAE5C,QAAA,kBAAkB,GAAG,GAAG,0BAAkB,+BAA+B,CAAC;AAC1E,QAAA,0BAA0B,GAAG,GAAG,0BAAkB,gBAAgB,CAAC;AAEnE,QAAA,UAAU,GAAG,GAAG,0BAAkB,aAAa,CAAC;AAChD,QAAA,WAAW,GAAG,GAAG,0BAAkB,cAAc,CAAC;AAClD,QAAA,SAAS,GAAG,GAAG,0BAAkB,YAAY,CAAC;AAE9C,QAAA,gCAAgC,GAAG,GAAG,0BAAkB,2CAA2C,CAAC;AACpG,QAAA,8BAA8B,GAAG,GAAG,0BAAkB,OAAO,CAAC;AAC9D,QAAA,gCAAgC,GAAG,GAAG,0BAAkB,2CAA2C,CAAC;AACpG,QAAA,8BAA8B,GAAG,GAAG,0BAAkB,kBAAkB,CAAC;AAEzE,QAAA,0BAA0B,GAAG,GAAG,0BAAkB,sCAAsC,CAAC;AACzF,QAAA,wBAAwB,GAAG,GAAG,0BAAkB,YAAY,CAAC;AAE7D,QAAA,uBAAuB,GAAG,GAAG,0BAAkB,mCAAmC,CAAC;AACnF,QAAA,qBAAqB,GAAG,GAAG,0BAAkB,SAAS,CAAC;AAEvD,QAAA,gBAAgB,GAAG,GAAG,0BAAkB,mBAAmB,CAAC;AAC5D,QAAA,iBAAiB,GAAG,GAAG,0BAAkB,kCAAkC,CAAC;AAC5E,QAAA,yBAAyB,GAAG,GAAG,0BAAkB,WAAW,CAAC;AAE7D,QAAA,iBAAiB,GAAG,GAAG,0BAAkB,uBAAuB,CAAC;AACjE,QAAA,yBAAyB,GAAG,GAAG,0BAAkB,WAAW,CAAC;AAE7D,QAAA,mBAAmB,GAAG,GAAG,0BAAkB,kCAAkC,CAAC;AAC9E,QAAA,iBAAiB,GAAG,GAAG,0BAAkB,aAAa,CAAC;AAEvD,QAAA,sCAAsC,GAAG,GAAG,0BAAkB,aAAa,CAAC;AAC5E,QAAA,8BAA8B,GAAG,GAAG,0BAAkB,gBAAgB,CAAC;AAEvE,QAAA,sBAAsB,GAAG,GAAG,0BAAkB,4BAA4B,CAAC;AAC3E,QAAA,uBAAuB,GAAG,GAAG,0BAAkB,8BAA8B,CAAC;AAC9E,QAAA,sBAAsB,GAAG,GAAG,0BAAkB,yBAAyB,CAAC;AACxE,QAAA,qBAAqB,GAAG,GAAG,0BAAkB,wBAAwB,CAAC"}
|
package/lib/client.js
CHANGED
|
@@ -11,6 +11,10 @@ import { setDataState, setWifiState } from './commands/network';
|
|
|
11
11
|
import { getNotifications, adjustNotificationsPermissions } from './commands/notifications';
|
|
12
12
|
import { getSmsList } from './commands/sms';
|
|
13
13
|
import { performEditorAction, typeUnicode } from './commands/typing';
|
|
14
|
+
import {
|
|
15
|
+
makeMediaProjectionRecorder,
|
|
16
|
+
adjustMediaProjectionServicePermissions,
|
|
17
|
+
} from './commands/media-projection';
|
|
14
18
|
|
|
15
19
|
/**
|
|
16
20
|
* @typedef {Object} SettingsAppOpts
|
|
@@ -173,4 +177,7 @@ export class SettingsApp {
|
|
|
173
177
|
|
|
174
178
|
performEditorAction = performEditorAction;
|
|
175
179
|
typeUnicode = typeUnicode;
|
|
180
|
+
|
|
181
|
+
makeMediaProjectionRecorder = makeMediaProjectionRecorder;
|
|
182
|
+
adjustMediaProjectionServicePermissions = adjustMediaProjectionServicePermissions;
|
|
176
183
|
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import {waitForCondition} from 'asyncbox';
|
|
2
|
+
import B from 'bluebird';
|
|
3
|
+
import _ from 'lodash';
|
|
4
|
+
import fs from 'fs/promises';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import {
|
|
7
|
+
SETTINGS_HELPER_ID,
|
|
8
|
+
RECORDING_ACTION_START,
|
|
9
|
+
RECORDING_ACTION_STOP,
|
|
10
|
+
RECORDING_ACTIVITY_NAME,
|
|
11
|
+
RECORDING_SERVICE_NAME,
|
|
12
|
+
} from '../constants';
|
|
13
|
+
|
|
14
|
+
const RECORDING_STARTUP_TIMEOUT_MS = 3 * 1000;
|
|
15
|
+
const RECORDING_STOP_TIMEOUT_MS = 3 * 1000;
|
|
16
|
+
const RECORDINGS_ROOT = `/storage/emulated/0/Android/data/${SETTINGS_HELPER_ID}/files`;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {Object} StartMediaProjectionRecordingOpts
|
|
20
|
+
* @property {string} [resolution] Maximum supported resolution on-device (Detected automatically by the app
|
|
21
|
+
* itself), which usually equals to Full HD 1920x1080 on most phones however
|
|
22
|
+
* you can change it to following supported resolutions as well: "1920x1080",
|
|
23
|
+
* "1280x720", "720x480", "320x240", "176x144".
|
|
24
|
+
* @property {number} [maxDurationSec=900] Maximum allowed duration is 15 minutes; you can increase it if your test
|
|
25
|
+
* takes longer than that.
|
|
26
|
+
* @property {'high' | 'normal' | 'low'} [priority='high'] Recording thread priority.
|
|
27
|
+
* If you face performance drops during testing with recording enabled, you
|
|
28
|
+
* can reduce recording priority
|
|
29
|
+
* @property {string} [filename] You can type recording video file name as you want, but recording currently
|
|
30
|
+
* supports only "mp4" format so your filename must end with ".mp4". An
|
|
31
|
+
* invalid file name will fail to start the recording.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
class MediaProjectionRecorder {
|
|
35
|
+
/**
|
|
36
|
+
* @param {ADB} adb
|
|
37
|
+
*/
|
|
38
|
+
constructor(adb) {
|
|
39
|
+
this.adb = adb;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
*
|
|
44
|
+
* @returns {Promise<boolean>}
|
|
45
|
+
*/
|
|
46
|
+
async isRunning() {
|
|
47
|
+
const stdout = await this.adb.shell([
|
|
48
|
+
'dumpsys',
|
|
49
|
+
'activity',
|
|
50
|
+
'services',
|
|
51
|
+
RECORDING_SERVICE_NAME,
|
|
52
|
+
]);
|
|
53
|
+
return stdout.includes(RECORDING_SERVICE_NAME);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
*
|
|
58
|
+
* @param {StartMediaProjectionRecordingOpts} opts
|
|
59
|
+
* @returns {Promise<boolean>}
|
|
60
|
+
*/
|
|
61
|
+
async start(opts = {}) {
|
|
62
|
+
if (await this.isRunning()) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
await this.cleanup();
|
|
67
|
+
const {filename, maxDurationSec, priority, resolution} = opts;
|
|
68
|
+
const args = ['am', 'start', '-n', RECORDING_ACTIVITY_NAME, '-a', RECORDING_ACTION_START];
|
|
69
|
+
if (filename) {
|
|
70
|
+
args.push('--es', 'filename', filename);
|
|
71
|
+
}
|
|
72
|
+
if (maxDurationSec) {
|
|
73
|
+
args.push('--es', 'max_duration_sec', `${maxDurationSec}`);
|
|
74
|
+
}
|
|
75
|
+
if (priority) {
|
|
76
|
+
args.push('--es', 'priority', priority);
|
|
77
|
+
}
|
|
78
|
+
if (resolution) {
|
|
79
|
+
args.push('--es', 'resolution', resolution);
|
|
80
|
+
}
|
|
81
|
+
await this.adb.shell(args);
|
|
82
|
+
await new B((resolve, reject) => {
|
|
83
|
+
setTimeout(async () => {
|
|
84
|
+
if (!(await this.isRunning())) {
|
|
85
|
+
return reject(
|
|
86
|
+
new Error(
|
|
87
|
+
`The media projection recording is not running after ${RECORDING_STARTUP_TIMEOUT_MS}ms. ` +
|
|
88
|
+
`Please check the logcat output for more details.`
|
|
89
|
+
)
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
resolve();
|
|
93
|
+
}, RECORDING_STARTUP_TIMEOUT_MS);
|
|
94
|
+
});
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @returns {Promise<void>}
|
|
100
|
+
*/
|
|
101
|
+
async cleanup() {
|
|
102
|
+
await this.adb.shell([`rm -f ${RECORDINGS_ROOT}/*`]);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
*
|
|
107
|
+
* @returns {Promise<string?>}
|
|
108
|
+
*/
|
|
109
|
+
async pullRecent() {
|
|
110
|
+
const recordings = await this.adb.ls(RECORDINGS_ROOT, ['-tr']);
|
|
111
|
+
if (_.isEmpty(recordings)) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const tmpRoot = await fs.mkdtemp('recording');
|
|
116
|
+
const dstPath = path.join(tmpRoot, recordings[0]);
|
|
117
|
+
// increase timeout to 5 minutes because it might take a while to pull a large video file
|
|
118
|
+
await this.adb.pull(`${RECORDINGS_ROOT}/${recordings[0]}`, dstPath, {timeout: 300000});
|
|
119
|
+
return dstPath;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @returns {Promise<boolean>}
|
|
124
|
+
*/
|
|
125
|
+
async stop() {
|
|
126
|
+
if (!(await this.isRunning())) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
await this.adb.shell([
|
|
131
|
+
'am',
|
|
132
|
+
'start',
|
|
133
|
+
'-n',
|
|
134
|
+
RECORDING_ACTIVITY_NAME,
|
|
135
|
+
'-a',
|
|
136
|
+
RECORDING_ACTION_STOP,
|
|
137
|
+
]);
|
|
138
|
+
try {
|
|
139
|
+
await waitForCondition(async () => !(await this.isRunning()), {
|
|
140
|
+
waitMs: RECORDING_STOP_TIMEOUT_MS,
|
|
141
|
+
intervalMs: 500,
|
|
142
|
+
});
|
|
143
|
+
} catch (e) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
`The attempt to stop the current media projection recording timed out after ` +
|
|
146
|
+
`${RECORDING_STOP_TIMEOUT_MS}ms`
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Creates a new instance of the MediaProjection-based recorder
|
|
155
|
+
* The recorder only works since Android API 29+
|
|
156
|
+
*
|
|
157
|
+
* @this {import('../client').SettingsApp}
|
|
158
|
+
* @returns {MediaProjectionRecorder} The recorder instance
|
|
159
|
+
*/
|
|
160
|
+
export function makeMediaProjectionRecorder() {
|
|
161
|
+
return new MediaProjectionRecorder(this.adb);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Adjusts the necessary permissions for the
|
|
166
|
+
* Media Projection-based recording service
|
|
167
|
+
*
|
|
168
|
+
* @this {import('../client').SettingsApp}
|
|
169
|
+
* @returns {Promise<boolean>} If the permssions adjustment has actually been made
|
|
170
|
+
*/
|
|
171
|
+
export async function adjustMediaProjectionServicePermissions() {
|
|
172
|
+
if (await this.adb.getApiLevel() >= 29) {
|
|
173
|
+
await this.adb.shell(['appops', 'set', SETTINGS_HELPER_ID, 'PROJECT_MEDIA', 'allow']);
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @typedef {import('appium-adb').ADB} ADB
|
|
181
|
+
*/
|
package/lib/constants.js
CHANGED
|
@@ -31,3 +31,8 @@ export const MEDIA_SCAN_ACTION = `${SETTINGS_HELPER_ID}.scan_media`;
|
|
|
31
31
|
|
|
32
32
|
export const SETTING_NOTIFICATIONS_LISTENER_SERVICE = `${SETTINGS_HELPER_ID}/.NLService`;
|
|
33
33
|
export const NOTIFICATIONS_RETRIEVAL_ACTION = `${SETTINGS_HELPER_ID}.notifications`;
|
|
34
|
+
|
|
35
|
+
export const RECORDING_SERVICE_NAME = `${SETTINGS_HELPER_ID}/.recorder.RecorderService`;
|
|
36
|
+
export const RECORDING_ACTIVITY_NAME = `${SETTINGS_HELPER_ID}/io.appium.settings.Settings`;
|
|
37
|
+
export const RECORDING_ACTION_START = `${SETTINGS_HELPER_ID}.recording.ACTION_START`;
|
|
38
|
+
export const RECORDING_ACTION_STOP = `${SETTINGS_HELPER_ID}.recording.ACTION_STOP`;
|