Skip to content

Commit cd1a07a

Browse files
feat: Add a capability to adjust injected image properties (#942)
1 parent 9272608 commit cd1a07a

File tree

4 files changed

+88
-7
lines changed

4 files changed

+88
-7
lines changed

Diff for: lib/commands/device/utils.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
EMPTY_IME,
1010
} from 'io.appium.settings';
1111
import B from 'bluebird';
12+
import { prepareEmulatorForImageInjection } from '../image-injection';
1213

1314
const HELPER_APP_INSTALL_RETRIES = 3;
1415
const HELPER_APP_INSTALL_RETRY_DELAY_MS = 5000;
@@ -118,8 +119,9 @@ export async function prepareEmulator(adb) {
118119
}
119120
const args = prepareAvdArgs.bind(this)();
120121
if (isEmulatorRunning) {
121-
if (args.includes('-wipe-data')) {
122-
this.log.debug(`Killing '${avdName}' because it needs to be wiped at start.`);
122+
if (await prepareEmulatorForImageInjection.bind(this)(/** @type {string} */ (adb.sdkRoot))
123+
|| args.includes('-wipe-data')) {
124+
this.log.debug(`Killing '${avdName}'`);
123125
await adb.killEmulator(avdName);
124126
} else {
125127
this.log.debug('Not launching AVD because it is already running.');

Diff for: lib/commands/image-injection.js

+50-5
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,73 @@ import _ from 'lodash';
66

77
const EMULATOR_RESOURCES_ROOT = ['emulator', 'resources'];
88
const CONFIG_NAME = 'Toren1BD.posters';
9-
const CONFIG = `poster wall
9+
const DEFAULT_CONFIG_PAYLOAD_PREFIX = `poster wall
1010
size 2 2
1111
position -0.807 0.320 5.316
1212
rotation 0 -150 0
1313
default poster.png
1414
15-
poster table
15+
poster table`;
16+
const DEFAULT_CONFIG_PAYLOAD = `${DEFAULT_CONFIG_PAYLOAD_PREFIX}
1617
size 1 1
1718
position 0 0 -1.5
1819
rotation 0 0 0
1920
`;
2021
const PNG_MAGIC = '89504e47';
2122
const PNG_MAGIC_LENGTH = 4;
2223

24+
/**
25+
* Updates the emulator configuration to show the
26+
* image using the given properties when one opens the Camera app.
27+
*
28+
* @this {AndroidDriver}
29+
* @param {string} sdkRoot ADB SDK root folder path
30+
* @returns {Promise<boolean>} If emulator services restart is needed
31+
* to load the updated config or false if the current config is already up to date
32+
* or does not need to be updated
33+
*/
34+
export async function prepareEmulatorForImageInjection(sdkRoot) {
35+
const { injectedImageProperties: props } = this.opts;
36+
if (!props) {
37+
return false;
38+
}
39+
40+
const size = `size ${props.size?.scaleX ?? 1} ${props.size?.scaleY ?? 1}`;
41+
const position = `position ${props.position?.x ?? 0} ${props.position?.y ?? 0} ${props.position?.z ?? -1.5}`;
42+
const rotation = `rotation ${props.rotation?.x ?? 0} ${props.rotation?.y ?? 0} ${props.rotation?.z ?? 0}`;
43+
const strProps = `${size}\n${position}\n${rotation}`;
44+
const configPath = path.resolve(sdkRoot, ...EMULATOR_RESOURCES_ROOT, CONFIG_NAME);
45+
if (await fs.exists(configPath)) {
46+
const configPayload = await fs.readFile(configPath, 'utf8');
47+
if (configPayload.includes(strProps)) {
48+
this.log.info(`The image injection config at '${configPath}' is already up to date. Doing nothing`);
49+
return false;
50+
}
51+
const updatedPayload = `${DEFAULT_CONFIG_PAYLOAD_PREFIX}
52+
${size}
53+
${position}
54+
${rotation}
55+
`;
56+
await fs.writeFile(configPath, updatedPayload, 'utf-8');
57+
} else {
58+
await fs.writeFile(configPath, DEFAULT_CONFIG_PAYLOAD, 'utf-8');
59+
}
60+
this.log.info(
61+
`The image injection config at '${configPath}' has been successfully updated with ` +
62+
`${size}, ${position}, ${rotation}. ` +
63+
`Expecting further emulator restart to reload the changed config.`
64+
);
65+
return true;
66+
}
67+
2368
/**
2469
* Updates the emulator configuration to show the given
2570
* image on the foreground when one opens the Camera app.
2671
* It is expected that the rear camera of the emulator is
2772
* configured to show VirtualScene for this feature to work.
73+
* It is expected that the Virtual Scene posters config is
74+
* already prepared and loaded either manually or using the
75+
* `injectedImageProperties` capability.
2876
*
2977
* @this {AndroidDriver}
3078
* @param {import('./types').ImageInjectionOpts} opts
@@ -49,9 +97,6 @@ export async function mobileInjectEmulatorCameraImage(opts) {
4997
);
5098
}
5199

52-
const sdkRoot = /** @type {string} */ (this.adb.sdkRoot);
53-
const destinationFolder = path.resolve(sdkRoot, ...EMULATOR_RESOURCES_ROOT);
54-
await fs.writeFile(path.resolve(destinationFolder, CONFIG_NAME), CONFIG, 'utf-8');
55100
const tmpImagePath = await tempDir.path({
56101
// this is needed to avoid creation of multiple temp files for the same image payload
57102
prefix: calculateImageHash(pngBuffer),

Diff for: lib/commands/types.ts

+31
Original file line numberDiff line numberDiff line change
@@ -1233,3 +1233,34 @@ export interface BluetoothOptions {
12331233
export interface NfcOptions {
12341234
action: 'enable' | 'disable';
12351235
}
1236+
1237+
export interface InjectedImageSize {
1238+
/** X scale value in range (0..) */
1239+
scaleX?: number;
1240+
/** Y scale value in range (0..) */
1241+
scaleY?: number;
1242+
}
1243+
1244+
export interface InjectedImagePosition {
1245+
/** Normalized X coordinate, where 0 means the image is centered on the viewport */
1246+
x?: number;
1247+
/** Normalized Y coordinate, where 0 means the image is centered on the viewport */
1248+
y?: number;
1249+
/** Normalized Z coordinate, where 0 means the image is centered on the viewport */
1250+
z?: number;
1251+
}
1252+
1253+
export interface InjectedImageRotation {
1254+
/** X rotation value in degrees */
1255+
x?: number;
1256+
/** Y rotation value in degrees */
1257+
y?: number;
1258+
/** Z rotation value in degrees */
1259+
z?: number;
1260+
}
1261+
1262+
export interface InjectedImageProperties {
1263+
size?: InjectedImageSize;
1264+
position?: InjectedImagePosition;
1265+
rotation?: InjectedImageRotation;
1266+
}

Diff for: lib/constraints.ts

+3
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,9 @@ export const ANDROID_DRIVER_CONSTRAINTS = {
266266
timeZone: {
267267
isString: true,
268268
},
269+
injectedImageProperties: {
270+
isObject: true,
271+
}
269272
} as const satisfies Constraints;
270273

271274
export default ANDROID_DRIVER_CONSTRAINTS;

0 commit comments

Comments
 (0)