Skip to content
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
67 changes: 62 additions & 5 deletions pythonforandroid/recipes/android/__init__.py
Original file line number Diff line number Diff line change
@@ -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):
Expand All @@ -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()
143 changes: 75 additions & 68 deletions pythonforandroid/recipes/android/src/android/_android.pyx
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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:
Expand All @@ -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
Expand Down Expand Up @@ -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 = <bytes>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 = <bytes>filename
if subject is not None:
j_subject = <bytes>subject
if text is not None:
j_text = <bytes>text
if chooser_title is not None:
j_chooser_title = <bytes>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 = <bytes>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 = <bytes>filename
if subject is not None:
j_subject = <bytes>subject
if text is not None:
j_text = <bytes>text
if chooser_title is not None:
j_chooser_title = <bytes>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):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <string.h>
#include <stdlib.h>

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); }
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down
8 changes: 5 additions & 3 deletions pythonforandroid/recipes/android/src/android/_android_jni.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <string.h>
#include <stdlib.h>

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); }
Expand Down Expand Up @@ -201,6 +201,7 @@ void android_get_buildinfo() {
}
}

#if IS_PYGAME
void android_activate_input(void) {
static JNIEnv *env = NULL;
static jclass *cls = NULL;
Expand Down Expand Up @@ -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;
Expand All @@ -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");
Expand All @@ -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);
Expand Down
7 changes: 4 additions & 3 deletions pythonforandroid/recipes/android/src/android/activity.py
Original file line number Diff line number Diff line change
@@ -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):
Expand All @@ -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):
Expand Down
Loading