Skip to content

Commit 703f204

Browse files
xaionaro@dx.centerxaionaro@dx.center
authored andcommitted
refactor: move exampleui to examples/common/ui, rewrite stub examples, fix JNI header warning
- Move exampleui/ to examples/common/ui/ (package renamed to `ui`) - Update all 53 example import paths from exampleui to ui - Rewrite 22 description-only examples to actually call their APIs - Fix notification example to post a real notification with sound - Fix incompatible-pointer-types warning: detect JDK vs NDK jni.h via _JAVASOFT_JNI_H_ guard and cast AttachCurrentThread's env parameter correctly for each header - Remove -Wno-incompatible-pointer-types workaround from apk.mk
1 parent d023167 commit 703f204

59 files changed

Lines changed: 3510 additions & 1404 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

capi/cgo_vtable_dispatch.h

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,26 @@
55

66
#include <jni.h>
77

8+
// The JDK and Android NDK disagree on the type of AttachCurrentThread's
9+
// env-out parameter: JDK uses void**, NDK uses JNIEnv**. Detect which
10+
// header was included and cast accordingly. The JDK header defines
11+
// _JAVASOFT_JNI_H_ while the NDK header does not.
12+
#ifdef _JAVASOFT_JNI_H_
13+
#define JNI_ENVPP_CAST(x) ((void**)(x))
14+
#else
15+
#define JNI_ENVPP_CAST(x) (x)
16+
#endif
17+
818
static inline jobject jni_AllocObject(JNIEnv* env, jclass p0) {
919
return (*env)->AllocObject(env, p0);
1020
}
1121

1222
static inline jint jni_AttachCurrentThread(JavaVM* vm, JNIEnv** p0, void* p1) {
13-
#ifdef __ANDROID__
14-
return (*vm)->AttachCurrentThread(vm, p0, p1);
15-
#else
16-
return (*vm)->AttachCurrentThread(vm, (void**)p0, p1);
17-
#endif
23+
return (*vm)->AttachCurrentThread(vm, JNI_ENVPP_CAST(p0), p1);
1824
}
1925

2026
static inline jint jni_AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p0, void* p1) {
21-
#ifdef __ANDROID__
22-
return (*vm)->AttachCurrentThreadAsDaemon(vm, p0, p1);
23-
#else
24-
return (*vm)->AttachCurrentThreadAsDaemon(vm, (void**)p0, p1);
25-
#endif
27+
return (*vm)->AttachCurrentThreadAsDaemon(vm, JNI_ENVPP_CAST(p0), p1);
2628
}
2729

2830
static inline jboolean jni_CallBooleanMethodA(JNIEnv* env, jobject p0, jmethodID p1, const jvalue* p2) {

examples/accounts/main.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,16 @@ import (
2121
"github.com/AndroidGoLab/jni"
2222
"github.com/AndroidGoLab/jni/accounts"
2323
"github.com/AndroidGoLab/jni/capi"
24-
"github.com/AndroidGoLab/jni/exampleui"
24+
"github.com/AndroidGoLab/jni/examples/common/ui"
2525
)
2626

2727
func main() {}
2828

29-
func init() { exampleui.Register(run) }
29+
func init() { ui.Register(run) }
3030

3131
//export ANativeActivity_onCreate
3232
func ANativeActivity_onCreate(activity *C.ANativeActivity, savedState unsafe.Pointer, savedStateSize C.size_t) {
33-
exampleui.OnCreate(
33+
ui.OnCreate(
3434
jni.VMFromPtr(unsafe.Pointer(activity.vm)),
3535
jni.ObjectFromRef(capi.Object(uintptr(unsafe.Pointer(activity.clazz)))),
3636
)
@@ -39,18 +39,18 @@ func ANativeActivity_onCreate(activity *C.ANativeActivity, savedState unsafe.Poi
3939

4040
//export goOnResume
4141
func goOnResume(activity *C.ANativeActivity) {
42-
exampleui.OnResume(
42+
ui.OnResume(
4343
jni.ObjectFromRef(capi.Object(uintptr(unsafe.Pointer(activity.clazz)))),
4444
)
4545
}
4646

4747
//export goOnNativeWindowCreated
4848
func goOnNativeWindowCreated(activity *C.ANativeActivity, window *C.ANativeWindow) {
49-
exampleui.OnNativeWindowCreated(unsafe.Pointer(window))
49+
ui.OnNativeWindowCreated(unsafe.Pointer(window))
5050
}
5151

5252
func run(vm *jni.VM, output *bytes.Buffer) error {
53-
ctx, err := exampleui.GetAppContext(vm)
53+
ctx, err := ui.GetAppContext(vm)
5454
if err != nil {
5555
return fmt.Errorf("get context: %w", err)
5656
}

examples/alarm/main.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import (
2525
"github.com/AndroidGoLab/jni/app/alarm"
2626
"github.com/AndroidGoLab/jni/app/notification"
2727
"github.com/AndroidGoLab/jni/capi"
28-
"github.com/AndroidGoLab/jni/exampleui"
28+
"github.com/AndroidGoLab/jni/examples/common/ui"
2929
"github.com/AndroidGoLab/jni/media/ringtone"
3030
)
3131

@@ -45,12 +45,12 @@ const (
4545

4646
func main() {}
4747

48-
func init() { exampleui.Register(run) }
48+
func init() { ui.Register(run) }
4949

5050
//export ANativeActivity_onCreate
5151
func ANativeActivity_onCreate(activity *C.ANativeActivity, savedState unsafe.Pointer, savedStateSize C.size_t) {
5252
println("ANativeActivity_onCreate called")
53-
exampleui.OnCreate(
53+
ui.OnCreate(
5454
jni.VMFromPtr(unsafe.Pointer(activity.vm)),
5555
jni.ObjectFromRef(capi.Object(uintptr(unsafe.Pointer(activity.clazz)))),
5656
)
@@ -59,18 +59,18 @@ func ANativeActivity_onCreate(activity *C.ANativeActivity, savedState unsafe.Poi
5959

6060
//export goOnResume
6161
func goOnResume(activity *C.ANativeActivity) {
62-
exampleui.OnResume(
62+
ui.OnResume(
6363
jni.ObjectFromRef(capi.Object(uintptr(unsafe.Pointer(activity.clazz)))),
6464
)
6565
}
6666

6767
//export goOnNativeWindowCreated
6868
func goOnNativeWindowCreated(activity *C.ANativeActivity, window *C.ANativeWindow) {
69-
exampleui.OnNativeWindowCreated(unsafe.Pointer(window))
69+
ui.OnNativeWindowCreated(unsafe.Pointer(window))
7070
}
7171

7272
func run(vm *jni.VM, output *bytes.Buffer) error {
73-
ctx, err := exampleui.GetAppContext(vm)
73+
ctx, err := ui.GetAppContext(vm)
7474
if err != nil {
7575
return fmt.Errorf("get context: %w", err)
7676
}
@@ -129,7 +129,7 @@ func run(vm *jni.VM, output *bytes.Buffer) error {
129129
fmt.Fprintf(output, "ringtone error: %v\n", err)
130130
}
131131
fmt.Fprintf(output, "alarm fired!\n")
132-
exampleui.RenderOutput()
132+
ui.RenderOutput()
133133
}()
134134

135135
return nil

examples/apk.mk

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,15 @@ endif
7070
$(BUILD)/lib/arm64-v8a/libexample.so: main.go
7171
@mkdir -p $(dir $@)
7272
cd ../.. && CGO_ENABLED=1 GOOS=android GOARCH=arm64 CC=$(CC_ARM64) \
73-
CGO_CFLAGS="-Wno-incompatible-pointer-types" CGO_LDFLAGS="-llog -landroid" \
73+
CGO_LDFLAGS="-llog -landroid" \
7474
go build -buildmode=c-shared \
7575
-o examples/$(EXAMPLE_NAME)/$@ \
7676
./examples/$(EXAMPLE_NAME)/
7777
@rm -f $(@:.so=.h)
7878
$(BUILD)/lib/x86_64/libexample.so: main.go
7979
@mkdir -p $(dir $@)
8080
cd ../.. && CGO_ENABLED=1 GOOS=android GOARCH=amd64 CC=$(CC_AMD64) \
81-
CGO_CFLAGS="-Wno-incompatible-pointer-types" CGO_LDFLAGS="-llog -landroid" \
81+
CGO_LDFLAGS="-llog -landroid" \
8282
go build -buildmode=c-shared \
8383
-o examples/$(EXAMPLE_NAME)/$@ \
8484
./examples/$(EXAMPLE_NAME)/

examples/app_framework/main.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,17 @@ import (
2323

2424
"github.com/AndroidGoLab/jni"
2525
"github.com/AndroidGoLab/jni/capi"
26-
"github.com/AndroidGoLab/jni/exampleui"
26+
"github.com/AndroidGoLab/jni/examples/common/ui"
2727
"github.com/AndroidGoLab/jni/app"
2828
)
2929

3030
func main() {}
3131

32-
func init() { exampleui.Register(run) }
32+
func init() { ui.Register(run) }
3333

3434
//export ANativeActivity_onCreate
3535
func ANativeActivity_onCreate(activity *C.ANativeActivity, savedState unsafe.Pointer, savedStateSize C.size_t) {
36-
exampleui.OnCreate(
36+
ui.OnCreate(
3737
jni.VMFromPtr(unsafe.Pointer(activity.vm)),
3838
jni.ObjectFromRef(capi.Object(uintptr(unsafe.Pointer(activity.clazz)))),
3939
)
@@ -42,19 +42,19 @@ func ANativeActivity_onCreate(activity *C.ANativeActivity, savedState unsafe.Poi
4242

4343
//export goOnResume
4444
func goOnResume(activity *C.ANativeActivity) {
45-
exampleui.OnResume(
45+
ui.OnResume(
4646
jni.ObjectFromRef(capi.Object(uintptr(unsafe.Pointer(activity.clazz)))),
4747
)
4848
}
4949

5050
//export goOnNativeWindowCreated
5151
func goOnNativeWindowCreated(activity *C.ANativeActivity, window *C.ANativeWindow) {
52-
exampleui.OnNativeWindowCreated(unsafe.Pointer(window))
52+
ui.OnNativeWindowCreated(unsafe.Pointer(window))
5353
}
5454

5555
func run(vm *jni.VM, output *bytes.Buffer) error {
5656
// --- Context ---
57-
ctx, err := exampleui.GetAppContext(vm)
57+
ctx, err := ui.GetAppContext(vm)
5858
if err != nil {
5959
return fmt.Errorf("get context: %w", err)
6060
}

examples/audiomanager/main.go

Lines changed: 100 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
//go:build android
22

3-
// Command audiomanager demonstrates the Android AudioManager for
4-
// controlling audio routing, volume, and device enumeration. It is
5-
// built as a c-shared library and packaged into an APK using the
6-
// shared apk.mk infrastructure.
7-
//
8-
// The audiomanager package wraps android.media.AudioManager.
9-
// Most methods are unexported (getStreamVolume, setStreamVolume, etc.)
10-
// and are intended to be wrapped by higher-level helpers.
3+
// Command audiomanager demonstrates the Android AudioManager by
4+
// querying live audio state: stream volumes, ringer mode, audio mode,
5+
// and boolean flags such as isMusicActive and isSpeakerphoneOn.
116
package main
127

138
/*
@@ -26,17 +21,17 @@ import (
2621

2722
"github.com/AndroidGoLab/jni"
2823
"github.com/AndroidGoLab/jni/capi"
29-
"github.com/AndroidGoLab/jni/exampleui"
24+
"github.com/AndroidGoLab/jni/examples/common/ui"
3025
"github.com/AndroidGoLab/jni/media/audiomanager"
3126
)
3227

3328
func main() {}
3429

35-
func init() { exampleui.Register(run) }
30+
func init() { ui.Register(run) }
3631

3732
//export ANativeActivity_onCreate
3833
func ANativeActivity_onCreate(activity *C.ANativeActivity, savedState unsafe.Pointer, savedStateSize C.size_t) {
39-
exampleui.OnCreate(
34+
ui.OnCreate(
4035
jni.VMFromPtr(unsafe.Pointer(activity.vm)),
4136
jni.ObjectFromRef(capi.Object(uintptr(unsafe.Pointer(activity.clazz)))),
4237
)
@@ -45,18 +40,18 @@ func ANativeActivity_onCreate(activity *C.ANativeActivity, savedState unsafe.Poi
4540

4641
//export goOnResume
4742
func goOnResume(activity *C.ANativeActivity) {
48-
exampleui.OnResume(
43+
ui.OnResume(
4944
jni.ObjectFromRef(capi.Object(uintptr(unsafe.Pointer(activity.clazz)))),
5045
)
5146
}
5247

5348
//export goOnNativeWindowCreated
5449
func goOnNativeWindowCreated(activity *C.ANativeActivity, window *C.ANativeWindow) {
55-
exampleui.OnNativeWindowCreated(unsafe.Pointer(window))
50+
ui.OnNativeWindowCreated(unsafe.Pointer(window))
5651
}
5752

5853
func run(vm *jni.VM, output *bytes.Buffer) error {
59-
ctx, err := exampleui.GetAppContext(vm)
54+
ctx, err := ui.GetAppContext(vm)
6055
if err != nil {
6156
return fmt.Errorf("get context: %w", err)
6257
}
@@ -68,39 +63,97 @@ func run(vm *jni.VM, output *bytes.Buffer) error {
6863
}
6964
defer mgr.Close()
7065

71-
// Audio device type constants from android.media.AudioDeviceInfo.
72-
fmt.Fprintf(output, "device types: speaker=%d, mic=%d, wired_headset=%d, wired_headphones=%d, bluetooth_a2dp=%d, usb=%d, hdmi=%d\n",
73-
audiomanager.TypeBuiltinSpeaker, audiomanager.TypeBuiltinMic,
74-
audiomanager.TypeWiredHeadset, audiomanager.TypeWiredHeadphones,
75-
audiomanager.TypeBluetoothA2dp,
76-
audiomanager.TypeUsbDevice, audiomanager.TypeHdmi)
77-
78-
// Audio focus request constants.
79-
fmt.Fprintf(output, "focus gain: gain=%d, transient=%d, transient_duck=%d\n",
80-
audiomanager.AudiofocusGain, audiomanager.AudiofocusGainTransient,
81-
audiomanager.AudiofocusGainTransientMayDuck)
82-
fmt.Fprintf(output, "focus loss: loss=%d, transient=%d, transient_duck=%d\n",
83-
audiomanager.AudiofocusLoss, audiomanager.AudiofocusLossTransient,
84-
audiomanager.AudiofocusLossTransientCanDuck)
85-
86-
// Stream type constants for volume control.
87-
fmt.Fprintf(output, "streams: voice_call=%d, system=%d, ring=%d, music=%d, alarm=%d, notification=%d\n",
88-
audiomanager.StreamVoiceCall, audiomanager.StreamSystem,
89-
audiomanager.StreamRing, audiomanager.StreamMusic,
90-
audiomanager.StreamAlarm, audiomanager.StreamNotification)
91-
92-
// Device filter constants for getDevices.
93-
fmt.Fprintf(output, "device filters: input=%d, output=%d, all=%d\n",
94-
audiomanager.GetDevicesInputs, audiomanager.GetDevicesOutputs, audiomanager.GetDevicesAll)
95-
96-
// The Manager also provides unexported methods for:
97-
// - getDevicesRaw(flags) - enumerate audio devices
98-
// - getStreamVolume(streamType) / setStreamVolume(streamType, index, flags)
99-
// - getStreamMaxVolume(streamType)
100-
// - isSpeakerphoneOn() / setSpeakerphoneOn(on)
101-
// - requestAudioFocusRaw(request) / abandonAudioFocusRequest(request)
102-
// - registerAudioDeviceCallback / unregisterAudioDeviceCallback
103-
fmt.Fprintln(output, "AudioManager created successfully")
66+
fmt.Fprintln(output, "=== AudioManager ===")
67+
68+
// Stream volumes.
69+
type streamInfo struct {
70+
name string
71+
constant int32
72+
}
73+
streams := []streamInfo{
74+
{"voice_call", int32(audiomanager.StreamVoiceCall)},
75+
{"system", int32(audiomanager.StreamSystem)},
76+
{"ring", int32(audiomanager.StreamRing)},
77+
{"music", int32(audiomanager.StreamMusic)},
78+
{"alarm", int32(audiomanager.StreamAlarm)},
79+
{"notification", int32(audiomanager.StreamNotification)},
80+
}
81+
for _, s := range streams {
82+
vol, err := mgr.GetStreamVolume(s.constant)
83+
if err != nil {
84+
fmt.Fprintf(output, " %s volume: error: %v\n", s.name, err)
85+
continue
86+
}
87+
maxVol, err := mgr.GetStreamMaxVolume(s.constant)
88+
if err != nil {
89+
fmt.Fprintf(output, " %s volume: %d (max: error: %v)\n", s.name, vol, err)
90+
continue
91+
}
92+
minVol, _ := mgr.GetStreamMinVolume(s.constant)
93+
muted, _ := mgr.IsStreamMute(s.constant)
94+
fmt.Fprintf(output, " %s: vol=%d min=%d max=%d muted=%v\n", s.name, vol, minVol, maxVol, muted)
95+
}
96+
97+
// Ringer mode.
98+
ringerMode, err := mgr.GetRingerMode()
99+
if err != nil {
100+
fmt.Fprintf(output, "ringer mode: error: %v\n", err)
101+
} else {
102+
name := "unknown"
103+
switch int(ringerMode) {
104+
case audiomanager.RingerModeSilent:
105+
name = "silent"
106+
case audiomanager.RingerModeVibrate:
107+
name = "vibrate"
108+
case audiomanager.RingerModeNormal:
109+
name = "normal"
110+
}
111+
fmt.Fprintf(output, "ringer mode: %s (%d)\n", name, ringerMode)
112+
}
113+
114+
// Audio mode.
115+
mode, err := mgr.GetMode()
116+
if err != nil {
117+
fmt.Fprintf(output, "audio mode: error: %v\n", err)
118+
} else {
119+
name := "unknown"
120+
switch int(mode) {
121+
case audiomanager.ModeNormal:
122+
name = "normal"
123+
case audiomanager.ModeRingtone:
124+
name = "ringtone"
125+
case audiomanager.ModeInCall:
126+
name = "in_call"
127+
case audiomanager.ModeInCommunication:
128+
name = "in_communication"
129+
case audiomanager.ModeCallScreening:
130+
name = "call_screening"
131+
}
132+
fmt.Fprintf(output, "audio mode: %s (%d)\n", name, mode)
133+
}
134+
135+
// Boolean flags.
136+
type boolQuery struct {
137+
name string
138+
fn func() (bool, error)
139+
}
140+
boolQueries := []boolQuery{
141+
{"music active", mgr.IsMusicActive},
142+
{"speakerphone on", mgr.IsSpeakerphoneOn},
143+
{"mic mute", mgr.IsMicrophoneMute},
144+
{"bluetooth A2DP on", mgr.IsBluetoothA2dpOn},
145+
{"bluetooth SCO on", mgr.IsBluetoothScoOn},
146+
{"wired headset on", mgr.IsWiredHeadsetOn},
147+
{"volume fixed", mgr.IsVolumeFixed},
148+
}
149+
for _, q := range boolQueries {
150+
val, err := q.fn()
151+
if err != nil {
152+
fmt.Fprintf(output, "%s: error: %v\n", q.name, err)
153+
} else {
154+
fmt.Fprintf(output, "%s: %v\n", q.name, val)
155+
}
156+
}
104157

105158
return nil
106159
}

0 commit comments

Comments
 (0)