Skip to content

Commit 175fa4d

Browse files
committed
refactor(android): keep the restore sequencing helpers under complexity thresholds
1 parent 6c5221b commit 175fa4d

1 file changed

Lines changed: 43 additions & 28 deletions

File tree

‎packages/platform-android/src/ime-restore.ts‎

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -84,50 +84,65 @@ async function settleTestImeFlushWindow(key: string, signal?: AbortSignal): Prom
8484
return true;
8585
}
8686

87+
// Whether this call proved the device needs no further recovery. A reason from an inspected
88+
// device answers directly; a nothing-inspected `not-activated-here` close earns it only by
89+
// consuming a deadline a confirmed restore opened. The window must be covered either way — an
90+
// aborted settle is never a completed settle.
91+
function isRecoveryMarkerClearEarned(
92+
reason: AndroidTestImeRestoreReason,
93+
settleOutcome: FlushSettleOutcome,
94+
flushWindowCovered: boolean,
95+
): boolean {
96+
if (!flushWindowCovered) return false;
97+
if (isDeviceRecoveryComplete(reason)) return true;
98+
return reason === 'not-activated-here' && settleOutcome === 'consumed';
99+
}
100+
87101
export async function restoreAndroidTestIme(
88102
device: DeviceInfo,
89103
options: { stateDir: string; shutdownTarget?: boolean; signal?: AbortSignal },
90104
): Promise<AndroidTestImeRestoreResult> {
91105
return await withAndroidTestImeRecoveryLock(options.stateDir, device.id, async () => {
92-
const deviceKey = getAndroidImeHelperDeviceKey(device);
93-
const settleKey = flushSettleKey(options.stateDir, device.id);
94-
const killingTarget = options.shutdownTarget === true && device.kind === 'emulator';
106+
const settling = options.shutdownTarget === true && device.kind === 'emulator';
95107
// This call returns before the close finalizer may start the kill, so a window opened by an
96108
// earlier aborted close is consumed here rather than outrunning this call's own restore.
97109
// An ordinary close starts no kill, so it neither consumes the window nor needs it covered;
98110
// the pending deadline stays for the next shutdown-bound close.
99-
const settleOutcome = killingTarget
100-
? await awaitPendingFlushSettle(settleKey, options.signal)
111+
const settleOutcome = settling
112+
? await awaitPendingFlushSettle(flushSettleKey(options.stateDir, device.id), options.signal)
101113
: 'none';
102-
let flushWindowCovered = settleOutcome !== 'aborted';
103-
let result: AndroidTestImeRestoreResult;
104-
if (!activeTestImeDevices.has(deviceKey)) {
105-
// Skip devices this process never activated (orphans from another process are handled by
106-
// restoreOrphanedAndroidTestImeOnDaemonStartup and the doctor check). Nothing was inspected,
107-
// so the device's recovery status stays unknown and its pending marker is none of ours —
108-
// unless this call just consumed a deadline opened by a confirmed restore.
109-
result = { restored: false, reason: 'not-activated-here' };
110-
} else {
111-
// Drop the owned-flag first so restoreAndroidTestImeFor's "owned by a live session" guard
112-
// does not skip this intentional close-time restore. The IME really is back on the previous
113-
// keyboard, so text routing must stop preferring the helper channel even on an abort.
114-
activeTestImeDevices.delete(deviceKey);
115-
const adb = resolveAndroidAdbExecutor(device);
116-
result = await restoreAndroidTestImeFor(adb, device);
117-
if (result.restored && killingTarget) {
118-
flushWindowCovered = await settleTestImeFlushWindow(settleKey, options.signal);
119-
}
120-
}
121-
const recoveryComplete =
122-
isDeviceRecoveryComplete(result.reason) ||
123-
(result.reason === 'not-activated-here' && settleOutcome === 'consumed');
124-
if (flushWindowCovered && recoveryComplete) {
114+
const result = await restoreOwnedAndroidTestIme(device);
115+
const flushWindowCovered =
116+
result.restored && settling
117+
? await settleTestImeFlushWindow(
118+
flushSettleKey(options.stateDir, device.id),
119+
options.signal,
120+
)
121+
: settleOutcome !== 'aborted';
122+
if (isRecoveryMarkerClearEarned(result.reason, settleOutcome, flushWindowCovered)) {
125123
await requireAndroidAdbHost().imeRecoveryMarkers.clear(options.stateDir, device.id);
126124
}
127125
return result;
128126
});
129127
}
130128

129+
// Restore this process's own device, or report that it never activated one. For an unactivated
130+
// device nothing is inspected (orphans from another process are handled by
131+
// restoreOrphanedAndroidTestImeOnDaemonStartup and the doctor check), so recovery stays unknown.
132+
async function restoreOwnedAndroidTestIme(
133+
device: DeviceInfo,
134+
): Promise<AndroidTestImeRestoreResult> {
135+
const deviceKey = getAndroidImeHelperDeviceKey(device);
136+
if (!activeTestImeDevices.has(deviceKey)) {
137+
return { restored: false, reason: 'not-activated-here' };
138+
}
139+
// Drop the owned-flag first so restoreAndroidTestImeFor's "owned by a live session" guard does
140+
// not skip this intentional close-time restore. The IME really is back on the previous keyboard,
141+
// so text routing must stop preferring the helper channel even on an abort.
142+
activeTestImeDevices.delete(deviceKey);
143+
return await restoreAndroidTestImeFor(resolveAndroidAdbExecutor(device), device);
144+
}
145+
131146
// The device was inspected and the helper is confirmed off it (restored, already not the active
132147
// IME, or no rebind record). A `set-failed` (still stuck), `record-unreadable` (the device may be
133148
// on Android's fallback IME), `owned-by-live-session` (a live session will restore it on close)

0 commit comments

Comments
 (0)