Skip to content

Commit 52006de

Browse files
committed
Almost everything works!
1 parent 86da5a5 commit 52006de

File tree

1 file changed

+74
-29
lines changed

1 file changed

+74
-29
lines changed

src/glue.ts

Lines changed: 74 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ class MetaMaskAndroidDriver {
8383
private running: boolean;
8484
private windowWatcher: Promise<void>;
8585
private readonly glue: MetaMaskAndroidGlue;
86+
private lastActive: DOMHighResTimeStamp;
8687

8788
private constructor(
8889
driver: Browser,
@@ -94,6 +95,7 @@ class MetaMaskAndroidDriver {
9495
this.windowWatcher = this.watchWindows();
9596
this.glue = glue;
9697
this.capabilities = caps;
98+
this.lastActive = performance.now();
9799
}
98100

99101
public static async create(
@@ -125,6 +127,12 @@ class MetaMaskAndroidDriver {
125127
return new MetaMaskAndroidDriver(driver, glue, capabilities);
126128
}
127129

130+
private async activateApp(driver: Browser): Promise<void> {
131+
await driver.executeScript("mobile: startActivity", [
132+
{ intent: "io.metamask/io.metamask.MainActivity" },
133+
]);
134+
}
135+
128136
public async unlockWithPassword(driver: Browser): Promise<void> {
129137
const passwordTxt = await driver.$(
130138
'//android.widget.EditText[@resource-id="login-password-input"]',
@@ -140,9 +148,7 @@ class MetaMaskAndroidDriver {
140148

141149
if (4 !== appState) {
142150
// The app is not in the foreground.
143-
await driver.executeScript("mobile: startActivity", [
144-
{ intent: "io.metamask/io.metamask.MainActivity" },
145-
]);
151+
this.activateApp(driver);
146152
}
147153

148154
try {
@@ -297,20 +303,32 @@ class MetaMaskAndroidDriver {
297303
);
298304
}
299305

300-
private async emitSignMessage(
301-
driver: Browser,
302-
handle: string,
303-
): Promise<void> {
306+
private async emitSignMessage(driver: Browser): Promise<Event> {
304307
logger.debug("emitting signmessage");
305308

306-
throw new Error("emitSignMessage: not implemented");
309+
const message = await driver
310+
.$(
311+
'//android.widget.TextView[@text="Message:"]/following-sibling::android.widget.TextView[@text]',
312+
)
313+
.getAttribute("text")
314+
.then((text) => text.trim());
307315

316+
const uuid = crypto.randomUUID();
308317
this.glue.emit(
309318
"signmessage",
310-
new SignMessageEvent(handle, {
311-
message: "TODO",
319+
new SignMessageEvent(uuid, {
320+
message,
312321
}),
313322
);
323+
return { uuid };
324+
}
325+
326+
private isPersonalSignModal(driver: Browser): Promise<boolean> {
327+
return driver
328+
.$(
329+
'//android.view.ViewGroup[@resource-id="personal-signature-request"]',
330+
)
331+
.isExisting();
314332
}
315333

316334
private async isSendTransactionModal(driver: Browser): Promise<boolean> {
@@ -319,16 +337,11 @@ class MetaMaskAndroidDriver {
319337
);
320338
const pillExists = pill.isExisting();
321339

322-
// TODO: find a better indicator of SendTransaction...
323-
/*
324-
const changes = await driver.$(
325-
'//android.widget.TextView[@text="Estimated changes"]',
326-
);
327-
const changesExists = changes.isExisting();
328-
*/
340+
const toExists = driver
341+
.$('//android.widget.TextView[@text="To:"]')
342+
.isExisting();
329343

330-
const changesExists = Promise.resolve(true);
331-
return (await pillExists) && (await changesExists);
344+
return (await pillExists) && (await toExists);
332345
}
333346

334347
private async isConnectAccountModal(driver: Browser): Promise<boolean> {
@@ -352,6 +365,10 @@ class MetaMaskAndroidDriver {
352365
active: this.isSendTransactionModal(driver),
353366
handler: (b: Browser) => this.emitSendTransaction(b),
354367
},
368+
{
369+
active: this.isPersonalSignModal(driver),
370+
handler: (b: Browser) => this.emitSignMessage(b),
371+
},
355372
];
356373

357374
let handler = null;
@@ -387,11 +404,24 @@ class MetaMaskAndroidDriver {
387404
{ appId: "io.metamask" },
388405
]);
389406

407+
const now = performance.now();
408+
390409
if (4 !== appState) {
391410
// The app is not in the foreground.
411+
412+
if (now - this.lastActive > 30000.0) {
413+
// Need to flip back to the app every so often to check
414+
// for events.
415+
this.lastActive = now; // Prevent spamming.
416+
await this.driver.lock(async (driver) => {
417+
await this.activateApp(driver);
418+
});
419+
}
392420
continue;
393421
}
394422

423+
this.lastActive = now;
424+
395425
await this.driver.lock(async (driver) => {
396426
this.pendingEvent = await this.event(driver);
397427
});
@@ -602,6 +632,19 @@ export class MetaMaskAndroidGlue extends Glue {
602632
);
603633
await nameEdit.addValue(chainName);
604634

635+
const chainIdEdit = await driver.$(
636+
'//android.widget.EditText[@resource-id="input-chain-id"]',
637+
);
638+
while (true) {
639+
try {
640+
await chainIdEdit.clearValue();
641+
await chainIdEdit.addValue(action.chainId);
642+
break;
643+
} catch (_) {
644+
// Sometimes MetaMask is fast enough to populate this field.
645+
}
646+
}
647+
605648
const rpcDrop = await driver.$(
606649
'//android.view.ViewGroup[@resource-id="drop-down-rpc-menu"]',
607650
);
@@ -622,15 +665,6 @@ export class MetaMaskAndroidGlue extends Glue {
622665
);
623666
await confirmRpcBtn.click();
624667

625-
const chainIdEdit = await driver.$(
626-
'//android.widget.EditText[@resource-id="input-chain-id"]',
627-
);
628-
try {
629-
await chainIdEdit.addValue(action.chainId);
630-
} catch (_) {
631-
// Sometimes MetaMask is fast enough to populate this field.
632-
}
633-
634668
const symbolEdit = await driver.$(
635669
'//android.widget.EditText[@resource-id="input-network-symbol"]',
636670
);
@@ -704,7 +738,18 @@ export class MetaMaskAndroidGlue extends Glue {
704738
override async signMessage(action: SignMessage): Promise<void> {
705739
const cb = await this.driver;
706740
await cb.lock(async (driver) => {
707-
throw new Error("signMessage: not implemented");
741+
let btnXpath;
742+
if (action.action === "reject") {
743+
btnXpath =
744+
'//android.widget.Button[@content-desc="request-signature-cancel-button"]';
745+
} else if (action.action === "approve") {
746+
btnXpath =
747+
'//android.widget.Button[@content-desc="request-signature-confirm-button"]';
748+
} else {
749+
throw new Error("signMessage: not implemented");
750+
}
751+
752+
await driver.$(btnXpath).click();
708753
}, action.id);
709754
}
710755

0 commit comments

Comments
 (0)