@@ -40,6 +40,14 @@ static jclass jni_GetObjectClass(JNIEnv *env, jobject obj) {
4040 return (*env)->GetObjectClass(env, obj);
4141}
4242
43+ static jclass jni_FindClass(JNIEnv *env, const char *name) {
44+ return (*env)->FindClass(env, name);
45+ }
46+
47+ static jobject jni_NewObject(JNIEnv *env, jclass clazz, jmethodID methodID) {
48+ return (*env)->NewObject(env, clazz, methodID);
49+ }
50+
4351static jmethodID jni_GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
4452 return (*env)->GetMethodID(env, clazz, name, sig);
4553}
@@ -131,6 +139,10 @@ import (
131139 "gioui.org/unit"
132140)
133141
142+ const (
143+ foregroundService = "org/gioui/GioForegroundService"
144+ )
145+
134146type window struct {
135147 callbacks * callbacks
136148
@@ -774,6 +786,59 @@ func getObjectClass(env *C.JNIEnv, obj C.jobject) C.jclass {
774786 return cls
775787}
776788
789+ // getIntent loads class and returns an android.content.Intent or error
790+ func getIntent (env * C.JNIEnv , obj C.jobject , class string ) (C.jobject , error ) {
791+ cls := getObjectClass (env , obj ) // android.app.Application
792+ getClassLoader := getMethodID (env , cls , "getClassLoader" , "()Ljava/lang/ClassLoader;" )
793+ ldr , err := callObjectMethod (env , obj , getClassLoader )
794+ if err != nil {
795+ return 0 , err
796+ }
797+ loadClassMethod := getMethodID (env , getObjectClass (env , ldr ), "loadClass" , "(Ljava/lang/String;)Ljava/lang/Class;" )
798+ loaded , err := callObjectMethod (env , ldr , loadClassMethod , jvalue (javaString (env , class )))
799+ if err != nil {
800+ return 0 , err
801+ }
802+ c := C .CString ("android/content/Intent" )
803+ defer C .free (unsafe .Pointer (c ))
804+ intentClass := C .jni_FindClass (env , c )
805+ if err := exception (env ); err != nil {
806+ return 0 , err
807+ }
808+ intentInit := getMethodID (env , intentClass , "<init>" , "()V" )
809+ intent := C .jni_NewObject (env , intentClass , intentInit )
810+ setClassMethod := getMethodID (env , intentClass , "setClass" , "(Landroid/content/Context;Ljava/lang/Class;)Landroid/content/Intent;" )
811+ _ , err = callObjectMethod (env , intent , setClassMethod , jvalue (obj ), jvalue (C .jclass (loaded )))
812+ if err != nil {
813+ return 0 , err
814+ }
815+
816+ return intent , nil
817+ }
818+
819+ // setforegroundIntentExtras adds the notification text to the intent.
820+ func setforegroundIntentExtras (env * C.JNIEnv , intent C.jobject , id int , name , desc string ) {
821+ cls := getObjectClass (env , intent )
822+ putExtraStringMethod := getMethodID (env , cls , "putExtra" , "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;" )
823+ putExtraIntMethod := getMethodID (env , cls , "putExtra" , "(Ljava/lang/String;I)Landroid/content/Intent;" )
824+ _ , err := callObjectMethod (env , intent , putExtraStringMethod , jvalue (javaString (env , "channelID" )), jvalue (javaString (env , name )))
825+ if err != nil {
826+ panic (err )
827+ }
828+ _ , err = callObjectMethod (env , intent , putExtraStringMethod , jvalue (javaString (env , "channelName" )), jvalue (javaString (env , name )))
829+ if err != nil {
830+ panic (err )
831+ }
832+ _ , err = callObjectMethod (env , intent , putExtraStringMethod , jvalue (javaString (env , "channelDesc" )), jvalue (javaString (env , desc )))
833+ if err != nil {
834+ panic (err )
835+ }
836+ _ , err = callObjectMethod (env , intent , putExtraIntMethod , jvalue (javaString (env , "notificationID" )), jvalue (id ))
837+ if err != nil {
838+ panic (err )
839+ }
840+ }
841+
777842// goString converts the JVM jstring to a Go string.
778843func goString (env * C.JNIEnv , str C.jstring ) string {
779844 if str == 0 {
@@ -951,3 +1016,44 @@ func Java_org_gioui_Gio_scheduleMainFuncs(env *C.JNIEnv, cls C.jclass) {
9511016}
9521017
9531018func (_ ViewEvent ) ImplementsEvent () {}
1019+
1020+ // start a foreground service
1021+ func Java_org_gioui_StartForeground (id int , title , text string ) chan struct {} {
1022+ closeChan := make (chan struct {})
1023+
1024+ // get a handle on the startForeground, stopForeground methods of GioForegroundService
1025+ // run everything in a goroutine so that start/stop calls are from the same thread
1026+ runInJVM (javaVM (), func (env * C.JNIEnv ) {
1027+ // create an intent and set it to the foregroundService
1028+ intent , err := getIntent (env , android .appCtx , foregroundService )
1029+ if err != nil {
1030+ panic (err )
1031+ }
1032+
1033+ // set the notification text
1034+ setforegroundIntentExtras (env , intent , id , title , text )
1035+
1036+ // locate the methods to start our service
1037+ cls := getObjectClass (env , android .appCtx )
1038+ startForegroundServiceMethod := getMethodID (env , cls , "startForegroundService" , "(Landroid/content/Intent;)Landroid/content/ComponentName;" )
1039+ // Call startForegroundService with the above intent
1040+ callObjectMethod (env , android .appCtx , startForegroundServiceMethod , jvalue (intent ))
1041+ })
1042+
1043+ go func () {
1044+ // wait for the channel to close, and then halt the foreground service
1045+ <- closeChan
1046+ runInJVM (javaVM (), func (env * C.JNIEnv ) {
1047+ // create an intent and set it to the foregroundService
1048+ intent , err := getIntent (env , android .appCtx , foregroundService )
1049+ if err != nil {
1050+ panic (err )
1051+ }
1052+ cls := getObjectClass (env , android .appCtx )
1053+ stopService := getMethodID (env , cls , "stopService" , "(Landroid/content/Intent;)Z" )
1054+ callVoidMethod (env , android .appCtx , stopService , jvalue (intent ))
1055+ })
1056+ }()
1057+
1058+ return closeChan
1059+ }
0 commit comments