diff --git a/pythonforandroid/recipes/android/__init__.py b/pythonforandroid/recipes/android/__init__.py index 77137cc1de..f8b37c5568 100644 --- a/pythonforandroid/recipes/android/__init__.py +++ b/pythonforandroid/recipes/android/__init__.py @@ -1,7 +1,9 @@ - from pythonforandroid.recipe import CythonRecipe, IncludedFilesBehaviour -import sh -from os.path import exists, join +from pythonforandroid.util import current_directory +from pythonforandroid.patching import will_build +from pythonforandroid import logger + +from os.path import join class AndroidRecipe(IncludedFilesBehaviour, CythonRecipe): @@ -11,8 +13,63 @@ class AndroidRecipe(IncludedFilesBehaviour, CythonRecipe): src_filename = 'src' - depends = ['pygame'] - conflicts = ['sdl2'] + depends = [('pygame', 'sdl2'), ('python2', 'python3')] + + config_env = {} + + def get_recipe_env(self, arch): + env = super(AndroidRecipe, self).get_recipe_env(arch) + env.update(self.config_env) + return env + + def prebuild_arch(self, arch): + super(AndroidRecipe, self).prebuild_arch(arch) + + tpxi = 'DEF {} = {}\n' + th = '#define {} {}\n' + tpy = '{} = {}\n' + + bootstrap_name = self.ctx.bootstrap.name + is_sdl2 = bootstrap_name in ('sdl2', 'sdl2python3') + is_pygame = bootstrap_name in ('pygame',) + + if is_sdl2: + bootstrap = 'sdl2' + java_ns = 'org.kivy.android' + jni_ns = 'org/kivy/android' + elif is_pygame: + bootstrap = 'pygame' + java_ns = 'org.renpy.android' + jni_ns = 'org/renpy/android' + else: + logger.error('unsupported bootstrap for android recipe: {}'.format(bootstrap_name)) + exit(1) + + config = { + 'BOOTSTRAP': bootstrap, + 'IS_SDL2': int(is_sdl2), + 'IS_PYGAME': int(is_pygame), + 'PY2': int(will_build('python2')(self)), + 'JAVA_NAMESPACE': java_ns, + 'JNI_NAMESPACE': jni_ns, + } + + with current_directory(self.get_build_dir(arch.arch)): + with open(join('android', 'config.pxi'), 'w') as fpxi: + with open(join('android', 'config.h'), 'w') as fh: + with open(join('android', 'config.py'), 'w') as fpy: + for key, value in config.items(): + fpxi.write(tpxi.format(key, repr(value))) + fpy.write(tpy.format(key, repr(value))) + fh.write(th.format(key, value if isinstance(value, int) + else '"{}"'.format(value))) + self.config_env[key] = str(value) + + if is_sdl2: + fh.write('JNIEnv *SDL_AndroidGetJNIEnv(void);\n') + fh.write('#define SDL_ANDROID_GetJNIEnv SDL_AndroidGetJNIEnv\n') + elif is_pygame: + fh.write('JNIEnv *SDL_ANDROID_GetJNIEnv(void);\n') recipe = AndroidRecipe() diff --git a/pythonforandroid/recipes/android/src/android/_android.pyx b/pythonforandroid/recipes/android/src/android/_android.pyx index 253c405d31..8a410e7d13 100644 --- a/pythonforandroid/recipes/android/src/android/_android.pyx +++ b/pythonforandroid/recipes/android/src/android/_android.pyx @@ -1,19 +1,22 @@ # Android-specific python services. -cdef extern int SDL_ANDROID_CheckPause() -cdef extern void SDL_ANDROID_WaitForResume() nogil -cdef extern void SDL_ANDROID_MapKey(int scancode, int keysym) - -def check_pause(): - return SDL_ANDROID_CheckPause() - -def wait_for_resume(): - android_accelerometer_enable(False) - SDL_ANDROID_WaitForResume() - android_accelerometer_enable(accelerometer_enabled) - -def map_key(scancode, keysym): - SDL_ANDROID_MapKey(scancode, keysym) +include "config.pxi" + +IF BOOTSTRAP == 'pygame': + cdef extern int SDL_ANDROID_CheckPause() + cdef extern void SDL_ANDROID_WaitForResume() nogil + cdef extern void SDL_ANDROID_MapKey(int scancode, int keysym) + + def check_pause(): + return SDL_ANDROID_CheckPause() + + def wait_for_resume(): + android_accelerometer_enable(False) + SDL_ANDROID_WaitForResume() + android_accelerometer_enable(accelerometer_enabled) + + def map_key(scancode, keysym): + SDL_ANDROID_MapKey(scancode, keysym) # Android keycodes. KEYCODE_UNKNOWN = 0 @@ -109,12 +112,6 @@ KEYCODE_MEDIA_REWIND = 89 KEYCODE_MEDIA_FAST_FORWARD = 90 KEYCODE_MUTE = 91 -# Activate input - required to receive input events. -cdef extern void android_activate_input() - -def init(): - android_activate_input() - # Vibration support. cdef extern void android_vibrate(double) @@ -178,7 +175,7 @@ api_version = autoclass('android.os.Build$VERSION').SDK_INT version_codes = autoclass('android.os.Build$VERSION_CODES') -python_act = autoclass('org.renpy.android.PythonActivity') +python_act = autoclass(JAVA_NAMESPACE + '.PythonActivity') Rect = autoclass('android.graphics.Rect') mActivity = python_act.mActivity if mActivity: @@ -194,7 +191,10 @@ if mActivity: self.height = mActivity.getWindowManager().getDefaultDisplay().getHeight() - (rctx.bottom - rctx.top) ll = LayoutListener() - python_act.mView.getViewTreeObserver().addOnGlobalLayoutListener(ll) + IF BOOTSTRAP == 'sdl2': + python_act.getLayout().getViewTreeObserver().addOnGlobalLayoutListener(ll) + ELSE: + python_act.mView.getViewTreeObserver().addOnGlobalLayoutListener(ll) def get_keyboard_height(): return ll.height @@ -276,52 +276,59 @@ def get_buildinfo(): binfo.VERSION_RELEASE = BUILD_VERSION_RELEASE return binfo -# Action send -cdef extern void android_action_send(char*, char*, char*, char*, char*) -def action_send(mimetype, filename=None, subject=None, text=None, - chooser_title=None): - cdef char *j_mimetype = mimetype - cdef char *j_filename = NULL - cdef char *j_subject = NULL - cdef char *j_text = NULL - cdef char *j_chooser_title = NULL - if filename is not None: - j_filename = filename - if subject is not None: - j_subject = subject - if text is not None: - j_text = text - if chooser_title is not None: - j_chooser_title = chooser_title - android_action_send(j_mimetype, j_filename, j_subject, j_text, - j_chooser_title) - -cdef extern int android_checkstop() -cdef extern void android_ackstop() - -def check_stop(): - return android_checkstop() - -def ack_stop(): - android_ackstop() - -# ------------------------------------------------------------------- -# URL Opening. -cdef extern void android_open_url(char *url) -def open_url(url): - android_open_url(url) - -# Web browser support. -class AndroidBrowser(object): - def open(self, url, new=0, autoraise=True): - open_url(url) - def open_new(self, url): - open_url(url) - def open_new_tab(self, url): - open_url(url) - -import webbrowser -webbrowser.register('android', AndroidBrowser, None, -1) +IF IS_PYGAME: + # Activate input - required to receive input events. + cdef extern void android_activate_input() + + def init(): + android_activate_input() + + # Action send + cdef extern void android_action_send(char*, char*, char*, char*, char*) + def action_send(mimetype, filename=None, subject=None, text=None, + chooser_title=None): + cdef char *j_mimetype = mimetype + cdef char *j_filename = NULL + cdef char *j_subject = NULL + cdef char *j_text = NULL + cdef char *j_chooser_title = NULL + if filename is not None: + j_filename = filename + if subject is not None: + j_subject = subject + if text is not None: + j_text = text + if chooser_title is not None: + j_chooser_title = chooser_title + android_action_send(j_mimetype, j_filename, j_subject, j_text, + j_chooser_title) + + cdef extern int android_checkstop() + cdef extern void android_ackstop() + + def check_stop(): + return android_checkstop() + + def ack_stop(): + android_ackstop() + + # ------------------------------------------------------------------- + # URL Opening. + cdef extern void android_open_url(char *url) + def open_url(url): + android_open_url(url) + + # Web browser support. + class AndroidBrowser(object): + def open(self, url, new=0, autoraise=True): + open_url(url) + def open_new(self, url): + open_url(url) + def open_new_tab(self, url): + open_url(url) + + import webbrowser + webbrowser.register('android', AndroidBrowser, None, -1) cdef extern void android_start_service(char *, char *, char *) def start_service(title=None, description=None, arg=None): diff --git a/pythonforandroid/recipes/android/src/android/_android_billing_jni.c b/pythonforandroid/recipes/android/src/android/_android_billing_jni.c index 099ce8429d..d438df3299 100644 --- a/pythonforandroid/recipes/android/src/android/_android_billing_jni.c +++ b/pythonforandroid/recipes/android/src/android/_android_billing_jni.c @@ -4,7 +4,7 @@ #include #include -JNIEnv *SDL_ANDROID_GetJNIEnv(void); +#include "config.h" #define aassert(x) { if (!x) { __android_log_print(ANDROID_LOG_ERROR, "android_jni", "Assertion failed. %s:%d", __FILE__, __LINE__); abort(); }} #define PUSH_FRAME { (*env)->PushLocalFrame(env, 16); } @@ -18,7 +18,7 @@ void android_billing_service_start() { if (env == NULL) { env = SDL_ANDROID_GetJNIEnv(); aassert(env); - cls = (*env)->FindClass(env, "org/renpy/android/PythonActivity"); + cls = (*env)->FindClass(env, JNI_NAMESPACE "/PythonActivity"); aassert(cls); mid = (*env)->GetStaticMethodID(env, cls, "billingServiceStart", "()V"); aassert(mid); @@ -37,7 +37,7 @@ void android_billing_service_stop() { if (env == NULL) { env = SDL_ANDROID_GetJNIEnv(); aassert(env); - cls = (*env)->FindClass(env, "org/renpy/android/PythonActivity"); + cls = (*env)->FindClass(env, JNI_NAMESPACE "/PythonActivity"); aassert(cls); mid = (*env)->GetStaticMethodID(env, cls, "billingServiceStop", "()V"); aassert(mid); @@ -56,7 +56,7 @@ void android_billing_buy(char *sku) { if (env == NULL) { env = SDL_ANDROID_GetJNIEnv(); aassert(env); - cls = (*env)->FindClass(env, "org/renpy/android/PythonActivity"); + cls = (*env)->FindClass(env, JNI_NAMESPACE "/PythonActivity"); aassert(cls); mid = (*env)->GetStaticMethodID(env, cls, "billingBuy", "(Ljava/lang/String;)V"); aassert(mid); @@ -81,7 +81,7 @@ char *android_billing_get_purchased_items() { if (env == NULL) { env = SDL_ANDROID_GetJNIEnv(); aassert(env); - cls = (*env)->FindClass(env, "org/renpy/android/PythonActivity"); + cls = (*env)->FindClass(env, JNI_NAMESPACE "/PythonActivity"); aassert(cls); mid = (*env)->GetStaticMethodID(env, cls, "billingGetPurchasedItems", "()Ljava/lang/String;"); aassert(mid); @@ -104,7 +104,7 @@ char *android_billing_get_pending_message() { if (env == NULL) { env = SDL_ANDROID_GetJNIEnv(); aassert(env); - cls = (*env)->FindClass(env, "org/renpy/android/PythonActivity"); + cls = (*env)->FindClass(env, JNI_NAMESPACE "/PythonActivity"); aassert(cls); mid = (*env)->GetStaticMethodID(env, cls, "billingGetPendingMessage", "()Ljava/lang/String;"); aassert(mid); diff --git a/pythonforandroid/recipes/android/src/android/_android_jni.c b/pythonforandroid/recipes/android/src/android/_android_jni.c index ee0c3c2ff3..8eee77073b 100644 --- a/pythonforandroid/recipes/android/src/android/_android_jni.c +++ b/pythonforandroid/recipes/android/src/android/_android_jni.c @@ -4,7 +4,7 @@ #include #include -JNIEnv *SDL_ANDROID_GetJNIEnv(void); +#include "config.h" #define aassert(x) { if (!x) { __android_log_print(ANDROID_LOG_ERROR, "android_jni", "Assertion failed. %s:%d", __FILE__, __LINE__); abort(); }} #define PUSH_FRAME { (*env)->PushLocalFrame(env, 16); } @@ -201,6 +201,7 @@ void android_get_buildinfo() { } } +#if IS_PYGAME void android_activate_input(void) { static JNIEnv *env = NULL; static jclass *cls = NULL; @@ -310,6 +311,7 @@ void android_open_url(char *url) { POP_FRAME; } +#endif // IS_PYGAME void android_start_service(char *title, char *description, char *arg) { static JNIEnv *env = NULL; @@ -319,7 +321,7 @@ void android_start_service(char *title, char *description, char *arg) { if (env == NULL) { env = SDL_ANDROID_GetJNIEnv(); aassert(env); - cls = (*env)->FindClass(env, "org/renpy/android/PythonActivity"); + cls = (*env)->FindClass(env, JNI_NAMESPACE "/PythonActivity"); aassert(cls); mid = (*env)->GetStaticMethodID(env, cls, "start_service", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); @@ -346,7 +348,7 @@ void android_stop_service() { if (env == NULL) { env = SDL_ANDROID_GetJNIEnv(); - cls = (*env)->FindClass(env, "org/renpy/android/PythonActivity"); + cls = (*env)->FindClass(env, JNI_NAMESPACE "/PythonActivity"); aassert(cls); mid = (*env)->GetStaticMethodID(env, cls, "stop_service", "()V"); aassert(mid); diff --git a/pythonforandroid/recipes/android/src/android/activity.py b/pythonforandroid/recipes/android/src/android/activity.py index eaf7b07bdd..94e08e71a1 100644 --- a/pythonforandroid/recipes/android/src/android/activity.py +++ b/pythonforandroid/recipes/android/src/android/activity.py @@ -1,13 +1,14 @@ from jnius import PythonJavaClass, java_method, autoclass, cast +from android.config import JAVA_NAMESPACE, JNI_NAMESPACE -_activity = autoclass('org.renpy.android.PythonActivity').mActivity +_activity = autoclass(JAVA_NAMESPACE + '.PythonActivity').mActivity _callbacks = { 'on_new_intent': [], 'on_activity_result': [] } class NewIntentListener(PythonJavaClass): - __javainterfaces__ = ['org/renpy/android/PythonActivity$NewIntentListener'] + __javainterfaces__ = [JNI_NAMESPACE + '/PythonActivity$NewIntentListener'] __javacontext__ = 'app' def __init__(self, callback, **kwargs): @@ -20,7 +21,7 @@ def onNewIntent(self, intent): class ActivityResultListener(PythonJavaClass): - __javainterfaces__ = ['org/renpy/android/PythonActivity$ActivityResultListener'] + __javainterfaces__ = [JNI_NAMESPACE + '/PythonActivity$ActivityResultListener'] __javacontext__ = 'app' def __init__(self, callback): diff --git a/pythonforandroid/recipes/android/src/android/broadcast.py b/pythonforandroid/recipes/android/src/android/broadcast.py index 6cab7ed05c..ba3dfc9765 100644 --- a/pythonforandroid/recipes/android/src/android/broadcast.py +++ b/pythonforandroid/recipes/android/src/android/broadcast.py @@ -2,11 +2,13 @@ # Broadcast receiver bridge from jnius import autoclass, PythonJavaClass, java_method +from android.config import JAVA_NAMESPACE, JNI_NAMESPACE + class BroadcastReceiver(object): class Callback(PythonJavaClass): - __javainterfaces__ = ['org/renpy/android/GenericBroadcastReceiverCallback'] + __javainterfaces__ = [JNI_NAMESPACE + '/GenericBroadcastReceiverCallback'] __javacontext__ = 'app' def __init__(self, callback, *args, **kwargs): @@ -39,7 +41,7 @@ def _expand_partial_name(partial_name): resolved_categories = [_expand_partial_name(x) for x in categories or []] # resolve android API - GenericBroadcastReceiver = autoclass('org.renpy.android.GenericBroadcastReceiver') + GenericBroadcastReceiver = autoclass(JAVA_NAMESPACE + '.GenericBroadcastReceiver') IntentFilter = autoclass('android.content.IntentFilter') HandlerThread = autoclass('android.os.HandlerThread') @@ -70,8 +72,8 @@ def stop(self): def context(self): from os import environ if 'PYTHON_SERVICE_ARGUMENT' in environ: - PythonService = autoclass('org.renpy.android.PythonService') + PythonService = autoclass(JAVA_NAMESPACE + '.PythonService') return PythonService.mService - PythonActivity = autoclass('org.renpy.android.PythonActivity') + PythonActivity = autoclass(JAVA_NAMESPACE + '.PythonActivity') return PythonActivity.mActivity diff --git a/pythonforandroid/recipes/android/src/android/runnable.py b/pythonforandroid/recipes/android/src/android/runnable.py index 2b36157fca..564d83b036 100644 --- a/pythonforandroid/recipes/android/src/android/runnable.py +++ b/pythonforandroid/recipes/android/src/android/runnable.py @@ -5,9 +5,10 @@ ''' from jnius import PythonJavaClass, java_method, autoclass +from android.config import JAVA_NAMESPACE # reference to the activity -_PythonActivity = autoclass('org.renpy.android.PythonActivity') +_PythonActivity = autoclass(JAVA_NAMESPACE + '.PythonActivity') class Runnable(PythonJavaClass): diff --git a/pythonforandroid/recipes/android/src/setup.py b/pythonforandroid/recipes/android/src/setup.py index 3c2ba63b0c..8a276fe2ed 100755 --- a/pythonforandroid/recipes/android/src/setup.py +++ b/pythonforandroid/recipes/android/src/setup.py @@ -1,27 +1,34 @@ from distutils.core import setup, Extension import os +library_dirs = ['libs/' + os.environ['ARCH']] +lib_dict = { + 'pygame': ['sdl'], + 'sdl2': ['SDL2', 'SDL2_image', 'SDL2_mixer', 'SDL2_ttf'] +} +sdl_libs = lib_dict[os.environ['BOOTSTRAP']] + +renpy_sound = Extension('android._android_sound', + ['android/_android_sound.c', 'android/_android_sound_jni.c', ], + libraries=sdl_libs + ['log'], + library_dirs=library_dirs) + +modules = [Extension('android._android', + ['android/_android.c', 'android/_android_jni.c'], + libraries=sdl_libs + ['log'], + library_dirs=library_dirs), + Extension('android._android_billing', + ['android/_android_billing.c', 'android/_android_billing_jni.c'], + libraries=['log'], + library_dirs=library_dirs)] + +if int(os.environ['IS_PYGAME']): + modules.append(renpy_sound) + + setup(name='android', version='1.0', packages=['android'], package_dir={'android': 'android'}, - ext_modules=[ - - Extension( - 'android._android', ['android/_android.c', 'android/_android_jni.c'], - libraries=[ 'sdl', 'log' ], - library_dirs=[ 'libs/' + os.environ['ARCH'] ], - ), - Extension( - 'android._android_billing', ['android/_android_billing.c', 'android/_android_billing_jni.c'], - libraries=[ 'log' ], - library_dirs=[ 'libs/' + os.environ['ARCH'] ], - ), - Extension( - 'android._android_sound', ['android/_android_sound.c', 'android/_android_sound_jni.c',], - libraries=[ 'sdl', 'log' ], - library_dirs=[ 'libs/' + os.environ['ARCH'] ], - ), - - ] + ext_modules=modules )