io.appium.settings
Version:
App for dealing with Android settings
164 lines • 6.08 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeMediaProjectionRecorder = makeMediaProjectionRecorder;
exports.adjustMediaProjectionServicePermissions = adjustMediaProjectionServicePermissions;
const asyncbox_1 = require("asyncbox");
const bluebird_1 = __importDefault(require("bluebird"));
const lodash_1 = __importDefault(require("lodash"));
const promises_1 = __importDefault(require("fs/promises"));
const node_path_1 = __importDefault(require("node:path"));
const constants_1 = require("../constants");
const RECORDING_STARTUP_TIMEOUT_MS = 3 * 1000;
const RECORDING_STOP_TIMEOUT_MS = 3 * 1000;
const RECORDINGS_ROOT = `/storage/emulated/0/Android/data/${constants_1.SETTINGS_HELPER_ID}/files`;
/**
* @typedef {Object} StartMediaProjectionRecordingOpts
* @property {string} [resolution] Maximum supported resolution on-device (Detected automatically by the app
* itself), which usually equals to Full HD 1920x1080 on most phones however
* you can change it to following supported resolutions as well: "1920x1080",
* "1280x720", "720x480", "320x240", "176x144".
* @property {number} [maxDurationSec=900] Maximum allowed duration is 15 minutes; you can increase it if your test
* takes longer than that.
* @property {'high' | 'normal' | 'low'} [priority='high'] Recording thread priority.
* If you face performance drops during testing with recording enabled, you
* can reduce recording priority
* @property {string} [filename] You can type recording video file name as you want, but recording currently
* supports only "mp4" format so your filename must end with ".mp4". An
* invalid file name will fail to start the recording.
*/
class MediaProjectionRecorder {
/**
* @param {ADB} adb
*/
constructor(adb) {
this.adb = adb;
}
/**
*
* @returns {Promise<boolean>}
*/
async isRunning() {
const stdout = await this.adb.shell([
'dumpsys',
'activity',
'services',
constants_1.RECORDING_SERVICE_NAME,
]);
return stdout.includes(constants_1.RECORDING_SERVICE_NAME);
}
/**
*
* @param {StartMediaProjectionRecordingOpts} opts
* @returns {Promise<boolean>}
*/
async start(opts = {}) {
if (await this.isRunning()) {
return false;
}
await this.cleanup();
const { filename, maxDurationSec, priority, resolution } = opts;
const args = ['am', 'start', '-n', constants_1.RECORDING_ACTIVITY_NAME, '-a', constants_1.RECORDING_ACTION_START];
if (filename) {
args.push('--es', 'filename', filename);
}
if (maxDurationSec) {
args.push('--es', 'max_duration_sec', `${maxDurationSec}`);
}
if (priority) {
args.push('--es', 'priority', priority);
}
if (resolution) {
args.push('--es', 'resolution', resolution);
}
await this.adb.shell(args);
await new bluebird_1.default((resolve, reject) => {
setTimeout(async () => {
if (!(await this.isRunning())) {
return reject(new Error(`The media projection recording is not running after ${RECORDING_STARTUP_TIMEOUT_MS}ms. ` +
`Please check the logcat output for more details.`));
}
resolve();
}, RECORDING_STARTUP_TIMEOUT_MS);
});
return true;
}
/**
* @returns {Promise<void>}
*/
async cleanup() {
await this.adb.shell([`rm -f ${RECORDINGS_ROOT}/*`]);
}
/**
*
* @returns {Promise<string?>}
*/
async pullRecent() {
const recordings = await this.adb.ls(RECORDINGS_ROOT, ['-tr']);
if (lodash_1.default.isEmpty(recordings)) {
return null;
}
const tmpRoot = await promises_1.default.mkdtemp('recording');
const dstPath = node_path_1.default.join(tmpRoot, recordings[0]);
// increase timeout to 5 minutes because it might take a while to pull a large video file
await this.adb.pull(`${RECORDINGS_ROOT}/${recordings[0]}`, dstPath, { timeout: 300000 });
return dstPath;
}
/**
* @returns {Promise<boolean>}
*/
async stop() {
if (!(await this.isRunning())) {
return false;
}
await this.adb.shell([
'am',
'start',
'-n',
constants_1.RECORDING_ACTIVITY_NAME,
'-a',
constants_1.RECORDING_ACTION_STOP,
]);
try {
await (0, asyncbox_1.waitForCondition)(async () => !(await this.isRunning()), {
waitMs: RECORDING_STOP_TIMEOUT_MS,
intervalMs: 500,
});
}
catch {
throw new Error(`The attempt to stop the current media projection recording timed out after ` +
`${RECORDING_STOP_TIMEOUT_MS}ms`);
}
return true;
}
}
/**
* Creates a new instance of the MediaProjection-based recorder
* The recorder only works since Android API 29+
*
* @this {import('../client').SettingsApp}
* @returns {MediaProjectionRecorder} The recorder instance
*/
function makeMediaProjectionRecorder() {
return new MediaProjectionRecorder(this.adb);
}
/**
* Adjusts the necessary permissions for the
* Media Projection-based recording service
*
* @this {import('../client').SettingsApp}
* @returns {Promise<boolean>} If the permssions adjustment has actually been made
*/
async function adjustMediaProjectionServicePermissions() {
if (await this.adb.getApiLevel() >= 29) {
await this.adb.shell(['appops', 'set', constants_1.SETTINGS_HELPER_ID, 'PROJECT_MEDIA', 'allow']);
return true;
}
return false;
}
/**
* @typedef {import('appium-adb').ADB} ADB
*/
//# sourceMappingURL=media-projection.js.map