Skip to content

Enable services with SDL2 bootstrap #578

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 13, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion pythonforandroid/bootstraps/sdl2/build/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ def make_package(args):
os.unlink('assets/private.mp3')

# In order to speedup import and initial depack,
# construct a python27.zip if not using CrystaX's pre-zipped package
# construct a python27.zip
make_python_zip()

# Package up the private and public data.
Expand Down Expand Up @@ -281,10 +281,16 @@ def make_package(args):
with open(args.intent_filters) as fd:
args.intent_filters = fd.read()

service = False
service_main = join(realpath(args.private), 'service', 'main.py')
if os.path.exists(service_main) or os.path.exists(service_main + 'o'):
service = True

render(
'AndroidManifest.tmpl.xml',
'AndroidManifest.xml',
args=args,
service=service,
)

render(
Expand All @@ -311,6 +317,7 @@ def make_package(args):

def parse_args(args=None):
global BLACKLIST_PATTERNS, WHITELIST_PATTERNS
default_android_api = 12
import argparse
ap = argparse.ArgumentParser(description='''\
Package a Python application for Android.
Expand Down Expand Up @@ -367,18 +374,35 @@ def parse_args(args=None):
help=('Add a Java .jar to the libs, so you can access its '
'classes with pyjnius. You can specify this '
'argument more than once to include multiple jars'))
ap.add_argument('--sdk', dest='sdk_version', default=-1,
type=int, help=('Android SDK version to use. Default to '
'the value of minsdk'))
ap.add_argument('--minsdk', dest='min_sdk_version',
default=default_android_api, type=int,
help=('Minimum Android SDK version to use. Default to '
'the value of ANDROIDAPI, or {} if not set'
.format(default_android_api)))
ap.add_argument('--intent-filters', dest='intent_filters',
help=('Add intent-filters xml rules to the '
'AndroidManifest.xml file. The argument is a '
'filename containing xml. The filename should be '
'located relative to the python-for-android '
'directory'))
ap.add_argument('--with-billing', dest='billing_pubkey',
help='If set, the billing service will be added (not implemented)')

if args is None:
args = sys.argv[1:]
args = ap.parse_args(args)
args.ignore_path = []

if args.billing_pubkey:
print('Billing not yet supported in sdl2 bootstrap!')
exit(1)

if args.sdk_version == -1:
args.sdk_version = args.min_sdk_version

if args.permissions is None:
args.permissions = []

Expand Down
56 changes: 28 additions & 28 deletions pythonforandroid/bootstraps/sdl2/build/jni/src/start.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ int main(int argc, char *argv[]) {
LOG("Initialize Python for Android");
/* env_argument = "/data/data/org.kivy.android/files"; */
env_argument = getenv("ANDROID_ARGUMENT");
/* setenv("ANDROID_APP_PATH", env_argument, 1); */
setenv("ANDROID_APP_PATH", env_argument, 1);

/* setenv("ANDROID_ARGUMENT", env_argument, 1); */
/* setenv("ANDROID_PRIVATE", env_argument, 1); */
Expand Down Expand Up @@ -314,32 +314,32 @@ int main(int argc, char *argv[]) {
return ret;
}

/* JNIEXPORT void JNICALL JAVA_EXPORT_NAME(PythonService_nativeStart) ( JNIEnv* env, jobject thiz, */
/* jstring j_android_private, */
/* jstring j_android_argument, */
/* jstring j_python_home, */
/* jstring j_python_path, */
/* jstring j_arg ) */
/* { */
/* jboolean iscopy; */
/* const char *android_private = (*env)->GetStringUTFChars(env, j_android_private, &iscopy); */
/* const char *android_argument = (*env)->GetStringUTFChars(env, j_android_argument, &iscopy); */
/* const char *python_home = (*env)->GetStringUTFChars(env, j_python_home, &iscopy); */
/* const char *python_path = (*env)->GetStringUTFChars(env, j_python_path, &iscopy); */
/* const char *arg = (*env)->GetStringUTFChars(env, j_arg, &iscopy); */

/* setenv("ANDROID_PRIVATE", android_private, 1); */
/* setenv("ANDROID_ARGUMENT", android_argument, 1); */
/* setenv("PYTHONOPTIMIZE", "2", 1); */
/* setenv("PYTHONHOME", python_home, 1); */
/* setenv("PYTHONPATH", python_path, 1); */
/* setenv("PYTHON_SERVICE_ARGUMENT", arg, 1); */

/* char *argv[] = { "service" }; */
/* /\* ANDROID_ARGUMENT points to service subdir, */
/* * so main() will run main.py from this dir */
/* *\/ */
/* main(1, argv); */
/* } */
JNIEXPORT void JNICALL Java_org_kivy_android_PythonService_nativeStart ( JNIEnv* env, jobject thiz,
jstring j_android_private,
jstring j_android_argument,
jstring j_python_home,
jstring j_python_path,
jstring j_arg )
{
jboolean iscopy;
const char *android_private = (*env)->GetStringUTFChars(env, j_android_private, &iscopy);
const char *android_argument = (*env)->GetStringUTFChars(env, j_android_argument, &iscopy);
const char *python_home = (*env)->GetStringUTFChars(env, j_python_home, &iscopy);
const char *python_path = (*env)->GetStringUTFChars(env, j_python_path, &iscopy);
const char *arg = (*env)->GetStringUTFChars(env, j_arg, &iscopy);

setenv("ANDROID_PRIVATE", android_private, 1);
setenv("ANDROID_ARGUMENT", android_argument, 1);
setenv("PYTHONOPTIMIZE", "2", 1);
setenv("PYTHONHOME", python_home, 1);
setenv("PYTHONPATH", python_path, 1);
setenv("PYTHON_SERVICE_ARGUMENT", arg, 1);

char *argv[] = { "service" };
/* ANDROID_ARGUMENT points to service subdir,
* so main() will run main.py from this dir
*/
main(1, argv);
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ApplicationInfo;
import android.content.Intent;

import org.libsdl.app.SDLActivity;

import org.kivy.android.PythonUtil;

import org.renpy.android.ResourceManager;
import org.renpy.android.AssetExtract;

Expand All @@ -35,7 +38,7 @@ public class PythonActivity extends SDLActivity {
private static final String TAG = "PythonActivity";

public static PythonActivity mActivity = null;

private ResourceManager resourceManager = null;
private Bundle mMetaData = null;
private PowerManager.WakeLock mWakeLock = null;
Expand All @@ -51,9 +54,9 @@ protected void onCreate(Bundle savedInstanceState) {
Log.v(TAG, "About to do super onCreate");
super.onCreate(savedInstanceState);
Log.v(TAG, "Did super onCreate");

this.mActivity = this;

String mFilesDirectory = mActivity.getFilesDir().getAbsolutePath();
Log.v(TAG, "Setting env vars for start.c and Python to use");
SDLActivity.nativeSetEnv("ANDROID_PRIVATE", mFilesDirectory);
Expand All @@ -62,7 +65,7 @@ protected void onCreate(Bundle savedInstanceState) {
SDLActivity.nativeSetEnv("PYTHONHOME", mFilesDirectory);
SDLActivity.nativeSetEnv("PYTHONPATH", mFilesDirectory + ":" + mFilesDirectory + "/lib");


// nativeSetEnv("ANDROID_ARGUMENT", getFilesDir());

try {
Expand All @@ -84,54 +87,11 @@ protected void onCreate(Bundle savedInstanceState) {
} catch (PackageManager.NameNotFoundException e) {
}
}

// This is just overrides the normal SDLActivity, which just loads
// SDL2 and main
protected String[] getLibraries() {
return new String[] {
"SDL2",
"SDL2_image",
"SDL2_mixer",
"SDL2_ttf",
"main"
};
}


public void loadLibraries() {
// AND: This should probably be replaced by a call to super
for (String lib : getLibraries()) {
System.loadLibrary(lib);
}

try {
System.loadLibrary("python2.7");
} catch(UnsatisfiedLinkError e) {
Log.v(TAG, "Failed to load libpython2.7");
}

try {
System.loadLibrary("python3.5m");
} catch(UnsatisfiedLinkError e) {
Log.v(TAG, "Failed to load libpython3.5m");
}

try {
System.load(getFilesDir() + "/lib/python2.7/lib-dynload/_io.so");
System.load(getFilesDir() + "/lib/python2.7/lib-dynload/unicodedata.so");
} catch(UnsatisfiedLinkError e) {
Log.v(TAG, "Failed to load _io.so or unicodedata.so...but that's okay.");
}

try {
// System.loadLibrary("ctypes");
System.load(getFilesDir() + "/lib/python2.7/lib-dynload/_ctypes.so");
} catch(UnsatisfiedLinkError e) {
Log.v(TAG, "Unsatisfied linker when loading ctypes");
}

Log.v(TAG, "Loaded everything!");
PythonUtil.loadLibraries(getFilesDir());
}

public void recursiveDelete(File f) {
if (f.isDirectory()) {
for (File r : f.listFiles()) {
Expand Down Expand Up @@ -163,15 +123,15 @@ public void run() {
}
}
}

public void unpackData(final String resource, File target) {

Log.v(TAG, "UNPACKING!!! " + resource + " " + target.getName());

// The version of data in memory and on disk.
String data_version = resourceManager.getString(resource + "_version");
String disk_version = null;

Log.v(TAG, "Data version is " + data_version);

// If no version, no unpacking is necessary.
Expand Down Expand Up @@ -220,7 +180,7 @@ public void unpackData(final String resource, File target) {
}
}
}

public static ViewGroup getLayout() {
return mLayout;
}
Expand Down Expand Up @@ -298,4 +258,23 @@ protected void onActivityResult(int requestCode, int resultCode, Intent intent)
}
}

public static void start_service(String serviceTitle, String serviceDescription,
String pythonServiceArgument) {
Intent serviceIntent = new Intent(PythonActivity.mActivity, PythonService.class);
String argument = PythonActivity.mActivity.getFilesDir().getAbsolutePath();
String filesDirectory = argument;
serviceIntent.putExtra("androidPrivate", argument);
serviceIntent.putExtra("androidArgument", filesDirectory);
serviceIntent.putExtra("pythonHome", argument);
serviceIntent.putExtra("pythonPath", argument + ":" + filesDirectory + "/lib");
serviceIntent.putExtra("serviceTitle", serviceTitle);
serviceIntent.putExtra("serviceDescription", serviceDescription);
serviceIntent.putExtra("pythonServiceArgument", pythonServiceArgument);
PythonActivity.mActivity.startService(serviceIntent);
}

public static void stop_service() {
Intent serviceIntent = new Intent(PythonActivity.mActivity, PythonService.class);
PythonActivity.mActivity.stopService(serviceIntent);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package org.kivy.android;

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 org.kivy.android.PythonUtil;

import org.renpy.android.Hardware;


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 pythonHome;
private String pythonPath;
// Argument to pass to Python code,
private String pythonServiceArgument;
public static Service mService = null;

@Override
public IBinder onBind(Intent arg0) {
return null;
}

@Override
public void onCreate() {
super.onCreate();
//Hardware.context = this;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (pythonThread != null) {
Log.v("python service", "service exists, do not start again");
return START_NOT_STICKY;
}

Bundle extras = intent.getExtras();
androidPrivate = extras.getString("androidPrivate");
// service code is located in service subdir
androidArgument = extras.getString("androidArgument") + "/service";
pythonHome = extras.getString("pythonHome");
pythonPath = extras.getString("pythonPath");
pythonServiceArgument = extras.getString("pythonServiceArgument");
String serviceTitle = extras.getString("serviceTitle");
String serviceDescription = extras.getString("serviceDescription");

pythonThread = new Thread(this);
pythonThread.start();

Context context = getApplicationContext();
Notification notification = new Notification(context.getApplicationInfo().icon,
serviceTitle,
System.currentTimeMillis());
Intent contextIntent = new Intent(context, PythonActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, contextIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
notification.setLatestEventInfo(context, serviceTitle, serviceDescription, pIntent);
startForeground(1, notification);

return START_NOT_STICKY;
}

@Override
public void onDestroy() {
super.onDestroy();
pythonThread = null;
Process.killProcess(Process.myPid());
}

@Override
public void run(){
PythonUtil.loadLibraries(getFilesDir());

this.mService = this;
nativeStart(androidPrivate, androidArgument, pythonHome, pythonPath,
pythonServiceArgument);
}

// Native part
public static native void nativeStart(String androidPrivate, String androidArgument,
String pythonHome, String pythonPath,
String pythonServiceArgument);

}
Loading