Documentation
¶
Overview ¶
This file is the transport that binds the sovereign codec (protocol.go) to a live Java host: it dials the host's abstract unix socket, creates and maps the shared framebuffer, hands its descriptor over, paints the go-widgets root into it and posts a frame per damaged rectangle. It is CGO-free — the whole Android side of the process is a socket and an mmap, both of which Go makes on its own — so the application binary stays exactly as sovereign as it is on X11 or Wayland.
Package android implements the application half of the go-widgets Android host protocol, so a go-widgets application runs inside a real Android app exactly as it runs on X11, Wayland, Cocoa or Win32.
Android hands out no drawable surface to a process that is not the app: the whole graphics API is behind JNI, and JNI needs cgo. So the app is split in two. A thin Java host owns the Activity, the SurfaceView and the input stream; the go-widgets application is an ordinary CGO-free executable the host spawns, which paints into a shared mapping and tells the host which rectangle changed. The split is the same one the Linux back-ends already live with — a socket protocol plus a shared pixel buffer — with the Java host standing where the X server or the Wayland compositor stands.
This file is the SOVEREIGN, transport-agnostic codec: the wire messages, the framing, and the input→toolkit.Event mapping, over plain Go values. It carries no syscall and no net dependency, so it builds — and is unit-tested to 100% — on every GOOS. The transport that dials the host socket, maps the buffer and drives a widget tree lives in client.go.
Index ¶
- Constants
- Variables
- func AndroidClass(r toolkit.Role) string
- func DecodeA11yAction(b []byte) (int, error)
- func DecodeReady(b []byte) (w, h int, err error)
- func DecodeTextDelete(b []byte) (int, error)
- func EncodeA11yAction(index int) []byte
- func EncodeA11yTree(els []A11yElement) []byte
- func EncodeConfig(c Config) []byte
- func EncodeFrame(r Rect) []byte
- func EncodeInsets(i Insets) []byte
- func EncodeKey(k Key) []byte
- func EncodeReady(w, h int) []byte
- func EncodeTextDelete(n int) []byte
- func EncodeTouch(t Touch) []byte
- func FrameMessage(typ uint8, body []byte) []byte
- func MapKey(k Key) []toolkit.Event
- func MapText(s string) []toolkit.Event
- func MapTextDelete(n int) []toolkit.Event
- func MapTouch(t Touch, held, primary bool) []toolkit.Event
- func ReadMessage(r io.Reader) (typ uint8, body []byte, err error)
- func WriteMessage(w io.Writer, typ uint8, body []byte) error
- type A11yElement
- type Client
- func (c *Client) Close() error
- func (c *Client) Density() int
- func (c *Client) Insets() Insets
- func (c *Client) Repaint()
- func (c *Client) Run(root toolkit.Widget) error
- func (c *Client) SetFullBleed(on bool)
- func (c *Client) SetSoftKeyboard(show bool)
- func (c *Client) SetTitle(title string)
- func (c *Client) Size() (int, int)
- func (c *Client) String() string
- type Config
- type Insets
- type Key
- type Rect
- type Touch
Constants ¶
const ( // MsgConfig carries the surface geometry and the shared buffer path. The // host sends it once at start-up and again on every resize or rotation. MsgConfig uint8 = 0x01 // MsgTouch carries one pointer sample in surface pixels. MsgTouch uint8 = 0x02 // MsgKey carries one key event: an Android key code plus the unicode rune // the host's key-character map produced (0 when the key produces none). MsgKey uint8 = 0x03 // MsgLifecycle carries an Activity transition: the app keeps its widget // tree across a pause, but stops painting until it resumes. MsgLifecycle uint8 = 0x04 // MsgClose asks the application to end its Run loop. MsgClose uint8 = 0x05 // MsgA11yRequest asks the application for its accessibility tree. The host // sends it only when something is actually reading one, so an app with no // screen reader attached never builds a tree at all. MsgA11yRequest uint8 = 0x07 // MsgA11yAction carries the index of the element a screen reader activated. MsgA11yAction uint8 = 0x08 // MsgText carries text an input method committed, as UTF-8. A soft keyboard // does not send keystrokes: it commits finished text, sometimes several // characters at once (a word completion, an emoji, a pasted clipboard). MsgText uint8 = 0x09 // MsgTextDelete asks to delete a number of characters before the cursor, // which is how an input method spells backspace. MsgTextDelete uint8 = 0x0a // MsgInsets carries the area of the surface the system is drawing over. // It is its own message rather than a Config field because insets change // on their own schedule: the soft keyboard opening does not resize the // surface, and a bar auto-hiding does not either. MsgInsets uint8 = 0x06 // MsgReady tells the host the shared buffer is mapped at the announced // size, so the host may map it in turn. Every MsgFrame that follows // refers to this mapping, until the next MsgReady replaces it. MsgReady uint8 = 0x81 // MsgFrame tells the host which surface-local rectangle changed. MsgFrame uint8 = 0x82 // MsgTitle updates the host's window title. MsgTitle uint8 = 0x83 // MsgBye tells the host the application ended. MsgBye uint8 = 0x84 // MsgA11yTree answers MsgA11yRequest with the accessibility elements. MsgA11yTree uint8 = 0x85 // MsgKeyboard asks the host to show or hide the soft keyboard. Only the host // can: the keyboard is a window, and the application owns no windows. MsgKeyboard uint8 = 0x86 )
Message types. Host→app messages are below 0x80, app→host at or above it, so a misrouted message is a decode error rather than a plausible other message.
const ( TouchDown uint8 = 0 TouchUp uint8 = 1 TouchMove uint8 = 2 )
Touch actions, matching the three MotionEvent actions the host forwards.
const ( KeyDown uint8 = 0 KeyUp uint8 = 1 )
Key actions.
const ( LifecyclePause uint8 = 0 LifecycleResume uint8 = 1 )
Lifecycle states.
const EnvSocket = "GW_ANDROID_SOCKET"
EnvSocket names the environment variable the Java host sets to the abstract socket it is listening on. The host generates a fresh name per launch, so two instances of the app never collide.
const MaxPayload = 1 << 16
MaxPayload bounds one decoded message body. The largest message a host legitimately sends is a Config carrying a filesystem path, so a frame beyond this is a desynchronised stream — refused rather than allocated.
Variables ¶
var ErrShortPayload = errors.New("android: truncated message payload")
ErrShortPayload reports a message whose body is too short for its type.
var ErrUnsupported = errors.New("android: no Android host on this platform")
ErrUnsupported reports an environment with no Android host: every GOOS but Linux, where the abstract socket and the shared mapping the host protocol needs do not exist. A cross-built application gets this from Dial and can report it and exit cleanly, exactly as go-widgets/window does off its supported back-ends.
Functions ¶
func AndroidClass ¶ added in v0.5.0
AndroidClass returns the Android class name for a toolkit role. Anything with no more specific mapping is a TextView, which is what Android itself uses for a piece of readable content.
func DecodeA11yAction ¶ added in v0.5.0
DecodeA11yAction parses a MsgA11yAction body: the index of the element a screen reader activated.
func DecodeReady ¶
DecodeReady parses a MsgReady body.
func DecodeTextDelete ¶ added in v0.10.0
DecodeTextDelete parses a MsgTextDelete body.
func EncodeA11yAction ¶ added in v0.5.0
EncodeA11yAction builds a MsgA11yAction body.
func EncodeA11yTree ¶ added in v0.5.0
func EncodeA11yTree(els []A11yElement) []byte
EncodeA11yTree builds a MsgA11yTree body: a count, then each element as three length-prefixed strings, four coordinates and a flag.
func EncodeFrame ¶
EncodeFrame builds a MsgFrame body naming the damaged rectangle.
func EncodeInsets ¶ added in v0.3.0
EncodeInsets builds a MsgInsets body.
func EncodeReady ¶
EncodeReady builds a MsgReady body: the size the application actually mapped.
func EncodeTextDelete ¶ added in v0.10.0
EncodeTextDelete builds a MsgTextDelete body.
func FrameMessage ¶ added in v0.4.0
FrameMessage returns one framed message: a 4-byte big-endian length covering the type byte and the body, then the type byte, then the body. Big-endian keeps the Java host on DataInputStream.readInt with no byte-swapping.
It exists as bytes rather than as writes because a message that carries an ancillary descriptor has to reach the host in ONE sendmsg: split across two writes, the host could attribute the descriptor to the wrong message.
func MapKey ¶
MapKey maps one Android key event to toolkit events, mirroring the wasmbox and X11 mappings: a named key is one EventKeyDown/EventKeyUp; a key that committed a character is an EventKeyDown followed by an EventChar on press, and an EventKeyUp on release. A key that is neither named nor printable reaches the tree as nothing.
func MapText ¶ added in v0.10.0
MapText turns text an input method committed into toolkit events.
A soft keyboard is not a keyboard: it does not send keystrokes, it commits finished text, sometimes several characters at once — a word completion, an emoji, a pasted clipboard. Each rune therefore becomes the pair a printable key produces, EventKeyDown then EventChar, which is exactly what the X11 and wasmbox back-ends emit for a typed character. Every text widget in the toolkit already consumes that pair, so an input method needs no new path through the widget tree.
func MapTextDelete ¶ added in v0.10.0
MapTextDelete turns an input method's "delete n characters before the cursor" into n backspaces, which is how the toolkit's text widgets spell it.
func MapTouch ¶
MapTouch maps one pointer sample to toolkit events.
A contact always yields its touch event — EventTouchStart/Move/End with the pointer id in Event.Code, which is what toolkit's GestureRecognizer and MultiTouchRecognizer key contacts by.
Only the PRIMARY contact also yields a compatibility mouse event. Most widgets, and every widget written before touch existed, listen for EventClick; but a second finger must not fire a second click, or a pinch would read as two taps to every widget in the tree. A browser draws the line in the same place, for the same reason.
The mouse half mirrors the wasmbox and X11 mappings: a press is a click, a move with a finger down is a drag. A touch screen has no hover, so a move with nothing down cannot occur and is mapped to a plain move rather than dropped, keeping a synthetic host (a test, a replay) honest.
func ReadMessage ¶
ReadMessage reads one framed message. It returns io.EOF when the stream ends cleanly between messages, so a caller can tell a closed host from a truncated one.
Types ¶
type A11yElement ¶ added in v0.5.0
type A11yElement struct {
// Class is the android.widget.* class name a screen reader expects for
// this kind of element. Android has no notion of an ARIA role; it decides
// almost everything from the class name of the node.
Class string
// Name is what a screen reader announces.
Name string
// Value is the element's current value, appended after the name for a
// control that has one (a text field's content, a slider's reading).
Value string
// X, Y, W, H is the element's rectangle in surface pixels.
X, Y, W, H int
// Clickable reports whether activating the element does something, i.e.
// whether the host should offer "double-tap to activate".
Clickable bool
}
A11yElement is one element of the accessibility tree as the host sees it: a role it can turn into an Android class name, the text a screen reader reads, and the rectangle to focus, in surface pixels.
func A11yElements ¶ added in v0.5.0
func A11yElements(root toolkit.Widget) []A11yElement
A11yElements turns the widget tree into the flat element list the host serves. Elements with nothing to announce are dropped: an unnamed, valueless element would reach a screen reader as an anonymous stop the user has to swipe past, and a zero-area one cannot be focused at all.
func DecodeA11yTree ¶ added in v0.5.0
func DecodeA11yTree(b []byte) ([]A11yElement, error)
DecodeA11yTree parses a MsgA11yTree body. It exists for the round-trip tests and for any host written in Go; the shipped host is the Java one.
func (A11yElement) Center ¶ added in v0.5.0
func (e A11yElement) Center() (int, int)
Center returns the point to replay an activation at: the middle of the element. A screen reader's activation becomes an ordinary click there, so every behaviour a click has is had by an accessibility action, with no second code path to drift from the first — the rule the AT-SPI bridge already follows.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is an open Android host surface bound to a go-widgets scene. It satisfies window.Backend (Run/Close/Size/String), so a go-widgets app runs through Open→Run unchanged whether the backend is X11, Wayland, Cocoa, Win32, wasmbox or — here — a Java host Activity.
func Dial ¶
Dial connects to the Java host named by $GW_ANDROID_SOCKET and blocks until the host has announced its surface geometry, so the returned Client is ready to paint. It is the Android-environment analogue of openX11/openWayland.
func (*Client) Close ¶
Close ends the session: it tells the host the application is going away, unmaps the framebuffer and ends Run. Idempotent.
func (*Client) Density ¶
Density returns the display density in hundredths, as the host read it from Android's DisplayMetrics (a 3x panel is 300). It is this back-end's spelling of the backing-scale factor.
func (*Client) Insets ¶ added in v0.3.0
Insets returns the margin of the surface the system is drawing over: the status and navigation bars, a display cutout, the soft keyboard. By default the widget tree is laid out inside what they leave; see Client.SetFullBleed.
func (*Client) Repaint ¶
func (c *Client) Repaint()
Repaint asks for a repaint from ANY goroutine, satisfying window.Repainter.
func (*Client) Run ¶
Run binds root, paints the seed frame and blocks while host messages drive the widget tree, until the host closes the surface (or Close ends it). It is the Android analogue of the X11 and Wayland event loops.
func (*Client) SetFullBleed ¶ added in v0.3.0
SetFullBleed lays the widget tree out over the WHOLE surface, insets and all. It is for a root that means to reach under the system bars — a photo, a map, a video — and is then responsible for keeping anything readable out of the area Client.Insets reports.
func (*Client) SetSoftKeyboard ¶ added in v0.10.0
SetSoftKeyboard asks the host to show or hide the on-screen keyboard.
Only the host can: the keyboard is a window, and an application that owns no windows cannot raise one. Showing it also changes the insets — the keyboard covers the bottom of the surface — so the tree is laid out above it without this back-end doing anything special, because an IME inset is an inset like any other (see Client.Insets).
type Config ¶
type Config struct {
// W and H are the surface size in physical pixels.
W, H int
// Density is the display density in hundredths (Android's
// DisplayMetrics.density × 100, so a 3.0x panel arrives as 300). It is the
// Android spelling of the backing-scale factor the Cocoa back-end reads
// from the screen.
Density int
// BufPath is the file the application maps as its framebuffer. The host
// picks it inside the app's own storage, which both processes share.
BufPath string
}
Config is the host's geometry announcement.
func DecodeConfig ¶
DecodeConfig parses a MsgConfig body.
type Insets ¶ added in v0.3.0
type Insets struct{ Left, Top, Right, Bottom int }
Insets is the margin of the surface the system draws over, in pixels.
An Android window is edge-to-edge from API 35: the surface really is the whole screen, and the status bar, the navigation bar, a display cutout and the soft keyboard are painted ON TOP of it rather than shrinking it. So a widget tree laid out to the full surface is correct in size and wrong in practice — its first and last rows are behind the bars. These are the four edges to keep clear.
func DecodeInsets ¶ added in v0.3.0
DecodeInsets parses a MsgInsets body.
type Key ¶
type Key struct {
Action uint8
// Code is the Android KeyEvent key code.
Code int
// Rune is the character the key produced, or 0 for a key that produces
// none (an arrow, a modifier, the back key).
Rune rune
}
Key is one key event.
type Rect ¶
type Rect struct{ X, Y, W, H int }
Rect is a surface-local rectangle in pixels. It mirrors toolkit.Rect but is kept local so the codec stays a leaf with one toolkit dependency (the event model).
