+
    \j"                         R t ^ RIt^ RIt^ RIt^ RIt^ RIHtHt Rt	RR/R lt
R t ! R R]4      tR	 t]R
8X  d
   ]! 4        R# R# )a  Small browser controller for a headless Android emulator.

The controller intentionally uses only ADB primitives:

- `adb exec-out screencap -p` streams the current display.
- `adb shell input ...` sends tap, swipe, key, and text events.

This is slower than native emulator WebRTC, but it works in a headless shell and
does not require an emulator window or a physical Android device.
N)BaseHTTPRequestHandlerThreadingHTTPServeray  <!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Yorvana Billing Emulator</title>
  <style>
    :root { color-scheme: dark; }
    body { margin: 0; font: 14px system-ui, sans-serif; background: #111; color: #eee; }
    main { display: grid; grid-template-columns: minmax(300px, 430px) minmax(280px, 1fr); min-height: 100vh; }
    #screen { width: 100%; max-height: 100vh; object-fit: contain; background: #000; cursor: crosshair; }
    aside { padding: 16px; display: grid; align-content: start; gap: 12px; }
    button, input { font: inherit; padding: 10px 12px; border-radius: 6px; border: 1px solid #555; }
    button { background: #276ef1; color: white; cursor: pointer; }
    button.secondary { background: #24272d; }
    input { background: #202124; color: #eee; min-width: 0; }
    .row { display: flex; gap: 8px; flex-wrap: wrap; }
    .row button { flex: 1 1 auto; }
    #status { min-height: 20px; color: #b8c1d6; }
    @media (max-width: 760px) { main { grid-template-columns: 1fr; } aside { order: -1; } }
  </style>
</head>
<body>
<main>
  <img id="screen" alt="Android screen">
  <aside>
    <div class="row">
      <button onclick="key(3)">Home</button>
      <button onclick="key(4)">Back</button>
      <button onclick="key(187)">Overview</button>
      <button onclick="refresh()" class="secondary">Refresh</button>
    </div>
    <div class="row">
      <button onclick="launchPlay()">Play Store</button>
      <button onclick="swipe(540,1600,540,500,450)" class="secondary">Swipe Up</button>
      <button onclick="swipe(540,500,540,1600,450)" class="secondary">Swipe Down</button>
    </div>
    <input id="text" placeholder="Text for adb input text">
    <div class="row">
      <button onclick="typeText()">Type</button>
      <button onclick="key(66)" class="secondary">Enter</button>
      <button onclick="key(67)" class="secondary">Delete</button>
    </div>
    <div id="status"></div>
  </aside>
</main>
<script>
const img = document.getElementById('screen');
const statusEl = document.getElementById('status');
let naturalW = 1080;
let naturalH = 1920;

function status(text) { statusEl.textContent = text; }

async function post(path, body = {}) {
  status('Working...');
  const res = await fetch(path, { method: 'POST', body: JSON.stringify(body) });
  if (!res.ok) status(await res.text());
  else status('OK');
  setTimeout(refresh, 250);
}

function refresh() {
  img.src = '/screen.png?ts=' + Date.now();
}

img.onload = () => {
  naturalW = img.naturalWidth;
  naturalH = img.naturalHeight;
};

img.onclick = (ev) => {
  const rect = img.getBoundingClientRect();
  const x = Math.round((ev.clientX - rect.left) * naturalW / rect.width);
  const y = Math.round((ev.clientY - rect.top) * naturalH / rect.height);
  post('/tap', { x, y });
};

function key(code) { post('/key', { code }); }
function swipe(x1, y1, x2, y2, ms) { post('/swipe', { x1, y1, x2, y2, ms }); }
function launchPlay() { post('/launch-play'); }
function typeText() { post('/text', { text: document.getElementById('text').value }); }

refresh();
setInterval(refresh, 2000);
</script>
</body>
</html>
stdinc                    \         P                  ! R RV .VOV\         P                  \         P                  RR7      P                  # )adbz-sT)inputstdoutstderrcheck)
subprocessrunPIPEr   )serialr   argss   &$* tools/billing/adb_web_control.pyr   r   o   s=    >>	f$t$ f    c                 F    V P                  R R4      P                  RR4      # )\z\\ z%s)replace)raws   &r   encode_input_textr   y   s     ;;tV$,,S$77r   c                   B   a  ] tR t^}t o RtR tR tR tR tR t	Rt
V tR# )	Handleremulator-5554c                F   \         P                  P                  V P                  4      P                  R 8X  dH   V P	                  ^4       V P                  RR4       V P                  RR4       V P                  4        R# V P	                  R4       V P                  4        R# )/screen.pngcontent-type	image/pngcache-controlno-storeN  )urllibparseurlparsepathsend_responsesend_headerend_headers)selfs   &r   do_HEADHandler.do_HEAD   sz    <<  +00MAs#^[9_j93r   c           
        \         P                  P                  V P                  4      P                  pVR 8X  d(   V P	                  ^R\
        P                  4       4       R# VR8X  d-    V P	                  ^R\        V P                  RRR4      4       R# V P	                  R
R	R4       R#   \        P                   d)   pT P	                  RR	TP                  4        Rp?R# Rp?ii ; i)/ztext/html; charset=utf-8Nr   r   zexec-out	screencap-p  
text/plainr!   	   not found)r"   r#   r$   r%   sendINDEXencoder   r   r   CalledProcessErrorr	   )r)   r%   excs   &  r   do_GETHandler.do_GET   s    ||$$TYY/443;IIc5u||~F= 9		#{CZVZ,[\ 		#|\2 00 9		#|SZZ889s   *)B* *C'?C""C'c                   \        V P                  P                  R R4      4      p\        P                  ! V P
                  P                  V4      ;'       g    R4      p\        P                  P                  V P                  4      P                  p VR8X  d=   \        V P                  RRR\        VR,          4      \        VR,          4      4       EM)VR	8X  dx   \        V P                  RRR
\        VR,          4      \        VR,          4      \        VR,          4      \        VR,          4      \        VP                  RR4      4      4	       MVR8X  d+   \        V P                  RRR\        VR,          4      4       MzVR8X  d   \        V P                  RRRRR4       MXVR8X  d=   \        V P                  RRR\        \        VP                  RR4      4      4      4       MV P                  RRR4       R# V P                  ^RR 4       R#   \         \"        P$                  3 dM   p\'        TR!R"4      ;'       g    \        T4      P)                  4       pT P                  R#RT4        Rp?R# Rp?ii ; i)$content-length0s   {}z/tapshellr   tapxyz/swipeswipex1y1x2y2msi,  z/keykeyeventcodez/launch-playmonkeyr/   zcom.android.vending1z/texttext r!   r1   r2   Nr   r	   s   bad requestr0   )intheadersgetjsonloadsrfilereadr"   r#   r$   r%   r   r   strr   r3   KeyErrorr   r6   getattrr5   )r)   lengthbodyr%   r7   messages   &     r   do_POSTHandler.do_POST   s   T\\%%&6<=zz$**//&1::U;||$$TYY/44	2v~DKK'5#d3i.#dSVi.Y!KKT
OT
OT
OT
Os+,
 DKK':s4<?PQ'DKK(D:OQTUDKK'6;LSQUQYQYZ`bdQeMf;gh		#|\:IIc<-*778 	2c8^<QQC@QGIIc<11	2s    E*H ?H I;/AI66I;c                    R # )N )r)   fmtr   s   &&*r   log_messageHandler.log_message   s    r   c                   V P                  V4       V P                  R V4       V P                  RR4       V P                  R\        \        V4      4      4       V P	                  4        V P
                  P                  V4       R# )r   r   r    r;   N)r&   r'   rT   lenr(   wfilewrite)r)   rH   content_typerX   s   &&&&r   r3   Handler.send   sg    4 6*5)3s4y>:

r   r]   N)__name__
__module____qualname____firstlineno__r   r*   r8   rZ   r_   r3   __static_attributes____classdictcell__)__classdict__s   @r   r   r   }   s)     F32B r   r   c            	         \         P                  ! R R7      p V P                  RRRR7       V P                  RRRR7       V P                  R	\        R
RR7       V P	                  4       pVP
                  \        n        \        RVP
                   RVP                   RVP                   R2RR7       \        VP                  VP                  3\        4      P                  4        R# )z;Serve a browser controller for a headless Android emulator.)descriptionz--serialr   zADB serial to control.)defaulthelpz--hostz	127.0.0.1zHost/IP to bind.z--porti  zHTTP port to bind.)typerp   rq   zServing z at http://:r-   T)flushN)argparseArgumentParseradd_argumentrM   
parse_argsr   r   printhostportr   serve_forever)parserr   s     r   mainr~      s    $$1noF

OBZ[
+<NO
sD?STD[[GN	HT[[MTYYKq1
ETRDII.8FFHr   __main__)__doc__ru   rP   r   urllib.parser"   http.serverr   r   r4   r   r   r   r~   rg   r]   r   r   <module>r      s_   	     CX	vT 8D$ DN	I zF r   