Goal: to start a Kivy service on bootup.
It's assumed that you already know how to create a service using Python-for-Android arbitrary service scripts. For example, let it be something simple:
from time import sleep
if __name__ == '__main__':
while True:
print ("myapp service")
sleep(5)
We need to listen to the boot complete event in Android, so add "RECEIVE_BOOT_COMPLETED" in your buildozer.spec file under android.permissions
A BroadcastReceiver is required to listen to this event. As there is no way that I know of for the build to input such a receiver in the AndroidManifest.xml, you need to alter AndroidManifest.tmpl.xml (<PROJECT FOLDER>/.buildozer/android/platform/build/dists/<PROJECT NAME>/templates/AndroidManifest.tmpl.xml
) with the following as a direct child of <application>
:
<receiver android:name=".MyBroadcastReceiver" android:enabled="true" >
<intent-filter><action android:name="android.intent.action.BOOT_COMPLETED" /></intent-filter>
</receiver>
Then build will output it into AndroidManifest.xml anyways. Note the "." in front of "MyBroadcastReceiver" tells Java that it belongs to the package in question.
Because on boot up there is no Python interpreter, the BroadcastReceiver cannot be coded in Python and adb logcat will report Android unable to load the Java class version of it. So it must be written in Java.
Here is MyBroadcastReceiver.java that starts Kivy App (not Service!):
package org.myproject.myapp;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.Context;
import org.kivy.android.PythonActivity;
public class MyBroadcastReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Intent ix = new Intent(context, PythonActivity.class);
ix.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(ix);
}
}
Here org.myproject.myapp is the package in question specified in your build parameters. Note that FLAG_ACTIVITY_NEW_TASK must be used as we are starting from boot and our app hasn't started yet. Place the Java file in ('PROJECT FOLDER>/.buildozer/android/platform/build/dists/PROJECT NAME/src/main/java/PACKAGE DOMAIN/PACKAGE DOMAIN/PROJECT NAME/') folder. The last three folders of your file path should be your package domain + name, eg: org/myproject/myapp/. If all is correct your ServiceMyservice file should also fall under the myapp folder, just for some reference.
Well, this code start the app but not the service. But usually we need to start the background service and not the app. This is also quite simple if you know what to do. Exactly we need to start the service instead of activity and put some extras:
package org.myproject.myapp;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.Context;
import org.myproject.myapp.ServiceMyservice;
public class MyBroadcastReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
String package_root = context.getFilesDir().getAbsolutePath();
String app_root = package_root + "/app";
Intent ix = new Intent(context, ServiceMyservice.class);
ix.putExtra("androidPrivate", package_root);
ix.putExtra("androidArgument", app_root);
ix.putExtra("serviceEntrypoint", "./service/main.py");
ix.putExtra("pythonName", "myservice");
ix.putExtra("pythonHome", app_root);
ix.putExtra("pythonPath", package_root);
ix.putExtra("pythonServiceArgument", app_root+":"+app_root+"/lib");
ix.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startService(ix);
}
}
That's all.
Question: What contents is the ServiceMyservice.java ? Where should it be put? Can't google out an answer. And should I still have to start the service in PythonActivity (i.e. main.py) like this?
service = autoclass('org.test.myapp.ServiceMyservice')
mActivity = autoclass('org.kivy.android.PythonActivity').mActivity
service.start(mActivity, "")