Skip to content

Commit da51da9

Browse files
docs: merge examples into spoilers with complete code
Combine "Usage Examples" and "Other examples" into a single "Examples" section. Wrap all examples in <details> spoilers. Make main examples complete programs with package/imports/main. Use current package constants (no magic values).
1 parent e7dc81e commit da51da9

1 file changed

Lines changed: 178 additions & 139 deletions

File tree

README.md

Lines changed: 178 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -71,183 +71,224 @@ All three libraries talk to the same Android system services, but through differ
7171
- **Android NDK r28** (28.0.13004108) or later
7272
- **API level 35** (Android 15) target
7373

74-
## Usage Examples
74+
## Examples
7575

76-
### Audio Playback (AAudio)
76+
All types implement idempotent, nil-safe `Close() error`. Error types wrap NDK status codes and work with `errors.Is`.
7777

78-
Configure a stream with the fluent builder, write samples, and clean up:
78+
<details>
79+
<summary>Audio playback (AAudio)</summary>
7980

8081
```go
81-
import "github.com/AndroidGoLab/ndk/audio"
82-
83-
builder, err := audio.NewStreamBuilder()
84-
if err != nil {
85-
log.Fatal(err)
86-
}
87-
defer builder.Close()
88-
89-
builder.
90-
SetDirection(audio.Output).
91-
SetSampleRate(44100).
92-
SetChannelCount(2).
93-
SetFormat(audio.PcmFloat).
94-
SetPerformanceMode(audio.LowLatency).
95-
SetSharingMode(audio.Shared)
96-
97-
stream, err := builder.Open()
98-
if err != nil {
99-
log.Fatal(err)
100-
}
101-
defer stream.Close()
102-
103-
log.Printf("opened: %d Hz, %d ch, burst=%d",
104-
stream.SampleRate(), stream.ChannelCount(), stream.FramesPerBurst())
105-
106-
stream.Start()
107-
defer stream.Stop()
108-
109-
buf := make([]float32, int(stream.FramesPerBurst())*2)
110-
stream.Write(unsafe.Pointer(&buf[0]), stream.FramesPerBurst(), 1_000_000_000)
111-
```
82+
package main
11283

113-
### Camera Discovery
84+
import (
85+
"log"
86+
"unsafe"
11487

115-
List cameras and query capabilities through the Camera2 NDK API:
88+
"github.com/AndroidGoLab/ndk/audio"
89+
)
11690

117-
```go
118-
import "github.com/AndroidGoLab/ndk/camera"
91+
func main() {
92+
builder, err := audio.NewStreamBuilder()
93+
if err != nil {
94+
log.Fatal(err)
95+
}
96+
defer builder.Close()
11997

120-
mgr := camera.NewManager()
121-
defer mgr.Close()
98+
builder.
99+
SetDirection(audio.Output).
100+
SetSampleRate(44100).
101+
SetChannelCount(2).
102+
SetFormat(audio.PcmFloat).
103+
SetPerformanceMode(audio.LowLatency).
104+
SetSharingMode(audio.Shared)
122105

123-
ids, err := mgr.CameraIdList()
124-
if err != nil {
125-
log.Fatal(err) // camera.ErrPermissionDenied if CAMERA not granted
126-
}
106+
stream, err := builder.Open()
107+
if err != nil {
108+
log.Fatal(err)
109+
}
110+
defer stream.Close()
111+
112+
log.Printf("opened: %d Hz, %d ch, burst=%d",
113+
stream.SampleRate(), stream.ChannelCount(), stream.FramesPerBurst())
114+
115+
if err := stream.Start(); err != nil {
116+
log.Fatal(err)
117+
}
118+
defer stream.Stop()
127119

128-
for _, id := range ids {
129-
meta, _ := mgr.GetCameraCharacteristics(id)
130-
orientation := meta.I32At(uint32(camera.SensorOrientation), 0)
131-
log.Printf("camera %s: orientation=%d°", id, orientation)
132-
}
120+
buf := make([]float32, int(stream.FramesPerBurst())*2)
121+
stream.Write(unsafe.Pointer(&buf[0]), stream.FramesPerBurst(), 1_000_000_000)
122+
}
133123
```
134124

135-
### Sensor Querying
125+
</details>
136126

137-
Discover device sensors through the singleton sensor manager:
127+
<details>
128+
<summary>Camera discovery</summary>
138129

139130
```go
140-
import "github.com/AndroidGoLab/ndk/sensor"
131+
package main
141132

142-
mgr := sensor.GetInstance()
143-
accel := mgr.DefaultSensor(sensor.Accelerometer)
133+
import (
134+
"log"
144135

145-
fmt.Printf("Sensor: %s (%s)\n", accel.Name(), accel.Vendor())
146-
fmt.Printf("Resolution: %g, min delay: %d µs\n",
147-
accel.Resolution(), accel.MinDelay())
136+
"github.com/AndroidGoLab/ndk/camera"
137+
)
138+
139+
func main() {
140+
mgr := camera.NewManager()
141+
defer mgr.Close()
142+
143+
ids, err := mgr.CameraIdList()
144+
if err != nil {
145+
log.Fatal(err) // camera.ErrPermissionDenied if CAMERA not granted
146+
}
147+
148+
for _, id := range ids {
149+
meta, _ := mgr.GetCameraCharacteristics(id)
150+
orientation := meta.I32At(uint32(camera.SensorOrientation), 0)
151+
log.Printf("camera %s: orientation=%d°", id, orientation)
152+
}
153+
}
148154
```
149155

150-
### Event Loop (ALooper)
156+
</details>
151157

152-
Prepare a thread-local looper and poll for events:
158+
<details>
159+
<summary>Sensor querying</summary>
153160

154161
```go
155-
import "github.com/AndroidGoLab/ndk/looper"
156-
157-
runtime.LockOSThread()
158-
defer runtime.UnlockOSThread()
159-
160-
lp := looper.Prepare(1) // ALOOPER_PREPARE_ALLOW_NON_CALLBACKS
161-
defer lp.Close()
162-
163-
go func() {
164-
time.Sleep(100 * time.Millisecond)
165-
lp.Acquire()
166-
lp.Wake()
167-
}()
168-
169-
var fd, events int32
170-
var data unsafe.Pointer
171-
switch looper.PollOnce(-1, &fd, &events, &data) {
172-
case -1: // ALOOPER_POLL_WAKE
173-
log.Println("woke up")
174-
case -3: // ALOOPER_POLL_TIMEOUT
175-
log.Println("timed out")
176-
}
162+
package main
163+
164+
import (
165+
"fmt"
166+
167+
"github.com/AndroidGoLab/ndk/sensor"
168+
)
169+
170+
func main() {
171+
mgr := sensor.GetInstance()
172+
accel := mgr.DefaultSensor(sensor.Accelerometer)
173+
174+
fmt.Printf("Sensor: %s (%s)\n", accel.Name(), accel.Vendor())
175+
fmt.Printf("Resolution: %g, min delay: %d µs\n",
176+
accel.Resolution(), accel.MinDelay())
177+
}
177178
```
178179

179-
### Camera Preview (Full Pipeline)
180+
</details>
180181

181-
A complete camera-to-screen example using NativeActivity, EGL, and OpenGL ES:
182+
<details>
183+
<summary>Event loop (ALooper)</summary>
182184

183185
```go
186+
package main
187+
184188
import (
185-
"github.com/AndroidGoLab/ndk/activity"
186-
"github.com/AndroidGoLab/ndk/camera"
187-
"github.com/AndroidGoLab/ndk/egl"
188-
"github.com/AndroidGoLab/ndk/gles2"
189-
"github.com/AndroidGoLab/ndk/surfacetexture"
190-
"github.com/AndroidGoLab/ndk/window"
189+
"log"
190+
"runtime"
191+
"time"
192+
"unsafe"
193+
194+
"github.com/AndroidGoLab/ndk/looper"
191195
)
192196

193-
// 1. Open camera
194-
mgr := camera.NewManager()
195-
defer mgr.Close()
196-
197-
device, err := mgr.OpenCamera(cameraID, camera.DeviceStateCallbacks{
198-
OnDisconnected: func() { log.Println("disconnected") },
199-
OnError: func(code int) { log.Printf("error: %d", code) },
200-
})
201-
defer device.Close()
202-
203-
// 2. Create capture request
204-
request, _ := device.CreateCaptureRequest(camera.Preview)
205-
defer request.Close()
206-
207-
// 3. Wire output surfaces
208-
target, _ := camera.NewOutputTarget(nativeWindow)
209-
request.AddTarget(target)
210-
211-
container, _ := camera.NewSessionOutputContainer()
212-
output, _ := camera.NewSessionOutput(nativeWindow)
213-
container.Add(output)
214-
215-
// 4. Start capture
216-
session, _ := device.CreateCaptureSession(container,
217-
camera.SessionStateCallbacks{
218-
OnReady: func() { log.Println("ready") },
219-
OnActive: func() { log.Println("active") },
220-
})
221-
session.SetRepeatingRequest(request)
222-
223-
// 5. Cleanup (reverse order)
224-
defer session.Close()
225-
defer container.Close()
226-
defer output.Close()
227-
defer target.Close()
197+
func main() {
198+
runtime.LockOSThread()
199+
defer runtime.UnlockOSThread()
200+
201+
lp := looper.Prepare(int32(looper.ALOOPER_PREPARE_ALLOW_NON_CALLBACKS))
202+
defer func() { _ = lp.Close() }()
203+
204+
lp.Acquire()
205+
go func() {
206+
time.Sleep(100 * time.Millisecond)
207+
lp.Wake()
208+
}()
209+
210+
var fd, events int32
211+
var data unsafe.Pointer
212+
result := looper.LOOPER_POLL(looper.PollOnce(-1, &fd, &events, &data))
213+
214+
switch result {
215+
case looper.ALOOPER_POLL_WAKE:
216+
log.Println("woke up")
217+
case looper.ALOOPER_POLL_TIMEOUT:
218+
log.Println("timed out")
219+
}
220+
}
228221
```
229222

230-
See [`examples/camera/display/`](examples/camera/display/) for the complete working application with EGL rendering and permission handling. Build it with `make apk-displaycamera`.
223+
</details>
231224

232-
### Asset Loading
225+
<details>
226+
<summary>Camera preview (full pipeline)</summary>
233227

234-
Read files from the APK's `assets/` directory:
228+
A complete camera-to-screen example using NativeActivity, EGL, and OpenGL ES. See [`examples/camera/display/`](examples/camera/display/) for the full working application. Build it with `make apk-displaycamera`.
235229

236230
```go
237-
import "github.com/AndroidGoLab/ndk/asset"
231+
// Sketch of the camera pipeline (requires NativeActivity context)
232+
233+
mgr := camera.NewManager()
234+
defer mgr.Close()
238235

239-
// mgr obtained from activity.AssetManager
240-
a := mgr.Open("textures/wood.png", asset.Streaming)
241-
defer a.Close()
236+
device, err := mgr.OpenCamera(cameraID, camera.DeviceStateCallbacks{
237+
OnDisconnected: func() { log.Println("disconnected") },
238+
OnError: func(code int) { log.Printf("error: %d", code) },
239+
})
240+
defer device.Close()
242241

243-
size := a.Length()
244-
buf := make([]byte, size)
245-
a.Read(buf)
242+
request, _ := device.CreateCaptureRequest(camera.Preview)
243+
defer request.Close()
244+
245+
target, _ := camera.NewOutputTarget(nativeWindow)
246+
request.AddTarget(target)
247+
248+
container, _ := camera.NewSessionOutputContainer()
249+
output, _ := camera.NewSessionOutput(nativeWindow)
250+
container.Add(output)
251+
252+
session, _ := device.CreateCaptureSession(container,
253+
camera.SessionStateCallbacks{
254+
OnReady: func() { log.Println("ready") },
255+
OnActive: func() { log.Println("active") },
256+
})
257+
session.SetRepeatingRequest(request)
246258
```
247259

248-
All types implement idempotent, nil-safe `Close() error`. Error types wrap NDK status codes and work with `errors.Is`.
260+
</details>
249261

250-
### Other examples
262+
<details>
263+
<summary>Asset loading</summary>
264+
265+
```go
266+
package main
267+
268+
import (
269+
"fmt"
270+
"io"
271+
"unsafe"
272+
273+
"github.com/AndroidGoLab/ndk/asset"
274+
)
275+
276+
func main() {
277+
// mgr obtained from activity.AssetManager in a real NativeActivity app.
278+
// This example documents the API pattern.
279+
var mgr *asset.Manager // = activity.AssetManager(nativeActivity)
280+
281+
a := mgr.Open("textures/wood.png", asset.Streaming)
282+
defer a.Close()
283+
284+
size := a.Length()
285+
buf := make([]byte, size)
286+
_, _ = io.ReadFull(unsafe.NewReader(a), buf)
287+
fmt.Printf("read %d bytes\n", len(buf))
288+
}
289+
```
290+
291+
</details>
251292

252293
<details>
253294
<summary>How to record from the microphone</summary>
@@ -849,9 +890,7 @@ func main() {
849890

850891
</details>
851892

852-
### More examples
853-
854-
For more examples, see: [`examples/`](examples/).
893+
For more examples, see [`examples/`](examples/).
855894

856895
## ndkcli
857896

0 commit comments

Comments
 (0)