206 lines
7.3 KiB
Java
206 lines
7.3 KiB
Java
package org.kivy.android;
|
|
|
|
import android.os.Build;
|
|
import java.lang.reflect.Method;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import android.app.Service;
|
|
import android.os.IBinder;
|
|
import android.os.Bundle;
|
|
import android.content.Intent;
|
|
import android.content.Context;
|
|
import android.util.Log;
|
|
import android.app.Notification;
|
|
import android.app.PendingIntent;
|
|
import android.os.Process;
|
|
import java.io.File;
|
|
|
|
//imports for channel definition
|
|
import android.app.NotificationManager;
|
|
import android.app.NotificationChannel;
|
|
import android.graphics.Color;
|
|
import android.graphics.BitmapFactory;
|
|
import android.graphics.Bitmap;
|
|
import android.graphics.drawable.Icon;
|
|
|
|
import android.net.wifi.WifiManager;
|
|
import android.net.wifi.WifiManager.MulticastLock;
|
|
|
|
public class PythonService extends Service implements Runnable {
|
|
|
|
// Thread for Python code
|
|
private Thread pythonThread = null;
|
|
|
|
// Python environment variables
|
|
private String androidPrivate;
|
|
private String androidArgument;
|
|
private String pythonName;
|
|
private String pythonHome;
|
|
private String pythonPath;
|
|
private String serviceEntrypoint;
|
|
// Argument to pass to Python code,
|
|
private String pythonServiceArgument;
|
|
|
|
|
|
public static PythonService mService = null;
|
|
private Intent startIntent = null;
|
|
|
|
private boolean autoRestartService = false;
|
|
|
|
public void setAutoRestartService(boolean restart) {
|
|
autoRestartService = restart;
|
|
}
|
|
|
|
public int startType() {
|
|
return START_NOT_STICKY;
|
|
}
|
|
|
|
@Override
|
|
public IBinder onBind(Intent arg0) {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public void onCreate() {
|
|
super.onCreate();
|
|
}
|
|
|
|
@Override
|
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
if (pythonThread != null) {
|
|
Log.v("python service", "service exists, do not start again");
|
|
return startType();
|
|
}
|
|
//intent is null if OS restarts a STICKY service
|
|
if (intent == null) {
|
|
Context context = getApplicationContext();
|
|
intent = getThisDefaultIntent(context, "");
|
|
}
|
|
|
|
startIntent = intent;
|
|
Bundle extras = intent.getExtras();
|
|
androidPrivate = extras.getString("androidPrivate");
|
|
androidArgument = extras.getString("androidArgument");
|
|
serviceEntrypoint = extras.getString("serviceEntrypoint");
|
|
pythonName = extras.getString("pythonName");
|
|
pythonHome = extras.getString("pythonHome");
|
|
pythonPath = extras.getString("pythonPath");
|
|
boolean serviceStartAsForeground = (
|
|
extras.getString("serviceStartAsForeground").equals("true")
|
|
);
|
|
pythonServiceArgument = extras.getString("pythonServiceArgument");
|
|
pythonThread = new Thread(this);
|
|
pythonThread.start();
|
|
|
|
if (serviceStartAsForeground) {
|
|
doStartForeground(extras);
|
|
}
|
|
|
|
return startType();
|
|
}
|
|
|
|
protected int getServiceId() {
|
|
return 1;
|
|
}
|
|
|
|
protected Intent getThisDefaultIntent(Context ctx, String pythonServiceArgument) {
|
|
return null;
|
|
}
|
|
|
|
protected void doStartForeground(Bundle extras) {
|
|
String serviceTitle = extras.getString("serviceTitle");
|
|
String serviceDescription = extras.getString("serviceDescription");
|
|
Notification notification;
|
|
Context context = getApplicationContext();
|
|
Intent contextIntent = new Intent(context, PythonActivity.class);
|
|
PendingIntent pIntent = PendingIntent.getActivity(context, 0, contextIntent,
|
|
PendingIntent.FLAG_UPDATE_CURRENT);
|
|
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
|
notification = new Notification(
|
|
context.getApplicationInfo().icon, serviceTitle, System.currentTimeMillis());
|
|
try {
|
|
// prevent using NotificationCompat, this saves 100kb on apk
|
|
Method func = notification.getClass().getMethod(
|
|
"setLatestEventInfo", Context.class, CharSequence.class,
|
|
CharSequence.class, PendingIntent.class);
|
|
func.invoke(notification, context, serviceTitle, serviceDescription, pIntent);
|
|
} catch (NoSuchMethodException | IllegalAccessException |
|
|
IllegalArgumentException | InvocationTargetException e) {
|
|
}
|
|
} else {
|
|
// for android 8+ we need to create our own channel
|
|
// https://stackoverflow.com/questions/47531742/startforeground-fail-after-upgrade-to-android-8-1
|
|
String NOTIFICATION_CHANNEL_ID = "io.unsigned.sideband.reticulum";
|
|
String channelName = "Background Service";
|
|
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName,
|
|
NotificationManager.IMPORTANCE_NONE);
|
|
|
|
chan.setLightColor(Color.BLUE);
|
|
chan.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
|
|
chan.setShowBadge(false);
|
|
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
|
manager.createNotificationChannel(chan);
|
|
|
|
Notification.Builder builder = new Notification.Builder(context, NOTIFICATION_CHANNEL_ID);
|
|
builder.setContentTitle("Sideband Active");
|
|
// builder.setContentText("Reticulum Active");
|
|
builder.setContentIntent(pIntent);
|
|
// builder.setOngoing(true);
|
|
|
|
// TODO: Generalise this
|
|
Bitmap icon_bitmap = BitmapFactory.decodeFile("/data/user/0/io.unsigned.sideband/files/app/assets/notification_icon.png");
|
|
Icon service_icon = Icon.createWithBitmap(icon_bitmap);
|
|
// builder.setSmallIcon(context.getApplicationInfo().icon);
|
|
builder.setSmallIcon(service_icon);
|
|
notification = builder.build();
|
|
}
|
|
startForeground(getServiceId(), notification);
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
super.onDestroy();
|
|
pythonThread = null;
|
|
if (autoRestartService && startIntent != null) {
|
|
Log.v("python service", "service restart requested");
|
|
startService(startIntent);
|
|
}
|
|
Process.killProcess(Process.myPid());
|
|
}
|
|
|
|
/**
|
|
* Stops the task gracefully when killed.
|
|
* Calling stopSelf() will trigger a onDestroy() call from the system.
|
|
*/
|
|
@Override
|
|
public void onTaskRemoved(Intent rootIntent) {
|
|
super.onTaskRemoved(rootIntent);
|
|
//sticky servcie runtime/restart is managed by the OS. leave it running when app is closed
|
|
if (startType() != START_STICKY) {
|
|
//stopSelf();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void run(){
|
|
String app_root = getFilesDir().getAbsolutePath() + "/app";
|
|
File app_root_file = new File(app_root);
|
|
PythonUtil.loadLibraries(app_root_file,
|
|
new File(getApplicationInfo().nativeLibraryDir));
|
|
this.mService = this;
|
|
nativeStart(
|
|
androidPrivate, androidArgument,
|
|
serviceEntrypoint, pythonName,
|
|
pythonHome, pythonPath,
|
|
pythonServiceArgument);
|
|
stopSelf();
|
|
}
|
|
|
|
// Native part
|
|
public static native void nativeStart(
|
|
String androidPrivate, String androidArgument,
|
|
String serviceEntrypoint, String pythonName,
|
|
String pythonHome, String pythonPath,
|
|
String pythonServiceArgument);
|
|
}
|