diff --git a/modules/androidforeground/ANDROID_c_additions b/modules/androidforeground/ANDROID_c_additions new file mode 100644 index 00000000..35d11590 --- /dev/null +++ b/modules/androidforeground/ANDROID_c_additions @@ -0,0 +1,39 @@ +/* androidforeground -*-C-*- */ + +int android_start_ln_foreground_service() +{ + JNIEnv *env = GetJNIEnv(); + if (env&&globalObj){ + jclass main_class = (*env)->FindClass(env, "@SYS_PACKAGE_SLASH@/@SYS_APPNAME@"); + jmethodID method = main_class ? (*env)->GetMethodID(env, main_class, "startLnForegroundService", "()V") : NULL; + if(main_class) (*env)->DeleteLocalRef(env, main_class); + if(!method) { + JNI_forward_exception_to_gambit(env); + return -1; + } + (*env)->CallVoidMethod(env, globalObj, method); + (*env)->DeleteLocalRef(env, method); + if(JNI_forward_exception_to_gambit(env)) { return -2; } + return 0; + } +} + +int android_stop_ln_foreground_service() +{ + JNIEnv *env = GetJNIEnv(); + if (env&&globalObj){ + jclass main_class = (*env)->FindClass(env, "@SYS_PACKAGE_SLASH@/@SYS_APPNAME@"); + jmethodID method = main_class ? (*env)->GetMethodID(env, main_class, "stopLnForegroundService", "()V") : NULL; + if(main_class) (*env)->DeleteLocalRef(env, main_class); + if(!method) { + JNI_forward_exception_to_gambit(env); + return -1; + } + (*env)->CallVoidMethod(env, globalObj, method); + (*env)->DeleteLocalRef(env, method); + if(JNI_forward_exception_to_gambit(env)) { return -2; } + return 0; + } +} + +/* EOF androidforeground */ diff --git a/modules/androidforeground/ANDROID_java_activityadditions b/modules/androidforeground/ANDROID_java_activityadditions new file mode 100644 index 00000000..b57396e7 --- /dev/null +++ b/modules/androidforeground/ANDROID_java_activityadditions @@ -0,0 +1,34 @@ +/* androidforeground -*- mode: java; c-basic-offset: 2; -*- */ + +void startLnForegroundService() { + /* API 26+: In order to compile for prior API versions comment out + * the following attempt to disable battery optimizations + */ + @IF_ANDROIDAPI_GT_22@ + if(Build.VERSION.SDK_INT>Build.VERSION_CODES.LOLLIPOP_MR1) { + String pkg=getPackageName(); + PowerManager pm=getSystemService(PowerManager.class); + if(!pm.isIgnoringBatteryOptimizations(pkg)) { + // See also the comment in ANDROID_xml_permissions: Google may + // not like the required + // ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS permission + // being used. + // + // If it is not requested in the permissions file, + // uncomment the following startActivityForResult(...) and + // comment out startActivity(...) in the line after. + + // startActivityForResult(new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS), 0); + startActivity(new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).setData(Uri.parse("package:"+pkg))); + } + } + // end of IF_ANDROIDAPI_GT_22 */ + + startService(new Intent(this, LambdaNativeForegroundService.class)); +} + +void stopLnForegroundService() { + stopService(new Intent(this, LambdaNativeForegroundService.class)); +} + +/* EOF androidforeground */ diff --git a/modules/androidforeground/ANDROID_java_imports b/modules/androidforeground/ANDROID_java_imports new file mode 100644 index 00000000..96c2540e --- /dev/null +++ b/modules/androidforeground/ANDROID_java_imports @@ -0,0 +1,2 @@ +import android.provider.Settings; +import android.os.Build; diff --git a/modules/androidforeground/ANDROID_java_public_LambdaNativeForegroundService.in b/modules/androidforeground/ANDROID_java_public_LambdaNativeForegroundService.in new file mode 100644 index 00000000..5e41ba08 --- /dev/null +++ b/modules/androidforeground/ANDROID_java_public_LambdaNativeForegroundService.in @@ -0,0 +1,78 @@ +/* -*- mode: java; c-basic-offset: 2; -*- */ + +package @SYS_PACKAGE_DOT@; +@IF_ANDROIDAPI_GT_25@ + import android.app.NotificationChannel; +import android.app.NotificationManager; +/* end of IF_ANDROIDAPI_GT_25 */ + +import android.util.Log; +import android.app.Service; +import android.app.Notification; +import android.app.Notification.Builder; + +//import android.support.v4.app.NotificationCompat; + +import android.content.Intent; +import android.os.IBinder; +import android.os.SystemClock; + +public class LambdaNativeForegroundService extends Service { + final static int notificationIsRunningId = 1; + boolean running=true; + Thread backgroundThread; + public LambdaNativeForegroundService() { + } + private Notification.Builder make_notification_template() { + return new Notification.Builder(this) + .setContentTitle(getString(R.string.app_name)) + // .setContentText("TBD") + .setSmallIcon(R.drawable.icon) + // .setLargeIcon(aBitmap) + .setOngoing(true); + } + private void keepAwake_LT_API26() { + startForeground(notificationIsRunningId, make_notification_template().build()); + } + private void keepAwake() { + @IF_ANDROIDAPI_GT_25@ + if(true) { + NotificationManager mgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + assert mgr != null; + NotificationChannel channel = + new NotificationChannel ("@SYS_PACKAGE_DOT@", ".working", NotificationManager.IMPORTANCE_NONE); + mgr.createNotificationChannel(channel); + Notification.Builder mknote = make_notification_template() + .setChannelId("@SYS_PACKAGE_DOT@") + .setCategory(Notification.CATEGORY_SERVICE); + startForeground(notificationIsRunningId, mknote.build()); + return; + } + /* end of IF_ANDROIDAPI_GT_25 */ + keepAwake_LT_API26(); + } + @Override + public IBinder onBind(Intent intent) { + throw new UnsupportedOperationException("Not implemented"); + } + @Override + public void onCreate() { + // Log.d("","LambdaNativeForegroundService created"); + super.onCreate(); + keepAwake(); + } + @Override + public void onStart(Intent intent, int startId) { + // Log.d("","LambdaNativeForegroundService starting"); + } + @Override public int onStartCommand(Intent intent, int flags, int startId) { + super.onStartCommand(intent, flags, startId); + return START_STICKY; + } + @Override + public void onDestroy() { + // running=false; + // Log.d("","LambdaNativeForegroundService stopped"); + } + // This is bound in the main class only!!! native void nativeEvent(int t, int x, int y); +} diff --git a/modules/androidforeground/ANDROID_xml_permissions b/modules/androidforeground/ANDROID_xml_permissions new file mode 100644 index 00000000..bed9072d --- /dev/null +++ b/modules/androidforeground/ANDROID_xml_permissions @@ -0,0 +1,17 @@ + + + + + diff --git a/modules/androidforeground/ANDROID_xml_services b/modules/androidforeground/ANDROID_xml_services new file mode 100644 index 00000000..61a36949 --- /dev/null +++ b/modules/androidforeground/ANDROID_xml_services @@ -0,0 +1 @@ + diff --git a/modules/androidforeground/androidforeground.scm b/modules/androidforeground/androidforeground.scm new file mode 100644 index 00000000..a423b116 --- /dev/null +++ b/modules/androidforeground/androidforeground.scm @@ -0,0 +1,37 @@ +;; this module creates an android service to drive the native lambdanative payload in the background + +;; Usage: +;; +;; (foreground-service! #t) ;; start service +;; +;; (foreground-service! #f) ;; stop service + +(c-declare "int android_start_ln_foreground_service();") +(c-declare "int android_stop_ln_foreground_service();") + +(define foreground-service! + (let ((running #f) + (start! (c-lambda () int " +#if defined(__ANDROID__) + ___return(android_start_ln_foreground_service()); +#else + ___return(0); +#endif +")) + (stop! (c-lambda () int " +#if defined(__ANDROID__) + ___return(android_stop_ln_foreground_service()); +#else + ___return(0); +#endif +"))) + (lambda (flag) + (cond + ((and flag (not running)) + (set! running #t) + (let ((result (start!))) + (when (negative? result) (log-error "foreground-service! failed to start " result)))) + ((and (not flag) running) + (set! running #f) + (let ((result (stop!))) + (when (negative? result) (log-error "foreground-service! failed to stop " result))))))))