Documentation
¶
Overview ¶
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(toolkit.Widget) error
- func (c *Client) SetFullBleed(bool)
- func (c *Client) SetSoftKeyboard(bool)
- func (c *Client) SetTitle(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 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{}
Client is the unavailable-here shape of the Android host surface. The host protocol needs a Linux abstract socket and a shared mapping, so there is nothing to connect to anywhere else — but the type and its method set exist on every GOOS so an application using this back-end still cross-builds and still vets, exactly as go-widgets/window's open_other.go keeps the native back-ends' entry point compiling everywhere.
func (*Client) Insets ¶ added in v0.3.0
Insets reports nothing covering a surface that does not exist.
func (*Client) SetFullBleed ¶ added in v0.3.0
SetFullBleed has no surface to bleed over.
func (*Client) SetSoftKeyboard ¶ added in v0.10.0
SetSoftKeyboard has no host to ask for a keyboard.
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).
