@@ -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,8 @@ import (
131139 "gioui.org/unit"
132140)
133141
142+ const foregroundService = "org/gioui/GioForegroundService"
143+
134144type window struct {
135145 callbacks * callbacks
136146
@@ -774,6 +784,59 @@ func getObjectClass(env *C.JNIEnv, obj C.jobject) C.jclass {
774784 return cls
775785}
776786
787+ // getIntent loads class and returns an android.content.Intent or error
788+ func getIntent (env * C.JNIEnv , obj C.jobject , class string ) (C.jobject , error ) {
789+ cls := getObjectClass (env , obj ) // android.app.Application
790+ getClassLoader := getMethodID (env , cls , "getClassLoader" , "()Ljava/lang/ClassLoader;" )
791+ ldr , err := callObjectMethod (env , obj , getClassLoader )
792+ if err != nil {
793+ return 0 , err
794+ }
795+ loadClassMethod := getMethodID (env , getObjectClass (env , ldr ), "loadClass" , "(Ljava/lang/String;)Ljava/lang/Class;" )
796+ loaded , err := callObjectMethod (env , ldr , loadClassMethod , jvalue (javaString (env , class )))
797+ if err != nil {
798+ return 0 , err
799+ }
800+ c := C .CString ("android/content/Intent" )
801+ defer C .free (unsafe .Pointer (c ))
802+ intentClass := C .jni_FindClass (env , c )
803+ if err := exception (env ); err != nil {
804+ return 0 , err
805+ }
806+ intentInit := getMethodID (env , intentClass , "<init>" , "()V" )
807+ intent := C .jni_NewObject (env , intentClass , intentInit )
808+ setClassMethod := getMethodID (env , intentClass , "setClass" , "(Landroid/content/Context;Ljava/lang/Class;)Landroid/content/Intent;" )
809+ _ , err = callObjectMethod (env , intent , setClassMethod , jvalue (obj ), jvalue (C .jclass (loaded )))
810+ if err != nil {
811+ return 0 , err
812+ }
813+
814+ return intent , nil
815+ }
816+
817+ // setforegroundIntentExtras adds the notification text to the intent.
818+ func setforegroundIntentExtras (env * C.JNIEnv , intent C.jobject , id int , name , desc string ) {
819+ cls := getObjectClass (env , intent )
820+ putExtraStringMethod := getMethodID (env , cls , "putExtra" , "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;" )
821+ putExtraIntMethod := getMethodID (env , cls , "putExtra" , "(Ljava/lang/String;I)Landroid/content/Intent;" )
822+ _ , err := callObjectMethod (env , intent , putExtraStringMethod , jvalue (javaString (env , "channelID" )), jvalue (javaString (env , name )))
823+ if err != nil {
824+ panic (err )
825+ }
826+ _ , err = callObjectMethod (env , intent , putExtraStringMethod , jvalue (javaString (env , "channelName" )), jvalue (javaString (env , name )))
827+ if err != nil {
828+ panic (err )
829+ }
830+ _ , err = callObjectMethod (env , intent , putExtraStringMethod , jvalue (javaString (env , "channelDesc" )), jvalue (javaString (env , desc )))
831+ if err != nil {
832+ panic (err )
833+ }
834+ _ , err = callObjectMethod (env , intent , putExtraIntMethod , jvalue (javaString (env , "notificationID" )), jvalue (id ))
835+ if err != nil {
836+ panic (err )
837+ }
838+ }
839+
777840// goString converts the JVM jstring to a Go string.
778841func goString (env * C.JNIEnv , str C.jstring ) string {
779842 if str == 0 {
@@ -951,3 +1014,45 @@ func Java_org_gioui_Gio_scheduleMainFuncs(env *C.JNIEnv, cls C.jclass) {
9511014}
9521015
9531016func (_ ViewEvent ) ImplementsEvent () {}
1017+
1018+ // start a foreground service
1019+ func Java_org_gioui_StartForeground (id int , title , text string ) (chan struct {}, error ) {
1020+ closeChan := make (chan struct {})
1021+ errChan := make (chan error )
1022+
1023+ // get a handle on the startService, stopService methods of GioForegroundService
1024+ // run everything in a goroutine so that start/stop calls are from the same thread
1025+ go func () {
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+ errChan <- err
1031+ return
1032+ }
1033+
1034+ // set the notification text
1035+ setforegroundIntentExtras (env , intent , id , title , text )
1036+
1037+ // locate the methods to start and stop our service
1038+ cls := getObjectClass (env , android .appCtx )
1039+ startServiceMethod := getMethodID (env , cls , "startService" , "(Landroid/content/Intent;)Landroid/content/ComponentName;" )
1040+ stopServiceMethod := getMethodID (env , cls , "stopService" , "(Landroid/content/Intent;)Z" )
1041+
1042+ // Call startService with the above intent
1043+ _ , err = callObjectMethod (env , android .appCtx , startServiceMethod , jvalue (intent ))
1044+ if err != nil {
1045+ errChan <- err
1046+ return
1047+ }
1048+
1049+ // wait for the channel to close, and then halt the foreground service
1050+ // create an intent and set it to the foregroundService
1051+ errChan <- nil
1052+ <- closeChan
1053+ callVoidMethod (env , android .appCtx , stopServiceMethod , jvalue (intent ))
1054+ })
1055+ }()
1056+
1057+ return closeChan , <- errChan
1058+ }
0 commit comments