1import type { DeviceHubAccess } from "@t3tools/client-runtime/state/deviceHubAccess";
2import { withDeviceHubQuery } from "@t3tools/client-runtime/state/deviceHubAccess";
3
4export type AndroidFoldPosture = "closed" | "opened";
5
6export interface AndroidFoldState {
7 readonly supported: boolean;
8 readonly posture: "closed" | "half_opened" | "opened" | "flipped" | "tent" | null;
9 readonly hingeAngle: number | null;
10}
11
12const isRecord = (value: unknown): value is Record<string, unknown> =>
13 typeof value === "object" && value !== null && !Array.isArray(value);
14
15const parseFold = (payload: unknown): AndroidFoldState => {
16 if (!isRecord(payload) || payload.ok !== true || !isRecord(payload.fold)) {
17 throw new Error("Unexpected Android fold response.");
18 }
19 const { supported, posture, hingeAngle } = payload.fold;
20 if (
21 typeof supported !== "boolean" ||
22 (posture !== null &&
23 posture !== "closed" &&
24 posture !== "half_opened" &&
25 posture !== "opened" &&
26 posture !== "flipped" &&
27 posture !== "tent") ||
28 (hingeAngle !== null && (typeof hingeAngle !== "number" || !Number.isFinite(hingeAngle)))
29 ) {
30 throw new Error("Unexpected Android fold response.");
31 }
32 return { supported, posture, hingeAngle };
33};
34
35const foldRequest = async (
36 access: DeviceHubAccess,
37 deviceId: string,
38 posture?: AndroidFoldPosture,
39 signal?: AbortSignal,
40): Promise<AndroidFoldState> => {
41 const url = withDeviceHubQuery(
42 `${access.httpBase}/vendor/serve-emu/api/fold?${new URLSearchParams({ device: deviceId })}`,
43 access,
44 );
45 const response = await fetch(url, {
46 method: posture ? "POST" : "GET",
47 cache: "no-store",
48 credentials: access.credentials ? "include" : "same-origin",
49 ...(posture
50 ? { headers: { "content-type": "application/json" }, body: JSON.stringify({ posture }) }
51 : {}),
52 ...(signal ? { signal } : {}),
53 });
54 if (!response.ok) {
55 const payload: unknown = await response.json().catch(() => null);
56 const error = isRecord(payload) ? payload.error : null;
57 throw new Error(
58 typeof error === "string" ? error : `Fold command failed (${response.status}).`,
59 );
60 }
61 return parseFold(await response.json());
62};
63
64export const readAndroidFold = (access: DeviceHubAccess, deviceId: string, signal?: AbortSignal) =>
65 foldRequest(access, deviceId, undefined, signal);
66
67export const setAndroidFold = (
68 access: DeviceHubAccess,
69 deviceId: string,
70 posture: AndroidFoldPosture,
71 signal?: AbortSignal,
72) => foldRequest(access, deviceId, posture, signal);