try stock android w/ lbry modification
This commit is contained in:
parent
13fc3a7ff2
commit
2926957670
30 changed files with 3232 additions and 426 deletions
257
MovedRecipes/android/__init__.py
Normal file
257
MovedRecipes/android/__init__.py
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
from pythonforandroid.recipe import CythonRecipe, IncludedFilesBehaviour
|
||||||
|
from pythonforandroid.util import current_directory
|
||||||
|
from pythonforandroid import logger
|
||||||
|
|
||||||
|
from os.path import join
|
||||||
|
|
||||||
|
|
||||||
|
class AndroidRecipe(IncludedFilesBehaviour, CythonRecipe):
|
||||||
|
# name = 'android'
|
||||||
|
version = None
|
||||||
|
url = None
|
||||||
|
|
||||||
|
src_filename = 'src'
|
||||||
|
|
||||||
|
depends = [('sdl2', 'genericndkbuild'), 'pyjnius']
|
||||||
|
|
||||||
|
config_env = {}
|
||||||
|
|
||||||
|
def get_recipe_env(self, arch):
|
||||||
|
env = super().get_recipe_env(arch)
|
||||||
|
env.update(self.config_env)
|
||||||
|
return env
|
||||||
|
|
||||||
|
def prebuild_arch(self, arch):
|
||||||
|
super().prebuild_arch(arch)
|
||||||
|
ctx_bootstrap = self.ctx.bootstrap.name
|
||||||
|
|
||||||
|
# define macros for Cython, C, Python
|
||||||
|
tpxi = 'DEF {} = {}\n'
|
||||||
|
th = '#define {} {}\n'
|
||||||
|
tpy = '{} = {}\n'
|
||||||
|
|
||||||
|
# make sure bootstrap name is in unicode
|
||||||
|
if isinstance(ctx_bootstrap, bytes):
|
||||||
|
ctx_bootstrap = ctx_bootstrap.decode('utf-8')
|
||||||
|
bootstrap = bootstrap_name = ctx_bootstrap
|
||||||
|
is_lbry = bootstrap_name in ('lbry',)
|
||||||
|
is_sdl2 = (bootstrap_name == "sdl2")
|
||||||
|
if bootstrap_name in ["sdl2", "webview", "service_only", "service_library", "lbry"]:
|
||||||
|
java_ns = u'org.kivy.android'
|
||||||
|
jni_ns = u'org/kivy/android'
|
||||||
|
else:
|
||||||
|
logger.error((
|
||||||
|
'unsupported bootstrap for android recipe: {}'
|
||||||
|
''.format(bootstrap_name)
|
||||||
|
))
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
config = {
|
||||||
|
'BOOTSTRAP': bootstrap,
|
||||||
|
'IS_SDL2': int(is_sdl2),
|
||||||
|
'PY2': 0,
|
||||||
|
'JAVA_NAMESPACE': java_ns,
|
||||||
|
'JNI_NAMESPACE': jni_ns,
|
||||||
|
'ACTIVITY_CLASS_NAME': self.ctx.activity_class_name,
|
||||||
|
'ACTIVITY_CLASS_NAMESPACE': self.ctx.activity_class_name.replace('.', '/'),
|
||||||
|
'SERVICE_CLASS_NAME': self.ctx.service_class_name,
|
||||||
|
}
|
||||||
|
|
||||||
|
# create config files for Cython, C and Python
|
||||||
|
with (
|
||||||
|
current_directory(self.get_build_dir(arch.arch))), (
|
||||||
|
open(join('android', 'config.pxi'), 'w')) as fpxi, (
|
||||||
|
open(join('android', 'config.h'), 'w')) as fh, (
|
||||||
|
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'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
fh.write('JNIEnv *WebView_AndroidGetJNIEnv(void);\n')
|
||||||
|
fh.write(
|
||||||
|
'#define SDL_ANDROID_GetJNIEnv WebView_AndroidGetJNIEnv\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
recipe = AndroidRecipe()
|
||||||
|
|
||||||
|
'''
|
||||||
|
from pythonforandroid.recipe import CythonRecipe, IncludedFilesBehaviour
|
||||||
|
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):
|
||||||
|
# name = 'android'
|
||||||
|
version = None
|
||||||
|
url = None
|
||||||
|
|
||||||
|
src_filename = 'src'
|
||||||
|
|
||||||
|
depends = [('pygame', 'sdl2', 'genericndkbuild'), ('python2', 'python3crystax')]
|
||||||
|
|
||||||
|
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 = bootstrap_name = self.ctx.bootstrap.name
|
||||||
|
is_sdl2 = bootstrap_name in ('sdl2', 'sdl2python3', 'sdl2_gradle')
|
||||||
|
is_pygame = bootstrap_name in ('pygame',)
|
||||||
|
is_webview = bootstrap_name in ('webview',)
|
||||||
|
is_lbry = bootstrap_name in ('lbry',)
|
||||||
|
|
||||||
|
if is_sdl2 or is_webview or is_lbry:
|
||||||
|
if is_sdl2:
|
||||||
|
bootstrap = 'sdl2'
|
||||||
|
java_ns = 'org.kivy.android'
|
||||||
|
jni_ns = 'org/kivy/android'
|
||||||
|
elif is_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()
|
||||||
|
'''
|
||||||
|
|
||||||
|
'''
|
||||||
|
from pythonforandroid.recipe import CythonRecipe, Recipe, IncludedFilesBehaviour
|
||||||
|
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):
|
||||||
|
# name = 'android'
|
||||||
|
version = None
|
||||||
|
url = None
|
||||||
|
|
||||||
|
src_filename = 'src'
|
||||||
|
|
||||||
|
depends = [('pygame', 'sdl2', 'genericndkbuild'), ('python2', 'python3crystax')]
|
||||||
|
|
||||||
|
call_hostpython_via_targetpython = False
|
||||||
|
|
||||||
|
config_env = {}
|
||||||
|
|
||||||
|
def get_recipe_env(self, arch):
|
||||||
|
env = super(AndroidRecipe, self).get_recipe_env(arch)
|
||||||
|
env.update(self.config_env)
|
||||||
|
|
||||||
|
target_python = Recipe.get_recipe('python2', self.ctx).get_build_dir(arch.arch)
|
||||||
|
env['PYTHON_ROOT'] = join(target_python, 'python-install')
|
||||||
|
env['CFLAGS'] += ' -I' + env['PYTHON_ROOT'] + '/include/python2.7'
|
||||||
|
env['LDFLAGS'] += ' -L' + env['PYTHON_ROOT'] + '/lib' + ' -lpython2.7'
|
||||||
|
|
||||||
|
return env
|
||||||
|
|
||||||
|
def prebuild_arch(self, arch):
|
||||||
|
super(AndroidRecipe, self).prebuild_arch(arch)
|
||||||
|
|
||||||
|
tpxi = 'DEF {} = {}\n'
|
||||||
|
th = '#define {} {}\n'
|
||||||
|
tpy = '{} = {}\n'
|
||||||
|
|
||||||
|
bootstrap = bootstrap_name = self.ctx.bootstrap.name
|
||||||
|
is_sdl2 = bootstrap_name in ('sdl2', 'sdl2python3')
|
||||||
|
is_pygame = bootstrap_name in ('pygame',)
|
||||||
|
is_webview = bootstrap_name in ('webview')
|
||||||
|
is_lbry = bootstrap_name in ('lbry')
|
||||||
|
|
||||||
|
if is_sdl2 or is_webview or is_lbry:
|
||||||
|
if is_sdl2:
|
||||||
|
bootstrap = 'sdl2'
|
||||||
|
java_ns = 'org.kivy.android'
|
||||||
|
jni_ns = 'org/kivy/android'
|
||||||
|
elif is_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()
|
||||||
|
'''
|
0
MovedRecipes/android/src/__init__.py
Normal file
0
MovedRecipes/android/src/__init__.py
Normal file
8
MovedRecipes/android/src/android/__init__.py
Normal file
8
MovedRecipes/android/src/android/__init__.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
'''
|
||||||
|
Android module
|
||||||
|
==============
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
# legacy import
|
||||||
|
from android._android import *
|
385
MovedRecipes/android/src/android/_android.pyx
Normal file
385
MovedRecipes/android/src/android/_android.pyx
Normal file
|
@ -0,0 +1,385 @@
|
||||||
|
# Android-specific python services.
|
||||||
|
|
||||||
|
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
|
||||||
|
KEYCODE_SOFT_LEFT = 1
|
||||||
|
KEYCODE_SOFT_RIGHT = 2
|
||||||
|
KEYCODE_HOME = 3
|
||||||
|
KEYCODE_BACK = 4
|
||||||
|
KEYCODE_CALL = 5
|
||||||
|
KEYCODE_ENDCALL = 6
|
||||||
|
KEYCODE_0 = 7
|
||||||
|
KEYCODE_1 = 8
|
||||||
|
KEYCODE_2 = 9
|
||||||
|
KEYCODE_3 = 10
|
||||||
|
KEYCODE_4 = 11
|
||||||
|
KEYCODE_5 = 12
|
||||||
|
KEYCODE_6 = 13
|
||||||
|
KEYCODE_7 = 14
|
||||||
|
KEYCODE_8 = 15
|
||||||
|
KEYCODE_9 = 16
|
||||||
|
KEYCODE_STAR = 17
|
||||||
|
KEYCODE_POUND = 18
|
||||||
|
KEYCODE_DPAD_UP = 19
|
||||||
|
KEYCODE_DPAD_DOWN = 20
|
||||||
|
KEYCODE_DPAD_LEFT = 21
|
||||||
|
KEYCODE_DPAD_RIGHT = 22
|
||||||
|
KEYCODE_DPAD_CENTER = 23
|
||||||
|
KEYCODE_VOLUME_UP = 24
|
||||||
|
KEYCODE_VOLUME_DOWN = 25
|
||||||
|
KEYCODE_POWER = 26
|
||||||
|
KEYCODE_CAMERA = 27
|
||||||
|
KEYCODE_CLEAR = 28
|
||||||
|
KEYCODE_A = 29
|
||||||
|
KEYCODE_B = 30
|
||||||
|
KEYCODE_C = 31
|
||||||
|
KEYCODE_D = 32
|
||||||
|
KEYCODE_E = 33
|
||||||
|
KEYCODE_F = 34
|
||||||
|
KEYCODE_G = 35
|
||||||
|
KEYCODE_H = 36
|
||||||
|
KEYCODE_I = 37
|
||||||
|
KEYCODE_J = 38
|
||||||
|
KEYCODE_K = 39
|
||||||
|
KEYCODE_L = 40
|
||||||
|
KEYCODE_M = 41
|
||||||
|
KEYCODE_N = 42
|
||||||
|
KEYCODE_O = 43
|
||||||
|
KEYCODE_P = 44
|
||||||
|
KEYCODE_Q = 45
|
||||||
|
KEYCODE_R = 46
|
||||||
|
KEYCODE_S = 47
|
||||||
|
KEYCODE_T = 48
|
||||||
|
KEYCODE_U = 49
|
||||||
|
KEYCODE_V = 50
|
||||||
|
KEYCODE_W = 51
|
||||||
|
KEYCODE_X = 52
|
||||||
|
KEYCODE_Y = 53
|
||||||
|
KEYCODE_Z = 54
|
||||||
|
KEYCODE_COMMA = 55
|
||||||
|
KEYCODE_PERIOD = 56
|
||||||
|
KEYCODE_ALT_LEFT = 57
|
||||||
|
KEYCODE_ALT_RIGHT = 58
|
||||||
|
KEYCODE_SHIFT_LEFT = 59
|
||||||
|
KEYCODE_SHIFT_RIGHT = 60
|
||||||
|
KEYCODE_TAB = 61
|
||||||
|
KEYCODE_SPACE = 62
|
||||||
|
KEYCODE_SYM = 63
|
||||||
|
KEYCODE_EXPLORER = 64
|
||||||
|
KEYCODE_ENVELOPE = 65
|
||||||
|
KEYCODE_ENTER = 66
|
||||||
|
KEYCODE_DEL = 67
|
||||||
|
KEYCODE_GRAVE = 68
|
||||||
|
KEYCODE_MINUS = 69
|
||||||
|
KEYCODE_EQUALS = 70
|
||||||
|
KEYCODE_LEFT_BRACKET = 71
|
||||||
|
KEYCODE_RIGHT_BRACKET = 72
|
||||||
|
KEYCODE_BACKSLASH = 73
|
||||||
|
KEYCODE_SEMICOLON = 74
|
||||||
|
KEYCODE_APOSTROPHE = 75
|
||||||
|
KEYCODE_SLASH = 76
|
||||||
|
KEYCODE_AT = 77
|
||||||
|
KEYCODE_NUM = 78
|
||||||
|
KEYCODE_HEADSETHOOK = 79
|
||||||
|
KEYCODE_FOCUS = 80
|
||||||
|
KEYCODE_PLUS = 81
|
||||||
|
KEYCODE_MENU = 82
|
||||||
|
KEYCODE_NOTIFICATION = 83
|
||||||
|
KEYCODE_SEARCH = 84
|
||||||
|
KEYCODE_MEDIA_PLAY_PAUSE= 85
|
||||||
|
KEYCODE_MEDIA_STOP = 86
|
||||||
|
KEYCODE_MEDIA_NEXT = 87
|
||||||
|
KEYCODE_MEDIA_PREVIOUS = 88
|
||||||
|
KEYCODE_MEDIA_REWIND = 89
|
||||||
|
KEYCODE_MEDIA_FAST_FORWARD = 90
|
||||||
|
KEYCODE_MUTE = 91
|
||||||
|
|
||||||
|
# Vibration support.
|
||||||
|
cdef extern void android_vibrate(double)
|
||||||
|
|
||||||
|
def vibrate(s):
|
||||||
|
android_vibrate(s)
|
||||||
|
|
||||||
|
# Accelerometer support.
|
||||||
|
cdef extern void android_accelerometer_enable(int)
|
||||||
|
cdef extern void android_accelerometer_reading(float *)
|
||||||
|
|
||||||
|
accelerometer_enabled = False
|
||||||
|
|
||||||
|
def accelerometer_enable(p):
|
||||||
|
global accelerometer_enabled
|
||||||
|
|
||||||
|
android_accelerometer_enable(p)
|
||||||
|
|
||||||
|
accelerometer_enabled = p
|
||||||
|
|
||||||
|
def accelerometer_reading():
|
||||||
|
cdef float rv[3]
|
||||||
|
android_accelerometer_reading(rv)
|
||||||
|
|
||||||
|
return (rv[0], rv[1], rv[2])
|
||||||
|
|
||||||
|
# Wifi reading support
|
||||||
|
cdef extern void android_wifi_scanner_enable()
|
||||||
|
cdef extern char * android_wifi_scan()
|
||||||
|
|
||||||
|
def wifi_scanner_enable():
|
||||||
|
android_wifi_scanner_enable()
|
||||||
|
|
||||||
|
def wifi_scan():
|
||||||
|
cdef char * reading
|
||||||
|
reading = android_wifi_scan()
|
||||||
|
|
||||||
|
reading_list = []
|
||||||
|
|
||||||
|
for line in filter(lambda l: l, reading.split('\n')):
|
||||||
|
[ssid, mac, level] = line.split('\t')
|
||||||
|
reading_list.append((ssid.strip(), mac.upper().strip(), int(level)))
|
||||||
|
|
||||||
|
return reading_list
|
||||||
|
|
||||||
|
# DisplayMetrics information.
|
||||||
|
cdef extern int android_get_dpi()
|
||||||
|
|
||||||
|
def get_dpi():
|
||||||
|
return android_get_dpi()
|
||||||
|
|
||||||
|
|
||||||
|
# Soft keyboard.
|
||||||
|
cdef extern void android_show_keyboard(int)
|
||||||
|
cdef extern void android_hide_keyboard()
|
||||||
|
|
||||||
|
|
||||||
|
from jnius import autoclass, PythonJavaClass, java_method, cast
|
||||||
|
|
||||||
|
# API versions
|
||||||
|
api_version = autoclass('android.os.Build$VERSION').SDK_INT
|
||||||
|
version_codes = autoclass('android.os.Build$VERSION_CODES')
|
||||||
|
|
||||||
|
|
||||||
|
python_act = autoclass(JAVA_NAMESPACE + '.PythonActivity')
|
||||||
|
Rect = autoclass('android.graphics.Rect')
|
||||||
|
mActivity = python_act.mActivity
|
||||||
|
if mActivity:
|
||||||
|
# PyGame backend already has the listener so adding
|
||||||
|
# one here leads to a crash/too much cpu usage.
|
||||||
|
# SDL2 now does noe need the listener so there is
|
||||||
|
# no point adding a processor intensive layout listenere here.
|
||||||
|
height = 0
|
||||||
|
def get_keyboard_height():
|
||||||
|
rctx = Rect()
|
||||||
|
mActivity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rctx)
|
||||||
|
# NOTE top should always be zero
|
||||||
|
rctx.top = 0
|
||||||
|
height = mActivity.getWindowManager().getDefaultDisplay().getHeight() - (rctx.bottom - rctx.top)
|
||||||
|
return height
|
||||||
|
else:
|
||||||
|
def get_keyboard_height():
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Flags for input_type, for requesting a particular type of keyboard
|
||||||
|
#android FLAGS
|
||||||
|
TYPE_CLASS_DATETIME = 4
|
||||||
|
TYPE_CLASS_NUMBER = 2
|
||||||
|
TYPE_NUMBER_VARIATION_NORMAL = 0
|
||||||
|
TYPE_NUMBER_VARIATION_PASSWORD = 16
|
||||||
|
TYPE_CLASS_TEXT = 1
|
||||||
|
TYPE_TEXT_FLAG_AUTO_COMPLETE = 65536
|
||||||
|
TYPE_TEXT_FLAG_AUTO_CORRECT = 32768
|
||||||
|
TYPE_TEXT_FLAG_NO_SUGGESTIONS = 524288
|
||||||
|
TYPE_TEXT_VARIATION_EMAIL_ADDRESS = 32
|
||||||
|
TYPE_TEXT_VARIATION_NORMAL = 0
|
||||||
|
TYPE_TEXT_VARIATION_PASSWORD = 128
|
||||||
|
TYPE_TEXT_VARIATION_POSTAL_ADDRESS = 112
|
||||||
|
TYPE_TEXT_VARIATION_URI = 16
|
||||||
|
TYPE_CLASS_PHONE = 3
|
||||||
|
|
||||||
|
IF BOOTSTRAP == 'sdl2':
|
||||||
|
def remove_presplash():
|
||||||
|
# Remove android presplash in SDL2 bootstrap.
|
||||||
|
mActivity.removeLoadingScreen()
|
||||||
|
|
||||||
|
def show_keyboard(target, input_type):
|
||||||
|
if input_type == 'text':
|
||||||
|
_input_type = TYPE_CLASS_TEXT
|
||||||
|
elif input_type == 'number':
|
||||||
|
_input_type = TYPE_CLASS_NUMBER
|
||||||
|
elif input_type == 'url':
|
||||||
|
_input_type = \
|
||||||
|
TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_URI
|
||||||
|
elif input_type == 'mail':
|
||||||
|
_input_type = \
|
||||||
|
TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_EMAIL_ADDRESS
|
||||||
|
elif input_type == 'datetime':
|
||||||
|
_input_type = TYPE_CLASS_DATETIME
|
||||||
|
elif input_type == 'tel':
|
||||||
|
_input_type = TYPE_CLASS_PHONE
|
||||||
|
elif input_type == 'address':
|
||||||
|
_input_type = TYPE_TEXT_VARIATION_POSTAL_ADDRESS
|
||||||
|
|
||||||
|
if hasattr(target, 'password') and target.password:
|
||||||
|
if _input_type == TYPE_CLASS_TEXT:
|
||||||
|
_input_type |= TYPE_TEXT_VARIATION_PASSWORD
|
||||||
|
elif _input_type == TYPE_CLASS_NUMBER:
|
||||||
|
_input_type |= TYPE_NUMBER_VARIATION_PASSWORD
|
||||||
|
|
||||||
|
if hasattr(target, 'keyboard_suggestions') and not target.keyboard_suggestions:
|
||||||
|
if _input_type == TYPE_CLASS_TEXT:
|
||||||
|
_input_type = TYPE_CLASS_TEXT | \
|
||||||
|
TYPE_TEXT_FLAG_NO_SUGGESTIONS
|
||||||
|
|
||||||
|
android_show_keyboard(_input_type)
|
||||||
|
|
||||||
|
def hide_keyboard():
|
||||||
|
android_hide_keyboard()
|
||||||
|
|
||||||
|
# Build info.
|
||||||
|
cdef extern char* BUILD_MANUFACTURER
|
||||||
|
cdef extern char* BUILD_MODEL
|
||||||
|
cdef extern char* BUILD_PRODUCT
|
||||||
|
cdef extern char* BUILD_VERSION_RELEASE
|
||||||
|
|
||||||
|
cdef extern void android_get_buildinfo()
|
||||||
|
|
||||||
|
class BuildInfo:
|
||||||
|
MANUFACTURER = None
|
||||||
|
MODEL = None
|
||||||
|
PRODUCT = None
|
||||||
|
VERSION_RELEASE = None
|
||||||
|
|
||||||
|
def get_buildinfo():
|
||||||
|
android_get_buildinfo()
|
||||||
|
binfo = BuildInfo()
|
||||||
|
binfo.MANUFACTURER = BUILD_MANUFACTURER
|
||||||
|
binfo.MODEL = BUILD_MODEL
|
||||||
|
binfo.PRODUCT = BUILD_PRODUCT
|
||||||
|
binfo.VERSION_RELEASE = BUILD_VERSION_RELEASE
|
||||||
|
return binfo
|
||||||
|
|
||||||
|
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.
|
||||||
|
def open_url(url):
|
||||||
|
Intent = autoclass('android.content.Intent')
|
||||||
|
Uri = autoclass('android.net.Uri')
|
||||||
|
browserIntent = Intent()
|
||||||
|
browserIntent.setAction(Intent.ACTION_VIEW)
|
||||||
|
browserIntent.setData(Uri.parse(url))
|
||||||
|
currentActivity = cast('android.app.Activity', mActivity)
|
||||||
|
currentActivity.startActivity(browserIntent)
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Web browser support.
|
||||||
|
class AndroidBrowser(object):
|
||||||
|
def open(self, url, new=0, autoraise=True):
|
||||||
|
return open_url(url)
|
||||||
|
def open_new(self, url):
|
||||||
|
return open_url(url)
|
||||||
|
def open_new_tab(self, url):
|
||||||
|
return 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):
|
||||||
|
cdef char *j_title = NULL
|
||||||
|
cdef char *j_description = NULL
|
||||||
|
if title is not None:
|
||||||
|
j_title = <bytes>title
|
||||||
|
if description is not None:
|
||||||
|
j_description = <bytes>description
|
||||||
|
if arg is not None:
|
||||||
|
j_arg = <bytes>arg
|
||||||
|
android_start_service(j_title, j_description, j_arg)
|
||||||
|
|
||||||
|
cdef extern void android_stop_service()
|
||||||
|
def stop_service():
|
||||||
|
android_stop_service()
|
||||||
|
|
||||||
|
class AndroidService(object):
|
||||||
|
'''Android service class.
|
||||||
|
Run ``service/main.py`` from application directory as a service.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`title`: str, default to 'Python service'
|
||||||
|
Notification title.
|
||||||
|
|
||||||
|
`description`: str, default to 'Kivy Python service started'
|
||||||
|
Notification text.
|
||||||
|
'''
|
||||||
|
|
||||||
|
def __init__(self, title='Python service',
|
||||||
|
description='Kivy Python service started'):
|
||||||
|
self.title = title
|
||||||
|
self.description = description
|
||||||
|
|
||||||
|
def start(self, arg=''):
|
||||||
|
'''Start the service.
|
||||||
|
|
||||||
|
:Parameters:
|
||||||
|
`arg`: str, default to ''
|
||||||
|
Argument to pass to a service,
|
||||||
|
through environment variable ``PYTHON_SERVICE_ARGUMENT``.
|
||||||
|
'''
|
||||||
|
start_service(self.title, self.description, arg)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
'''Stop the service.
|
||||||
|
'''
|
||||||
|
stop_service()
|
||||||
|
|
||||||
|
|
81
MovedRecipes/android/src/android/_android_billing.pyx
Normal file
81
MovedRecipes/android/src/android/_android_billing.pyx
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
# -------------------------------------------------------------------
|
||||||
|
# Billing
|
||||||
|
cdef extern void android_billing_service_start()
|
||||||
|
cdef extern void android_billing_service_stop()
|
||||||
|
cdef extern void android_billing_buy(char *sku)
|
||||||
|
cdef extern char *android_billing_get_purchased_items()
|
||||||
|
cdef extern char *android_billing_get_pending_message()
|
||||||
|
|
||||||
|
class BillingService(object):
|
||||||
|
|
||||||
|
BILLING_ACTION_SUPPORTED = 'billingsupported'
|
||||||
|
BILLING_ACTION_ITEMSCHANGED = 'itemschanged'
|
||||||
|
|
||||||
|
BILLING_TYPE_INAPP = 'inapp'
|
||||||
|
BILLING_TYPE_SUBSCRIPTION = 'subs'
|
||||||
|
|
||||||
|
def __init__(self, callback):
|
||||||
|
super(BillingService, self).__init__()
|
||||||
|
self.callback = callback
|
||||||
|
self.purchased_items = None
|
||||||
|
android_billing_service_start()
|
||||||
|
|
||||||
|
def _stop(self):
|
||||||
|
android_billing_service_stop()
|
||||||
|
|
||||||
|
def buy(self, sku):
|
||||||
|
cdef char *j_sku = <bytes>sku
|
||||||
|
android_billing_buy(j_sku)
|
||||||
|
|
||||||
|
def get_purchased_items(self):
|
||||||
|
cdef char *items = NULL
|
||||||
|
cdef bytes pitem
|
||||||
|
items = android_billing_get_purchased_items()
|
||||||
|
if items == NULL:
|
||||||
|
return []
|
||||||
|
pitems = items
|
||||||
|
ret = {}
|
||||||
|
for item in pitems.split('\n'):
|
||||||
|
if not item:
|
||||||
|
continue
|
||||||
|
sku, qt = item.split(',')
|
||||||
|
ret[sku] = {'qt': int(qt)}
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def check(self, *largs):
|
||||||
|
cdef char *message
|
||||||
|
cdef bytes pymessage
|
||||||
|
|
||||||
|
while True:
|
||||||
|
message = android_billing_get_pending_message()
|
||||||
|
if message == NULL:
|
||||||
|
break
|
||||||
|
pymessage = <bytes>message
|
||||||
|
self._handle_message(pymessage)
|
||||||
|
|
||||||
|
if self.purchased_items is None:
|
||||||
|
self._check_new_items()
|
||||||
|
|
||||||
|
def _handle_message(self, message):
|
||||||
|
action, data = message.split('|', 1)
|
||||||
|
#print "HANDLE MESSAGE-----", (action, data)
|
||||||
|
|
||||||
|
if action == 'billingSupported':
|
||||||
|
tp, value = data.split('|')
|
||||||
|
value = True if value == '1' else False
|
||||||
|
self.callback(BillingService.BILLING_ACTION_SUPPORTED, tp, value)
|
||||||
|
|
||||||
|
elif action == 'requestPurchaseResponse':
|
||||||
|
self._check_new_items()
|
||||||
|
|
||||||
|
elif action == 'purchaseStateChange':
|
||||||
|
self._check_new_items()
|
||||||
|
|
||||||
|
elif action == 'restoreTransaction':
|
||||||
|
self._check_new_items()
|
||||||
|
|
||||||
|
def _check_new_items(self):
|
||||||
|
items = self.get_purchased_items()
|
||||||
|
if self.purchased_items != items:
|
||||||
|
self.purchased_items = items
|
||||||
|
self.callback(BillingService.BILLING_ACTION_ITEMSCHANGED, self.purchased_items)
|
120
MovedRecipes/android/src/android/_android_billing_jni.c
Normal file
120
MovedRecipes/android/src/android/_android_billing_jni.c
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
#include <jni.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <android/log.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#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); }
|
||||||
|
#define POP_FRAME { (*env)->PopLocalFrame(env, NULL); }
|
||||||
|
|
||||||
|
void android_billing_service_start() {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, JNI_NAMESPACE "/PythonActivity");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "billingServiceStart", "()V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
PUSH_FRAME;
|
||||||
|
(*env)->CallStaticVoidMethod(env, cls, mid);
|
||||||
|
POP_FRAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_billing_service_stop() {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, JNI_NAMESPACE "/PythonActivity");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "billingServiceStop", "()V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
PUSH_FRAME;
|
||||||
|
(*env)->CallStaticVoidMethod(env, cls, mid);
|
||||||
|
POP_FRAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_billing_buy(char *sku) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, JNI_NAMESPACE "/PythonActivity");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "billingBuy", "(Ljava/lang/String;)V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
PUSH_FRAME;
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(
|
||||||
|
env, cls, mid,
|
||||||
|
(*env)->NewStringUTF(env, sku)
|
||||||
|
);
|
||||||
|
|
||||||
|
POP_FRAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *android_billing_get_purchased_items() {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
jobject jreading;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, JNI_NAMESPACE "/PythonActivity");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "billingGetPurchasedItems", "()Ljava/lang/String;");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
PUSH_FRAME;
|
||||||
|
jreading = (*env)->CallStaticObjectMethod(env, cls, mid);
|
||||||
|
const char * reading = (*env)->GetStringUTFChars(env, jreading, 0);
|
||||||
|
POP_FRAME;
|
||||||
|
|
||||||
|
return reading;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *android_billing_get_pending_message() {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
jobject jreading;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, JNI_NAMESPACE "/PythonActivity");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "billingGetPendingMessage", "()Ljava/lang/String;");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
PUSH_FRAME;
|
||||||
|
jreading = (*env)->CallStaticObjectMethod(env, cls, mid);
|
||||||
|
const char * reading = (*env)->GetStringUTFChars(env, jreading, 0);
|
||||||
|
POP_FRAME;
|
||||||
|
|
||||||
|
return reading;
|
||||||
|
}
|
||||||
|
|
358
MovedRecipes/android/src/android/_android_jni.c
Normal file
358
MovedRecipes/android/src/android/_android_jni.c
Normal file
|
@ -0,0 +1,358 @@
|
||||||
|
#include <jni.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <android/log.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#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); }
|
||||||
|
#define POP_FRAME { (*env)->PopLocalFrame(env, NULL); }
|
||||||
|
|
||||||
|
void android_vibrate(double seconds) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/Hardware");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "vibrate", "(D)V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(
|
||||||
|
env, cls, mid,
|
||||||
|
(jdouble) seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_accelerometer_enable(int enable) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/Hardware");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "accelerometerEnable", "(Z)V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(
|
||||||
|
env, cls, mid,
|
||||||
|
(jboolean) enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_wifi_scanner_enable(void){
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/Hardware");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "enableWifiScanner", "()V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(env, cls, mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char * android_wifi_scan() {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
jobject jreading;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/Hardware");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "scanWifi", "()Ljava/lang/String;");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
PUSH_FRAME;
|
||||||
|
jreading = (*env)->CallStaticObjectMethod(env, cls, mid);
|
||||||
|
const char * reading = (*env)->GetStringUTFChars(env, jreading, 0);
|
||||||
|
POP_FRAME;
|
||||||
|
|
||||||
|
return reading;
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_accelerometer_reading(float *values) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
jobject jvalues;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/Hardware");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "accelerometerReading", "()[F");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
PUSH_FRAME;
|
||||||
|
|
||||||
|
jvalues = (*env)->CallStaticObjectMethod(env, cls, mid);
|
||||||
|
(*env)->GetFloatArrayRegion(env, jvalues, 0, 3, values);
|
||||||
|
|
||||||
|
POP_FRAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
int android_get_dpi(void) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/Hardware");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "getDPI", "()I");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*env)->CallStaticIntMethod(env, cls, mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_show_keyboard(int input_type) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/Hardware");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "showKeyboard", "(I)V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(env, cls, mid, (jint) input_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_hide_keyboard(void) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/Hardware");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "hideKeyboard", "()V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(env, cls, mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* BUILD_MANUFACTURER = NULL;
|
||||||
|
char* BUILD_MODEL = NULL;
|
||||||
|
char* BUILD_PRODUCT = NULL;
|
||||||
|
char* BUILD_VERSION_RELEASE = NULL;
|
||||||
|
|
||||||
|
void android_get_buildinfo() {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
jclass *cls = NULL;
|
||||||
|
jfieldID fid;
|
||||||
|
jstring sval;
|
||||||
|
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
|
||||||
|
cls = (*env)->FindClass(env, "android/os/Build");
|
||||||
|
|
||||||
|
fid = (*env)->GetStaticFieldID(env, cls, "MANUFACTURER", "Ljava/lang/String;");
|
||||||
|
sval = (jstring) (*env)->GetStaticObjectField(env, cls, fid);
|
||||||
|
BUILD_MANUFACTURER = (*env)->GetStringUTFChars(env, sval, 0);
|
||||||
|
|
||||||
|
fid = (*env)->GetStaticFieldID(env, cls, "MODEL", "Ljava/lang/String;");
|
||||||
|
sval = (jstring) (*env)->GetStaticObjectField(env, cls, fid);
|
||||||
|
BUILD_MODEL = (*env)->GetStringUTFChars(env, sval, 0);
|
||||||
|
|
||||||
|
fid = (*env)->GetStaticFieldID(env, cls, "PRODUCT", "Ljava/lang/String;");
|
||||||
|
sval = (jstring) (*env)->GetStaticObjectField(env, cls, fid);
|
||||||
|
BUILD_PRODUCT = (*env)->GetStringUTFChars(env, sval, 0);
|
||||||
|
|
||||||
|
cls = (*env)->FindClass(env, "android/os/Build$VERSION");
|
||||||
|
|
||||||
|
fid = (*env)->GetStaticFieldID(env, cls, "RELEASE", "Ljava/lang/String;");
|
||||||
|
sval = (jstring) (*env)->GetStaticObjectField(env, cls, fid);
|
||||||
|
BUILD_VERSION_RELEASE = (*env)->GetStringUTFChars(env, sval, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if IS_PYGAME
|
||||||
|
void android_activate_input(void) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/SDLSurfaceView");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "activateInput", "()V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(env, cls, mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
int android_checkstop(void) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/SDLSurfaceView");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "checkStop", "()I");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*env)->CallStaticIntMethod(env, cls, mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_ackstop(void) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/SDLSurfaceView");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "ackStop", "()I");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*env)->CallStaticIntMethod(env, cls, mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_action_send(char *mimeType, char *filename, char *subject, char *text, char *chooser_title) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/Action");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "send",
|
||||||
|
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
jstring j_mimeType = (*env)->NewStringUTF(env, mimeType);
|
||||||
|
jstring j_filename = NULL;
|
||||||
|
jstring j_subject = NULL;
|
||||||
|
jstring j_text = NULL;
|
||||||
|
jstring j_chooser_title = NULL;
|
||||||
|
if ( filename != NULL )
|
||||||
|
j_filename = (*env)->NewStringUTF(env, filename);
|
||||||
|
if ( subject != NULL )
|
||||||
|
j_subject = (*env)->NewStringUTF(env, subject);
|
||||||
|
if ( text != NULL )
|
||||||
|
j_text = (*env)->NewStringUTF(env, text);
|
||||||
|
if ( chooser_title != NULL )
|
||||||
|
j_chooser_title = (*env)->NewStringUTF(env, text);
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(
|
||||||
|
env, cls, mid,
|
||||||
|
j_mimeType, j_filename, j_subject, j_text,
|
||||||
|
j_chooser_title);
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_open_url(char *url) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/SDLSurfaceView");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "openUrl", "(Ljava/lang/String;)V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
PUSH_FRAME;
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(
|
||||||
|
env, cls, mid,
|
||||||
|
(*env)->NewStringUTF(env, url)
|
||||||
|
);
|
||||||
|
|
||||||
|
POP_FRAME;
|
||||||
|
}
|
||||||
|
#endif // IS_PYGAME
|
||||||
|
|
||||||
|
void android_start_service(char *title, char *description, char *arg) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
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");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
jstring j_title = NULL;
|
||||||
|
jstring j_description = NULL;
|
||||||
|
jstring j_arg = NULL;
|
||||||
|
if ( title != 0 )
|
||||||
|
j_title = (*env)->NewStringUTF(env, title);
|
||||||
|
if ( description != 0 )
|
||||||
|
j_description = (*env)->NewStringUTF(env, description);
|
||||||
|
if ( arg != 0 )
|
||||||
|
j_arg = (*env)->NewStringUTF(env, arg);
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(env, cls, mid, j_title, j_description, j_arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_stop_service() {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
cls = (*env)->FindClass(env, JNI_NAMESPACE "/PythonActivity");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "stop_service", "()V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(env, cls, mid);
|
||||||
|
}
|
125
MovedRecipes/android/src/android/_android_sound.pyx
Normal file
125
MovedRecipes/android/src/android/_android_sound.pyx
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
cdef extern void android_sound_queue(int, char *, char *, long long, long long)
|
||||||
|
cdef extern void android_sound_play(int, char *, char *, long long, long long)
|
||||||
|
cdef extern void android_sound_stop(int)
|
||||||
|
cdef extern void android_sound_seek(int, float)
|
||||||
|
cdef extern void android_sound_dequeue(int)
|
||||||
|
cdef extern void android_sound_playing_name(int, char *, int)
|
||||||
|
cdef extern void android_sound_pause(int)
|
||||||
|
cdef extern void android_sound_unpause(int)
|
||||||
|
|
||||||
|
cdef extern void android_sound_set_volume(int, float)
|
||||||
|
cdef extern void android_sound_set_secondary_volume(int, float)
|
||||||
|
cdef extern void android_sound_set_pan(int, float)
|
||||||
|
|
||||||
|
cdef extern int android_sound_queue_depth(int)
|
||||||
|
cdef extern int android_sound_get_pos(int)
|
||||||
|
cdef extern int android_sound_get_length(int)
|
||||||
|
|
||||||
|
channels = set()
|
||||||
|
volumes = { }
|
||||||
|
|
||||||
|
def queue(channel, file, name, fadein=0, tight=False):
|
||||||
|
|
||||||
|
channels.add(channel)
|
||||||
|
|
||||||
|
real_fn = file.name
|
||||||
|
base = getattr(file, "base", -1)
|
||||||
|
length = getattr(file, "length", -1)
|
||||||
|
|
||||||
|
android_sound_queue(channel, name, real_fn, base, length)
|
||||||
|
|
||||||
|
def play(channel, file, name, paused=False, fadein=0, tight=False):
|
||||||
|
|
||||||
|
channels.add(channel)
|
||||||
|
|
||||||
|
real_fn = file.name
|
||||||
|
base = getattr(file, "base", -1)
|
||||||
|
length = getattr(file, "length", -1)
|
||||||
|
|
||||||
|
android_sound_play(channel, name, real_fn, base, length)
|
||||||
|
|
||||||
|
def seek(channel, position):
|
||||||
|
android_sound_seek(channel, position)
|
||||||
|
|
||||||
|
def stop(channel):
|
||||||
|
android_sound_stop(channel)
|
||||||
|
|
||||||
|
def dequeue(channel, even_tight=False):
|
||||||
|
android_sound_dequeue(channel)
|
||||||
|
|
||||||
|
def queue_depth(channel):
|
||||||
|
return android_sound_queue_depth(channel)
|
||||||
|
|
||||||
|
def playing_name(channel):
|
||||||
|
cdef char buf[1024]
|
||||||
|
|
||||||
|
android_sound_playing_name(channel, buf, 1024)
|
||||||
|
|
||||||
|
rv = buf
|
||||||
|
if not len(rv):
|
||||||
|
return None
|
||||||
|
return rv
|
||||||
|
|
||||||
|
def pause(channel):
|
||||||
|
android_sound_pause(channel)
|
||||||
|
return
|
||||||
|
|
||||||
|
def unpause(channel):
|
||||||
|
android_sound_unpause(channel)
|
||||||
|
return
|
||||||
|
|
||||||
|
def unpause_all():
|
||||||
|
for i in channels:
|
||||||
|
unpause(i)
|
||||||
|
|
||||||
|
def pause_all():
|
||||||
|
for i in channels:
|
||||||
|
pause(i)
|
||||||
|
|
||||||
|
def fadeout(channel, ms):
|
||||||
|
stop(channel)
|
||||||
|
|
||||||
|
def busy(channel):
|
||||||
|
return playing_name(channel) != None
|
||||||
|
|
||||||
|
def get_pos(channel):
|
||||||
|
return android_sound_get_pos(channel)
|
||||||
|
|
||||||
|
def get_length(channel):
|
||||||
|
return android_sound_get_length(channel)
|
||||||
|
|
||||||
|
def set_volume(channel, volume):
|
||||||
|
android_sound_set_volume(channel, volume)
|
||||||
|
volumes[channel] = volume
|
||||||
|
|
||||||
|
def set_secondary_volume(channel, volume):
|
||||||
|
android_sound_set_secondary_volume(channel, volume)
|
||||||
|
|
||||||
|
def set_pan(channel, pan):
|
||||||
|
android_sound_set_pan(channel, pan)
|
||||||
|
|
||||||
|
def set_end_event(channel, event):
|
||||||
|
return
|
||||||
|
|
||||||
|
def get_volume(channel):
|
||||||
|
return volumes.get(channel, 1.0)
|
||||||
|
|
||||||
|
def init(freq, stereo, samples, status=False):
|
||||||
|
return
|
||||||
|
|
||||||
|
def quit():
|
||||||
|
for i in channels:
|
||||||
|
stop(i)
|
||||||
|
|
||||||
|
def periodic():
|
||||||
|
return
|
||||||
|
|
||||||
|
def alloc_event(surf):
|
||||||
|
return
|
||||||
|
|
||||||
|
def refresh_event():
|
||||||
|
return
|
||||||
|
|
||||||
|
def check_version(version):
|
||||||
|
return
|
||||||
|
|
308
MovedRecipes/android/src/android/_android_sound_jni.c
Normal file
308
MovedRecipes/android/src/android/_android_sound_jni.c
Normal file
|
@ -0,0 +1,308 @@
|
||||||
|
#include <jni.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <android/log.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
JNIEnv *SDL_ANDROID_GetJNIEnv();
|
||||||
|
|
||||||
|
#define aassert(x) { if (!x) { __android_log_print(ANDROID_LOG_ERROR, "android_sound_jni", "Assertion failed. %s:%d", __FILE__, __LINE__); abort(); }}
|
||||||
|
#define PUSH_FRAME { (*env)->PushLocalFrame(env, 16); }
|
||||||
|
#define POP_FRAME { (*env)->PopLocalFrame(env, NULL); }
|
||||||
|
|
||||||
|
|
||||||
|
void android_sound_queue(int channel, char *filename, char *real_fn, long long base, long long length) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/RenPySound");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "queue", "(ILjava/lang/String;Ljava/lang/String;JJ)V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
PUSH_FRAME;
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(
|
||||||
|
env, cls, mid,
|
||||||
|
channel,
|
||||||
|
(*env)->NewStringUTF(env, filename),
|
||||||
|
(*env)->NewStringUTF(env, real_fn),
|
||||||
|
(jlong) base,
|
||||||
|
(jlong) length);
|
||||||
|
|
||||||
|
POP_FRAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_sound_play(int channel, char *filename, char *real_fn, long long base, long long length) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/RenPySound");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "play", "(ILjava/lang/String;Ljava/lang/String;JJ)V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
PUSH_FRAME;
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(
|
||||||
|
env, cls, mid,
|
||||||
|
channel,
|
||||||
|
(*env)->NewStringUTF(env, filename),
|
||||||
|
(*env)->NewStringUTF(env, real_fn),
|
||||||
|
(jlong) base,
|
||||||
|
(jlong) length);
|
||||||
|
|
||||||
|
POP_FRAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_sound_seek(int channel, float position){
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/RenPySound");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "seek", "(IF)V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(
|
||||||
|
env, cls, mid,
|
||||||
|
channel,
|
||||||
|
(jfloat) position);
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_sound_stop(int channel) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/RenPySound");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "stop", "(I)V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(
|
||||||
|
env, cls, mid,
|
||||||
|
channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_sound_dequeue(int channel) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/RenPySound");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "dequeue", "(I)V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(
|
||||||
|
env, cls, mid,
|
||||||
|
channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
int android_sound_queue_depth(int channel) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/RenPySound");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "queue_depth", "(I)I");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*env)->CallStaticIntMethod(
|
||||||
|
env, cls, mid,
|
||||||
|
channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_sound_playing_name(int channel, char *buf, int buflen) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
jobject s = NULL;
|
||||||
|
char *jbuf;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/RenPySound");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "playing_name", "(I)Ljava/lang/String;");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
PUSH_FRAME;
|
||||||
|
|
||||||
|
s = (*env)->CallStaticObjectMethod(
|
||||||
|
env, cls, mid,
|
||||||
|
channel);
|
||||||
|
|
||||||
|
jbuf = (*env)->GetStringUTFChars(env, s, NULL);
|
||||||
|
strncpy(buf, jbuf, buflen);
|
||||||
|
(*env)->ReleaseStringUTFChars(env, s, jbuf);
|
||||||
|
|
||||||
|
POP_FRAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_sound_set_volume(int channel, float value) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/RenPySound");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "set_volume", "(IF)V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(
|
||||||
|
env, cls, mid,
|
||||||
|
channel,
|
||||||
|
(jfloat) value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_sound_set_secondary_volume(int channel, float value) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/RenPySound");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "set_secondary_volume", "(IF)V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(
|
||||||
|
env, cls, mid,
|
||||||
|
channel,
|
||||||
|
(jfloat) value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_sound_set_pan(int channel, float value) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/RenPySound");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "set_pan", "(IF)V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(
|
||||||
|
env, cls, mid,
|
||||||
|
channel,
|
||||||
|
(jfloat) value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_sound_pause(int channel) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/RenPySound");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "pause", "(I)V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(
|
||||||
|
env, cls, mid,
|
||||||
|
channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void android_sound_unpause(int channel) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/RenPySound");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "unpause", "(I)V");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(
|
||||||
|
env, cls, mid,
|
||||||
|
channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
int android_sound_get_pos(int channel) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/RenPySound");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "get_pos", "(I)I");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*env)->CallStaticIntMethod(
|
||||||
|
env, cls, mid,
|
||||||
|
channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
int android_sound_get_length(int channel) {
|
||||||
|
static JNIEnv *env = NULL;
|
||||||
|
static jclass *cls = NULL;
|
||||||
|
static jmethodID mid = NULL;
|
||||||
|
|
||||||
|
if (env == NULL) {
|
||||||
|
env = SDL_ANDROID_GetJNIEnv();
|
||||||
|
aassert(env);
|
||||||
|
cls = (*env)->FindClass(env, "org/renpy/android/RenPySound");
|
||||||
|
aassert(cls);
|
||||||
|
mid = (*env)->GetStaticMethodID(env, cls, "get_length", "(I)I");
|
||||||
|
aassert(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*env)->CallStaticIntMethod(
|
||||||
|
env, cls, mid,
|
||||||
|
channel);
|
||||||
|
}
|
61
MovedRecipes/android/src/android/activity.py
Normal file
61
MovedRecipes/android/src/android/activity.py
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
from jnius import PythonJavaClass, java_method, autoclass, cast
|
||||||
|
from android.config import JAVA_NAMESPACE, JNI_NAMESPACE
|
||||||
|
|
||||||
|
_activity = autoclass(JAVA_NAMESPACE + '.PythonActivity').mActivity
|
||||||
|
|
||||||
|
_callbacks = {
|
||||||
|
'on_new_intent': [],
|
||||||
|
'on_activity_result': [] }
|
||||||
|
|
||||||
|
class NewIntentListener(PythonJavaClass):
|
||||||
|
__javainterfaces__ = [JNI_NAMESPACE + '/PythonActivity$NewIntentListener']
|
||||||
|
__javacontext__ = 'app'
|
||||||
|
|
||||||
|
def __init__(self, callback, **kwargs):
|
||||||
|
super(NewIntentListener, self).__init__(**kwargs)
|
||||||
|
self.callback = callback
|
||||||
|
|
||||||
|
@java_method('(Landroid/content/Intent;)V')
|
||||||
|
def onNewIntent(self, intent):
|
||||||
|
self.callback(intent)
|
||||||
|
|
||||||
|
|
||||||
|
class ActivityResultListener(PythonJavaClass):
|
||||||
|
__javainterfaces__ = [JNI_NAMESPACE + '/PythonActivity$ActivityResultListener']
|
||||||
|
__javacontext__ = 'app'
|
||||||
|
|
||||||
|
def __init__(self, callback):
|
||||||
|
super(ActivityResultListener, self).__init__()
|
||||||
|
self.callback = callback
|
||||||
|
|
||||||
|
@java_method('(IILandroid/content/Intent;)V')
|
||||||
|
def onActivityResult(self, requestCode, resultCode, intent):
|
||||||
|
self.callback(requestCode, resultCode, intent)
|
||||||
|
|
||||||
|
|
||||||
|
def bind(**kwargs):
|
||||||
|
for event, callback in kwargs.items():
|
||||||
|
if event not in _callbacks:
|
||||||
|
raise Exception('Unknown {!r} event'.format(event))
|
||||||
|
elif event == 'on_new_intent':
|
||||||
|
listener = NewIntentListener(callback)
|
||||||
|
_activity.registerNewIntentListener(listener)
|
||||||
|
_callbacks[event].append(listener)
|
||||||
|
elif event == 'on_activity_result':
|
||||||
|
listener = ActivityResultListener(callback)
|
||||||
|
_activity.registerActivityResultListener(listener)
|
||||||
|
_callbacks[event].append(listener)
|
||||||
|
|
||||||
|
def unbind(**kwargs):
|
||||||
|
for event, callback in kwargs.items():
|
||||||
|
if event not in _callbacks:
|
||||||
|
raise Exception('Unknown {!r} event'.format(event))
|
||||||
|
else:
|
||||||
|
for listener in _callbacks[event][:]:
|
||||||
|
if listener.callback == callback:
|
||||||
|
_callbacks[event].remove(listener)
|
||||||
|
if event == 'on_new_intent':
|
||||||
|
_activity.unregisterNewIntentListener(listener)
|
||||||
|
elif event == 'on_activity_result':
|
||||||
|
_activity.unregisterActivityResultListener(listener)
|
||||||
|
|
7
MovedRecipes/android/src/android/billing.py
Normal file
7
MovedRecipes/android/src/android/billing.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
'''
|
||||||
|
Android Billing API
|
||||||
|
===================
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
from android._android_billing import *
|
79
MovedRecipes/android/src/android/broadcast.py
Normal file
79
MovedRecipes/android/src/android/broadcast.py
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
# -------------------------------------------------------------------
|
||||||
|
# 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__ = [JNI_NAMESPACE + '/GenericBroadcastReceiverCallback']
|
||||||
|
__javacontext__ = 'app'
|
||||||
|
|
||||||
|
def __init__(self, callback, *args, **kwargs):
|
||||||
|
self.callback = callback
|
||||||
|
PythonJavaClass.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
@java_method('(Landroid/content/Context;Landroid/content/Intent;)V')
|
||||||
|
def onReceive(self, context, intent):
|
||||||
|
self.callback(context, intent)
|
||||||
|
|
||||||
|
def __init__(self, callback, actions=None, categories=None):
|
||||||
|
super(BroadcastReceiver, self).__init__()
|
||||||
|
self.callback = callback
|
||||||
|
|
||||||
|
if not actions and not categories:
|
||||||
|
raise Exception('You need to define at least actions or categories')
|
||||||
|
|
||||||
|
def _expand_partial_name(partial_name):
|
||||||
|
if '.' in partial_name:
|
||||||
|
return partial_name # Its actually a full dotted name
|
||||||
|
else:
|
||||||
|
name = 'ACTION_{}'.format(partial_name.upper())
|
||||||
|
if not hasattr(Intent, name):
|
||||||
|
raise Exception('The intent {} doesnt exist'.format(name))
|
||||||
|
return getattr(Intent, name)
|
||||||
|
|
||||||
|
# resolve actions/categories first
|
||||||
|
Intent = autoclass('android.content.Intent')
|
||||||
|
resolved_actions = [_expand_partial_name(x) for x in actions or []]
|
||||||
|
resolved_categories = [_expand_partial_name(x) for x in categories or []]
|
||||||
|
|
||||||
|
# resolve android API
|
||||||
|
GenericBroadcastReceiver = autoclass(JAVA_NAMESPACE + '.GenericBroadcastReceiver')
|
||||||
|
IntentFilter = autoclass('android.content.IntentFilter')
|
||||||
|
HandlerThread = autoclass('android.os.HandlerThread')
|
||||||
|
|
||||||
|
# create a thread for handling events from the receiver
|
||||||
|
self.handlerthread = HandlerThread('handlerthread')
|
||||||
|
|
||||||
|
# create a listener
|
||||||
|
self.listener = BroadcastReceiver.Callback(self.callback)
|
||||||
|
self.receiver = GenericBroadcastReceiver(self.listener)
|
||||||
|
self.receiver_filter = IntentFilter()
|
||||||
|
for x in resolved_actions:
|
||||||
|
self.receiver_filter.addAction(x)
|
||||||
|
for x in resolved_categories:
|
||||||
|
self.receiver_filter.addCategory(x)
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
Handler = autoclass('android.os.Handler')
|
||||||
|
self.handlerthread.start()
|
||||||
|
self.handler = Handler(self.handlerthread.getLooper())
|
||||||
|
self.context.registerReceiver(self.receiver, self.receiver_filter, None,
|
||||||
|
self.handler)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.context.unregisterReceiver(self.receiver)
|
||||||
|
self.handlerthread.quit()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def context(self):
|
||||||
|
from os import environ
|
||||||
|
if 'PYTHON_SERVICE_ARGUMENT' in environ:
|
||||||
|
PythonService = autoclass(JAVA_NAMESPACE + '.PythonService')
|
||||||
|
return PythonService.mService
|
||||||
|
PythonActivity = autoclass(JAVA_NAMESPACE + '.PythonActivity')
|
||||||
|
return PythonActivity.mActivity
|
||||||
|
|
309
MovedRecipes/android/src/android/mixer.py
Normal file
309
MovedRecipes/android/src/android/mixer.py
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
# This module is, as much a possible, a clone of the pygame
|
||||||
|
# mixer api.
|
||||||
|
|
||||||
|
import android._android_sound as sound
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
import os
|
||||||
|
|
||||||
|
condition = threading.Condition()
|
||||||
|
|
||||||
|
def periodic():
|
||||||
|
for i in range(0, num_channels):
|
||||||
|
if i in channels:
|
||||||
|
channels[i].periodic()
|
||||||
|
|
||||||
|
num_channels = 8
|
||||||
|
reserved_channels = 0
|
||||||
|
|
||||||
|
def init(frequency=22050, size=-16, channels=2, buffer=4096):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def pre_init(frequency=22050, size=-16, channels=2, buffersize=4096):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def quit():
|
||||||
|
stop()
|
||||||
|
return None
|
||||||
|
|
||||||
|
def stop():
|
||||||
|
for i in range(0, num_channels):
|
||||||
|
sound.stop(i)
|
||||||
|
|
||||||
|
def pause():
|
||||||
|
for i in range(0, num_channels):
|
||||||
|
sound.pause(i)
|
||||||
|
|
||||||
|
def unpause():
|
||||||
|
for i in range(0, num_channels):
|
||||||
|
sound.unpause(i)
|
||||||
|
|
||||||
|
def get_busy():
|
||||||
|
for i in range(0, num_channels):
|
||||||
|
if sound.busy(i):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def fadeout(time):
|
||||||
|
# Fadeout doesn't work - it just immediately stops playback.
|
||||||
|
stop()
|
||||||
|
|
||||||
|
|
||||||
|
# A map from channel number to Channel object.
|
||||||
|
channels = { }
|
||||||
|
|
||||||
|
def set_num_channels(count):
|
||||||
|
global num_channels
|
||||||
|
num_channels = count
|
||||||
|
|
||||||
|
def get_num_channels(count):
|
||||||
|
return num_channels
|
||||||
|
|
||||||
|
def set_reserved(count):
|
||||||
|
global reserved_channels
|
||||||
|
reserved_channels = count
|
||||||
|
|
||||||
|
def find_channel(force=False):
|
||||||
|
|
||||||
|
busy = [ ]
|
||||||
|
|
||||||
|
for i in range(reserved_channels, num_channels):
|
||||||
|
c = Channel(i)
|
||||||
|
|
||||||
|
if not c.get_busy():
|
||||||
|
return c
|
||||||
|
|
||||||
|
busy.append(c)
|
||||||
|
|
||||||
|
if not force:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return min(busy, key=lambda x : x.play_time)
|
||||||
|
|
||||||
|
class ChannelImpl(object):
|
||||||
|
|
||||||
|
def __init__(self, id):
|
||||||
|
self.id = id
|
||||||
|
self.loop = None
|
||||||
|
self.queued = None
|
||||||
|
|
||||||
|
self.play_time = time.time()
|
||||||
|
|
||||||
|
def periodic(self):
|
||||||
|
qd = sound.queue_depth(self.id)
|
||||||
|
|
||||||
|
if qd < 2:
|
||||||
|
self.queued = None
|
||||||
|
|
||||||
|
if self.loop is not None and sound.queue_depth(self.id) < 2:
|
||||||
|
self.queue(self.loop, loops=1)
|
||||||
|
|
||||||
|
|
||||||
|
def play(self, s, loops=0, maxtime=0, fade_ms=0):
|
||||||
|
if loops:
|
||||||
|
self.loop = s
|
||||||
|
|
||||||
|
sound.play(self.id, s.file, s.serial)
|
||||||
|
|
||||||
|
self.play_time = time.time()
|
||||||
|
|
||||||
|
with condition:
|
||||||
|
condition.notify()
|
||||||
|
|
||||||
|
def seek(self, position):
|
||||||
|
sound.seek(self.id, position)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.loop = None
|
||||||
|
sound.stop(self.id)
|
||||||
|
|
||||||
|
def pause(self):
|
||||||
|
sound.pause(self.id)
|
||||||
|
|
||||||
|
def unpause(self):
|
||||||
|
sound.pause(self.id)
|
||||||
|
|
||||||
|
def fadeout(self, time):
|
||||||
|
# No fadeout
|
||||||
|
self.stop()
|
||||||
|
|
||||||
|
def set_volume(self, left, right=None):
|
||||||
|
sound.set_volume(self.id, left)
|
||||||
|
|
||||||
|
def get_volume(self):
|
||||||
|
return sound.get_volume(self.id)
|
||||||
|
|
||||||
|
def get_busy(self):
|
||||||
|
return sound.busy(self.id)
|
||||||
|
|
||||||
|
def get_sound(self):
|
||||||
|
is_busy = sound.busy(self.id)
|
||||||
|
if not is_busy:
|
||||||
|
return
|
||||||
|
serial = sound.playing_name(self.id)
|
||||||
|
if not serial:
|
||||||
|
return
|
||||||
|
return sounds.get(serial, None)
|
||||||
|
|
||||||
|
def queue(self, s):
|
||||||
|
self.loop = None
|
||||||
|
self.queued = s
|
||||||
|
|
||||||
|
sound.queue(self.id, s.what, s.serial)
|
||||||
|
|
||||||
|
with condition:
|
||||||
|
condition.notify()
|
||||||
|
|
||||||
|
def get_queue(self):
|
||||||
|
return self.queued
|
||||||
|
|
||||||
|
def get_pos(self):
|
||||||
|
return sound.get_pos(self.id)/1000.
|
||||||
|
|
||||||
|
def get_length(self):
|
||||||
|
return sound.get_length(self.id)/1000.
|
||||||
|
|
||||||
|
|
||||||
|
def Channel(n):
|
||||||
|
"""
|
||||||
|
Gets the channel with the given number.
|
||||||
|
"""
|
||||||
|
|
||||||
|
rv = channels.get(n, None)
|
||||||
|
if rv is None:
|
||||||
|
rv = ChannelImpl(n)
|
||||||
|
channels[n] = rv
|
||||||
|
|
||||||
|
return rv
|
||||||
|
|
||||||
|
|
||||||
|
sound_serial = 0
|
||||||
|
sounds = { }
|
||||||
|
|
||||||
|
class Sound(object):
|
||||||
|
|
||||||
|
def __init__(self, what):
|
||||||
|
|
||||||
|
# Doesn't support buffers.
|
||||||
|
|
||||||
|
global sound_serial
|
||||||
|
|
||||||
|
self._channel = None
|
||||||
|
self._volume = 1.
|
||||||
|
self.serial = str(sound_serial)
|
||||||
|
sound_serial += 1
|
||||||
|
|
||||||
|
if isinstance(what, file):
|
||||||
|
self.file = what
|
||||||
|
else:
|
||||||
|
self.file = file(os.path.abspath(what), "rb")
|
||||||
|
|
||||||
|
sounds[self.serial] = self
|
||||||
|
|
||||||
|
def play(self, loops=0, maxtime=0, fade_ms=0):
|
||||||
|
# avoid new play if the sound is already playing
|
||||||
|
# -> same behavior as standard pygame.
|
||||||
|
if self._channel is not None:
|
||||||
|
if self._channel.get_sound() is self:
|
||||||
|
return
|
||||||
|
self._channel = channel = find_channel(True)
|
||||||
|
channel.set_volume(self._volume)
|
||||||
|
channel.play(self, loops=loops)
|
||||||
|
return channel
|
||||||
|
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
for i in range(0, num_channels):
|
||||||
|
if Channel(i).get_sound() is self:
|
||||||
|
Channel(i).stop()
|
||||||
|
|
||||||
|
def fadeout(self, time):
|
||||||
|
self.stop()
|
||||||
|
|
||||||
|
def set_volume(self, left, right=None):
|
||||||
|
self._volume = left
|
||||||
|
if self._channel:
|
||||||
|
if self._channel.get_sound() is self:
|
||||||
|
self._channel.set_volume(self._volume)
|
||||||
|
|
||||||
|
def get_volume(self):
|
||||||
|
return self._volume
|
||||||
|
|
||||||
|
def get_num_channels(self):
|
||||||
|
rv = 0
|
||||||
|
|
||||||
|
for i in range(0, num_channels):
|
||||||
|
if Channel(i).get_sound() is self:
|
||||||
|
rv += 1
|
||||||
|
|
||||||
|
return rv
|
||||||
|
|
||||||
|
def get_length(self):
|
||||||
|
return 1.0
|
||||||
|
|
||||||
|
music_channel = Channel(256)
|
||||||
|
music_sound = None
|
||||||
|
|
||||||
|
class music(object):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load(filename):
|
||||||
|
|
||||||
|
music_channel.stop()
|
||||||
|
|
||||||
|
global music_sound
|
||||||
|
music_sound = Sound(filename)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def play(loops=0, start=0.0):
|
||||||
|
# No start.
|
||||||
|
|
||||||
|
music_channel.play(music_sound, loops=loops)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def rewind():
|
||||||
|
music_channel.play(music_sound)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def seek(position):
|
||||||
|
music_channel.seek(position)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def stop():
|
||||||
|
music_channel.stop()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def pause():
|
||||||
|
music_channel.pause()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def unpause():
|
||||||
|
music_channel.unpause()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def fadeout(time):
|
||||||
|
music_channel.fadeout(time)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_volume(value):
|
||||||
|
music_channel.set_volume(value)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_volume():
|
||||||
|
return music_channel.get_volume()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_busy():
|
||||||
|
return music_channel.get_busy()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_pos():
|
||||||
|
return music_channel.get_pos()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def queue(filename):
|
||||||
|
return music_channel.queue(Sound(filename))
|
||||||
|
|
||||||
|
|
||||||
|
|
48
MovedRecipes/android/src/android/runnable.py
Normal file
48
MovedRecipes/android/src/android/runnable.py
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
'''
|
||||||
|
Runnable
|
||||||
|
========
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
from jnius import PythonJavaClass, java_method, autoclass
|
||||||
|
from android.config import JAVA_NAMESPACE
|
||||||
|
|
||||||
|
# reference to the activity
|
||||||
|
_PythonActivity = autoclass(JAVA_NAMESPACE + '.PythonActivity')
|
||||||
|
|
||||||
|
|
||||||
|
class Runnable(PythonJavaClass):
|
||||||
|
'''Wrapper around Java Runnable class. This class can be used to schedule a
|
||||||
|
call of a Python function into the PythonActivity thread.
|
||||||
|
'''
|
||||||
|
|
||||||
|
__javainterfaces__ = ['java/lang/Runnable']
|
||||||
|
__runnables__ = []
|
||||||
|
|
||||||
|
def __init__(self, func):
|
||||||
|
super(Runnable, self).__init__()
|
||||||
|
self.func = func
|
||||||
|
|
||||||
|
def __call__(self, *args, **kwargs):
|
||||||
|
self.args = args
|
||||||
|
self.kwargs = kwargs
|
||||||
|
Runnable.__runnables__.append(self)
|
||||||
|
_PythonActivity.mActivity.runOnUiThread(self)
|
||||||
|
|
||||||
|
@java_method('()V')
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
self.func(*self.args, **self.kwargs)
|
||||||
|
except:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
Runnable.__runnables__.remove(self)
|
||||||
|
|
||||||
|
def run_on_ui_thread(f):
|
||||||
|
'''Decorator to create automatically a :class:`Runnable` object with the
|
||||||
|
function. The function will be delayed and call into the Activity thread.
|
||||||
|
'''
|
||||||
|
def f2(*args, **kwargs):
|
||||||
|
Runnable(f)(*args, **kwargs)
|
||||||
|
return f2
|
34
MovedRecipes/android/src/setup.py
Executable file
34
MovedRecipes/android/src/setup.py
Executable file
|
@ -0,0 +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']] if os.environ['BOOTSTRAP'] == 'sdl2' else []
|
||||||
|
|
||||||
|
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=modules
|
||||||
|
)
|
|
@ -87,171 +87,3 @@ class AndroidRecipe(IncludedFilesBehaviour, CythonRecipe):
|
||||||
|
|
||||||
|
|
||||||
recipe = AndroidRecipe()
|
recipe = AndroidRecipe()
|
||||||
|
|
||||||
'''
|
|
||||||
from pythonforandroid.recipe import CythonRecipe, IncludedFilesBehaviour
|
|
||||||
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):
|
|
||||||
# name = 'android'
|
|
||||||
version = None
|
|
||||||
url = None
|
|
||||||
|
|
||||||
src_filename = 'src'
|
|
||||||
|
|
||||||
depends = [('pygame', 'sdl2', 'genericndkbuild'), ('python2', 'python3crystax')]
|
|
||||||
|
|
||||||
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 = bootstrap_name = self.ctx.bootstrap.name
|
|
||||||
is_sdl2 = bootstrap_name in ('sdl2', 'sdl2python3', 'sdl2_gradle')
|
|
||||||
is_pygame = bootstrap_name in ('pygame',)
|
|
||||||
is_webview = bootstrap_name in ('webview',)
|
|
||||||
is_lbry = bootstrap_name in ('lbry',)
|
|
||||||
|
|
||||||
if is_sdl2 or is_webview or is_lbry:
|
|
||||||
if is_sdl2:
|
|
||||||
bootstrap = 'sdl2'
|
|
||||||
java_ns = 'org.kivy.android'
|
|
||||||
jni_ns = 'org/kivy/android'
|
|
||||||
elif is_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()
|
|
||||||
'''
|
|
||||||
|
|
||||||
'''
|
|
||||||
from pythonforandroid.recipe import CythonRecipe, Recipe, IncludedFilesBehaviour
|
|
||||||
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):
|
|
||||||
# name = 'android'
|
|
||||||
version = None
|
|
||||||
url = None
|
|
||||||
|
|
||||||
src_filename = 'src'
|
|
||||||
|
|
||||||
depends = [('pygame', 'sdl2', 'genericndkbuild'), ('python2', 'python3crystax')]
|
|
||||||
|
|
||||||
call_hostpython_via_targetpython = False
|
|
||||||
|
|
||||||
config_env = {}
|
|
||||||
|
|
||||||
def get_recipe_env(self, arch):
|
|
||||||
env = super(AndroidRecipe, self).get_recipe_env(arch)
|
|
||||||
env.update(self.config_env)
|
|
||||||
|
|
||||||
target_python = Recipe.get_recipe('python2', self.ctx).get_build_dir(arch.arch)
|
|
||||||
env['PYTHON_ROOT'] = join(target_python, 'python-install')
|
|
||||||
env['CFLAGS'] += ' -I' + env['PYTHON_ROOT'] + '/include/python2.7'
|
|
||||||
env['LDFLAGS'] += ' -L' + env['PYTHON_ROOT'] + '/lib' + ' -lpython2.7'
|
|
||||||
|
|
||||||
return env
|
|
||||||
|
|
||||||
def prebuild_arch(self, arch):
|
|
||||||
super(AndroidRecipe, self).prebuild_arch(arch)
|
|
||||||
|
|
||||||
tpxi = 'DEF {} = {}\n'
|
|
||||||
th = '#define {} {}\n'
|
|
||||||
tpy = '{} = {}\n'
|
|
||||||
|
|
||||||
bootstrap = bootstrap_name = self.ctx.bootstrap.name
|
|
||||||
is_sdl2 = bootstrap_name in ('sdl2', 'sdl2python3')
|
|
||||||
is_pygame = bootstrap_name in ('pygame',)
|
|
||||||
is_webview = bootstrap_name in ('webview')
|
|
||||||
is_lbry = bootstrap_name in ('lbry')
|
|
||||||
|
|
||||||
if is_sdl2 or is_webview or is_lbry:
|
|
||||||
if is_sdl2:
|
|
||||||
bootstrap = 'sdl2'
|
|
||||||
java_ns = 'org.kivy.android'
|
|
||||||
jni_ns = 'org/kivy/android'
|
|
||||||
elif is_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()
|
|
||||||
'''
|
|
|
@ -5,4 +5,4 @@ Android module
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# legacy import
|
# legacy import
|
||||||
from android._android import *
|
from android._android import * # noqa: F401, F403
|
||||||
|
|
|
@ -2,22 +2,6 @@
|
||||||
|
|
||||||
include "config.pxi"
|
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.
|
# Android keycodes.
|
||||||
KEYCODE_UNKNOWN = 0
|
KEYCODE_UNKNOWN = 0
|
||||||
KEYCODE_SOFT_LEFT = 1
|
KEYCODE_SOFT_LEFT = 1
|
||||||
|
@ -175,13 +159,11 @@ api_version = autoclass('android.os.Build$VERSION').SDK_INT
|
||||||
version_codes = autoclass('android.os.Build$VERSION_CODES')
|
version_codes = autoclass('android.os.Build$VERSION_CODES')
|
||||||
|
|
||||||
|
|
||||||
python_act = autoclass(JAVA_NAMESPACE + '.PythonActivity')
|
python_act = autoclass(ACTIVITY_CLASS_NAME)
|
||||||
Rect = autoclass('android.graphics.Rect')
|
Rect = autoclass(u'android.graphics.Rect')
|
||||||
mActivity = python_act.mActivity
|
mActivity = python_act.mActivity
|
||||||
if mActivity:
|
if mActivity:
|
||||||
# PyGame backend already has the listener so adding
|
# SDL2 now does not need the listener so there is
|
||||||
# one here leads to a crash/too much cpu usage.
|
|
||||||
# SDL2 now does noe need the listener so there is
|
|
||||||
# no point adding a processor intensive layout listenere here.
|
# no point adding a processor intensive layout listenere here.
|
||||||
height = 0
|
height = 0
|
||||||
def get_keyboard_height():
|
def get_keyboard_height():
|
||||||
|
@ -274,42 +256,6 @@ def get_buildinfo():
|
||||||
binfo.VERSION_RELEASE = BUILD_VERSION_RELEASE
|
binfo.VERSION_RELEASE = BUILD_VERSION_RELEASE
|
||||||
return binfo
|
return binfo
|
||||||
|
|
||||||
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.
|
# URL Opening.
|
||||||
def open_url(url):
|
def open_url(url):
|
||||||
|
@ -332,19 +278,31 @@ class AndroidBrowser(object):
|
||||||
return open_url(url)
|
return open_url(url)
|
||||||
|
|
||||||
import webbrowser
|
import webbrowser
|
||||||
webbrowser.register('android', AndroidBrowser, None, -1)
|
webbrowser.register('android', AndroidBrowser)
|
||||||
|
|
||||||
|
|
||||||
|
def start_service(title="Background Service",
|
||||||
|
description="", arg="",
|
||||||
|
as_foreground=True):
|
||||||
|
# Legacy None value support (for old function signature style):
|
||||||
|
if title is None:
|
||||||
|
title = "Background Service"
|
||||||
|
if description is None:
|
||||||
|
description = ""
|
||||||
|
if arg is None:
|
||||||
|
arg = ""
|
||||||
|
|
||||||
|
# Start service:
|
||||||
|
mActivity = autoclass(ACTIVITY_CLASS_NAME).mActivity
|
||||||
|
if as_foreground:
|
||||||
|
mActivity.start_service(
|
||||||
|
title, description, arg
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
mActivity.start_service_not_as_foreground(
|
||||||
|
title, description, arg
|
||||||
|
)
|
||||||
|
|
||||||
cdef extern void android_start_service(char *, char *, char *)
|
|
||||||
def start_service(title=None, description=None, arg=None):
|
|
||||||
cdef char *j_title = NULL
|
|
||||||
cdef char *j_description = NULL
|
|
||||||
if title is not None:
|
|
||||||
j_title = <bytes>title
|
|
||||||
if description is not None:
|
|
||||||
j_description = <bytes>description
|
|
||||||
if arg is not None:
|
|
||||||
j_arg = <bytes>arg
|
|
||||||
android_start_service(j_title, j_description, j_arg)
|
|
||||||
|
|
||||||
cdef extern void android_stop_service()
|
cdef extern void android_stop_service()
|
||||||
def stop_service():
|
def stop_service():
|
||||||
|
|
|
@ -15,7 +15,7 @@ class BillingService(object):
|
||||||
BILLING_TYPE_SUBSCRIPTION = 'subs'
|
BILLING_TYPE_SUBSCRIPTION = 'subs'
|
||||||
|
|
||||||
def __init__(self, callback):
|
def __init__(self, callback):
|
||||||
super(BillingService, self).__init__()
|
super().__init__()
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
self.purchased_items = None
|
self.purchased_items = None
|
||||||
android_billing_service_start()
|
android_billing_service_start()
|
||||||
|
|
|
@ -201,146 +201,6 @@ void android_get_buildinfo() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if IS_PYGAME
|
|
||||||
void android_activate_input(void) {
|
|
||||||
static JNIEnv *env = NULL;
|
|
||||||
static jclass *cls = NULL;
|
|
||||||
static jmethodID mid = NULL;
|
|
||||||
|
|
||||||
if (env == NULL) {
|
|
||||||
env = SDL_ANDROID_GetJNIEnv();
|
|
||||||
aassert(env);
|
|
||||||
cls = (*env)->FindClass(env, "org/renpy/android/SDLSurfaceView");
|
|
||||||
aassert(cls);
|
|
||||||
mid = (*env)->GetStaticMethodID(env, cls, "activateInput", "()V");
|
|
||||||
aassert(mid);
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, cls, mid);
|
|
||||||
}
|
|
||||||
|
|
||||||
int android_checkstop(void) {
|
|
||||||
static JNIEnv *env = NULL;
|
|
||||||
static jclass *cls = NULL;
|
|
||||||
static jmethodID mid = NULL;
|
|
||||||
|
|
||||||
if (env == NULL) {
|
|
||||||
env = SDL_ANDROID_GetJNIEnv();
|
|
||||||
aassert(env);
|
|
||||||
cls = (*env)->FindClass(env, "org/renpy/android/SDLSurfaceView");
|
|
||||||
aassert(cls);
|
|
||||||
mid = (*env)->GetStaticMethodID(env, cls, "checkStop", "()I");
|
|
||||||
aassert(mid);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (*env)->CallStaticIntMethod(env, cls, mid);
|
|
||||||
}
|
|
||||||
|
|
||||||
void android_ackstop(void) {
|
|
||||||
static JNIEnv *env = NULL;
|
|
||||||
static jclass *cls = NULL;
|
|
||||||
static jmethodID mid = NULL;
|
|
||||||
|
|
||||||
if (env == NULL) {
|
|
||||||
env = SDL_ANDROID_GetJNIEnv();
|
|
||||||
aassert(env);
|
|
||||||
cls = (*env)->FindClass(env, "org/renpy/android/SDLSurfaceView");
|
|
||||||
aassert(cls);
|
|
||||||
mid = (*env)->GetStaticMethodID(env, cls, "ackStop", "()I");
|
|
||||||
aassert(mid);
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticIntMethod(env, cls, mid);
|
|
||||||
}
|
|
||||||
|
|
||||||
void android_action_send(char *mimeType, char *filename, char *subject, char *text, char *chooser_title) {
|
|
||||||
static JNIEnv *env = NULL;
|
|
||||||
static jclass *cls = NULL;
|
|
||||||
static jmethodID mid = NULL;
|
|
||||||
|
|
||||||
if (env == NULL) {
|
|
||||||
env = SDL_ANDROID_GetJNIEnv();
|
|
||||||
aassert(env);
|
|
||||||
cls = (*env)->FindClass(env, "org/renpy/android/Action");
|
|
||||||
aassert(cls);
|
|
||||||
mid = (*env)->GetStaticMethodID(env, cls, "send",
|
|
||||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
|
|
||||||
aassert(mid);
|
|
||||||
}
|
|
||||||
|
|
||||||
jstring j_mimeType = (*env)->NewStringUTF(env, mimeType);
|
|
||||||
jstring j_filename = NULL;
|
|
||||||
jstring j_subject = NULL;
|
|
||||||
jstring j_text = NULL;
|
|
||||||
jstring j_chooser_title = NULL;
|
|
||||||
if ( filename != NULL )
|
|
||||||
j_filename = (*env)->NewStringUTF(env, filename);
|
|
||||||
if ( subject != NULL )
|
|
||||||
j_subject = (*env)->NewStringUTF(env, subject);
|
|
||||||
if ( text != NULL )
|
|
||||||
j_text = (*env)->NewStringUTF(env, text);
|
|
||||||
if ( chooser_title != NULL )
|
|
||||||
j_chooser_title = (*env)->NewStringUTF(env, text);
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(
|
|
||||||
env, cls, mid,
|
|
||||||
j_mimeType, j_filename, j_subject, j_text,
|
|
||||||
j_chooser_title);
|
|
||||||
}
|
|
||||||
|
|
||||||
void android_open_url(char *url) {
|
|
||||||
static JNIEnv *env = NULL;
|
|
||||||
static jclass *cls = NULL;
|
|
||||||
static jmethodID mid = NULL;
|
|
||||||
|
|
||||||
if (env == NULL) {
|
|
||||||
env = SDL_ANDROID_GetJNIEnv();
|
|
||||||
aassert(env);
|
|
||||||
cls = (*env)->FindClass(env, "org/renpy/android/SDLSurfaceView");
|
|
||||||
aassert(cls);
|
|
||||||
mid = (*env)->GetStaticMethodID(env, cls, "openUrl", "(Ljava/lang/String;)V");
|
|
||||||
aassert(mid);
|
|
||||||
}
|
|
||||||
|
|
||||||
PUSH_FRAME;
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(
|
|
||||||
env, cls, mid,
|
|
||||||
(*env)->NewStringUTF(env, url)
|
|
||||||
);
|
|
||||||
|
|
||||||
POP_FRAME;
|
|
||||||
}
|
|
||||||
#endif // IS_PYGAME
|
|
||||||
|
|
||||||
void android_start_service(char *title, char *description, char *arg) {
|
|
||||||
static JNIEnv *env = NULL;
|
|
||||||
static jclass *cls = NULL;
|
|
||||||
static jmethodID mid = NULL;
|
|
||||||
|
|
||||||
if (env == NULL) {
|
|
||||||
env = SDL_ANDROID_GetJNIEnv();
|
|
||||||
aassert(env);
|
|
||||||
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");
|
|
||||||
aassert(mid);
|
|
||||||
}
|
|
||||||
|
|
||||||
jstring j_title = NULL;
|
|
||||||
jstring j_description = NULL;
|
|
||||||
jstring j_arg = NULL;
|
|
||||||
if ( title != 0 )
|
|
||||||
j_title = (*env)->NewStringUTF(env, title);
|
|
||||||
if ( description != 0 )
|
|
||||||
j_description = (*env)->NewStringUTF(env, description);
|
|
||||||
if ( arg != 0 )
|
|
||||||
j_arg = (*env)->NewStringUTF(env, arg);
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, cls, mid, j_title, j_description, j_arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void android_stop_service() {
|
void android_stop_service() {
|
||||||
static JNIEnv *env = NULL;
|
static JNIEnv *env = NULL;
|
||||||
static jclass *cls = NULL;
|
static jclass *cls = NULL;
|
||||||
|
|
67
recipes/android/src/android/_ctypes_library_finder.py
Normal file
67
recipes/android/src/android/_ctypes_library_finder.py
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def get_activity_lib_dir(activity_name):
|
||||||
|
from jnius import autoclass
|
||||||
|
|
||||||
|
# Get the actual activity instance:
|
||||||
|
activity_class = autoclass(activity_name)
|
||||||
|
if activity_class is None:
|
||||||
|
return None
|
||||||
|
activity = None
|
||||||
|
if hasattr(activity_class, "mActivity") and \
|
||||||
|
activity_class.mActivity is not None:
|
||||||
|
activity = activity_class.mActivity
|
||||||
|
elif hasattr(activity_class, "mService") and \
|
||||||
|
activity_class.mService is not None:
|
||||||
|
activity = activity_class.mService
|
||||||
|
if activity is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Extract the native lib dir from the activity instance:
|
||||||
|
package_name = activity.getApplicationContext().getPackageName()
|
||||||
|
manager = activity.getApplicationContext().getPackageManager()
|
||||||
|
manager_class = autoclass("android.content.pm.PackageManager")
|
||||||
|
native_lib_dir = manager.getApplicationInfo(
|
||||||
|
package_name, manager_class.GET_SHARED_LIBRARY_FILES
|
||||||
|
).nativeLibraryDir
|
||||||
|
return native_lib_dir
|
||||||
|
|
||||||
|
|
||||||
|
def does_libname_match_filename(search_name, file_path):
|
||||||
|
# Filter file names so given search_name="mymodule" we match one of:
|
||||||
|
# mymodule.so (direct name + .so)
|
||||||
|
# libmymodule.so (added lib prefix)
|
||||||
|
# mymodule.arm64.so (added dot-separated middle parts)
|
||||||
|
# mymodule.so.1.3.4 (added dot-separated version tail)
|
||||||
|
# and all above (all possible combinations)
|
||||||
|
import re
|
||||||
|
file_name = os.path.basename(file_path)
|
||||||
|
return (re.match(r"^(lib)?" + re.escape(search_name) +
|
||||||
|
r"\.(.*\.)?so(\.[0-9]+)*$", file_name) is not None)
|
||||||
|
|
||||||
|
|
||||||
|
def find_library(name):
|
||||||
|
# Obtain all places for native libraries:
|
||||||
|
if sys.maxsize > 2**32: # 64bit-build
|
||||||
|
lib_search_dirs = ["/system/lib64", "/system/lib"]
|
||||||
|
else:
|
||||||
|
lib_search_dirs = ["/system/lib"]
|
||||||
|
lib_dir_1 = get_activity_lib_dir("org.kivy.android.PythonActivity")
|
||||||
|
if lib_dir_1 is not None:
|
||||||
|
lib_search_dirs.insert(0, lib_dir_1)
|
||||||
|
lib_dir_2 = get_activity_lib_dir("org.kivy.android.PythonService")
|
||||||
|
if lib_dir_2 is not None and lib_dir_2 not in lib_search_dirs:
|
||||||
|
lib_search_dirs.insert(0, lib_dir_2)
|
||||||
|
|
||||||
|
# Now scan the lib dirs:
|
||||||
|
for lib_dir in [ldir for ldir in lib_search_dirs if os.path.exists(ldir)]:
|
||||||
|
filelist = [
|
||||||
|
f for f in os.listdir(lib_dir)
|
||||||
|
if does_libname_match_filename(name, f)
|
||||||
|
]
|
||||||
|
if len(filelist) > 0:
|
||||||
|
return os.path.join(lib_dir, filelist[0])
|
||||||
|
return None
|
|
@ -1,18 +1,20 @@
|
||||||
from jnius import PythonJavaClass, java_method, autoclass, cast
|
from jnius import PythonJavaClass, autoclass, java_method
|
||||||
from android.config import JAVA_NAMESPACE, JNI_NAMESPACE
|
from android.config import ACTIVITY_CLASS_NAME, ACTIVITY_CLASS_NAMESPACE
|
||||||
|
|
||||||
_activity = autoclass(JAVA_NAMESPACE + '.PythonActivity').mActivity
|
_activity = autoclass(ACTIVITY_CLASS_NAME).mActivity
|
||||||
|
|
||||||
_callbacks = {
|
_callbacks = {
|
||||||
'on_new_intent': [],
|
'on_new_intent': [],
|
||||||
'on_activity_result': [] }
|
'on_activity_result': [],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class NewIntentListener(PythonJavaClass):
|
class NewIntentListener(PythonJavaClass):
|
||||||
__javainterfaces__ = [JNI_NAMESPACE + '/PythonActivity$NewIntentListener']
|
__javainterfaces__ = [ACTIVITY_CLASS_NAMESPACE + '$NewIntentListener']
|
||||||
__javacontext__ = 'app'
|
__javacontext__ = 'app'
|
||||||
|
|
||||||
def __init__(self, callback, **kwargs):
|
def __init__(self, callback, **kwargs):
|
||||||
super(NewIntentListener, self).__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
|
|
||||||
@java_method('(Landroid/content/Intent;)V')
|
@java_method('(Landroid/content/Intent;)V')
|
||||||
|
@ -21,11 +23,11 @@ class NewIntentListener(PythonJavaClass):
|
||||||
|
|
||||||
|
|
||||||
class ActivityResultListener(PythonJavaClass):
|
class ActivityResultListener(PythonJavaClass):
|
||||||
__javainterfaces__ = [JNI_NAMESPACE + '/PythonActivity$ActivityResultListener']
|
__javainterfaces__ = [ACTIVITY_CLASS_NAMESPACE + '$ActivityResultListener']
|
||||||
__javacontext__ = 'app'
|
__javacontext__ = 'app'
|
||||||
|
|
||||||
def __init__(self, callback):
|
def __init__(self, callback):
|
||||||
super(ActivityResultListener, self).__init__()
|
super().__init__()
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
|
|
||||||
@java_method('(IILandroid/content/Intent;)V')
|
@java_method('(IILandroid/content/Intent;)V')
|
||||||
|
@ -46,6 +48,7 @@ def bind(**kwargs):
|
||||||
_activity.registerActivityResultListener(listener)
|
_activity.registerActivityResultListener(listener)
|
||||||
_callbacks[event].append(listener)
|
_callbacks[event].append(listener)
|
||||||
|
|
||||||
|
|
||||||
def unbind(**kwargs):
|
def unbind(**kwargs):
|
||||||
for event, callback in kwargs.items():
|
for event, callback in kwargs.items():
|
||||||
if event not in _callbacks:
|
if event not in _callbacks:
|
||||||
|
@ -59,3 +62,153 @@ def unbind(**kwargs):
|
||||||
elif event == 'on_activity_result':
|
elif event == 'on_activity_result':
|
||||||
_activity.unregisterActivityResultListener(listener)
|
_activity.unregisterActivityResultListener(listener)
|
||||||
|
|
||||||
|
|
||||||
|
# Keep a reference to all the registered classes so that python doesn't
|
||||||
|
# garbage collect them.
|
||||||
|
_lifecycle_callbacks = set()
|
||||||
|
|
||||||
|
|
||||||
|
class ActivityLifecycleCallbacks(PythonJavaClass):
|
||||||
|
"""Callback class for handling PythonActivity lifecycle transitions"""
|
||||||
|
|
||||||
|
__javainterfaces__ = ['android/app/Application$ActivityLifecycleCallbacks']
|
||||||
|
|
||||||
|
def __init__(self, callbacks):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
# It would be nice to use keyword arguments, but PythonJavaClass
|
||||||
|
# doesn't allow that in its __cinit__ method.
|
||||||
|
if not isinstance(callbacks, dict):
|
||||||
|
raise ValueError('callbacks must be a dict instance')
|
||||||
|
self.callbacks = callbacks
|
||||||
|
|
||||||
|
def _callback(self, name, *args):
|
||||||
|
func = self.callbacks.get(name)
|
||||||
|
if func:
|
||||||
|
return func(*args)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;Landroid/os/Bundle;)V')
|
||||||
|
def onActivityCreated(self, activity, savedInstanceState):
|
||||||
|
self._callback('onActivityCreated', activity, savedInstanceState)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;)V')
|
||||||
|
def onActivityDestroyed(self, activity):
|
||||||
|
self._callback('onActivityDestroyed', activity)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;)V')
|
||||||
|
def onActivityPaused(self, activity):
|
||||||
|
self._callback('onActivityPaused', activity)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;Landroid/os/Bundle;)V')
|
||||||
|
def onActivityPostCreated(self, activity, savedInstanceState):
|
||||||
|
self._callback('onActivityPostCreated', activity, savedInstanceState)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;)V')
|
||||||
|
def onActivityPostDestroyed(self, activity):
|
||||||
|
self._callback('onActivityPostDestroyed', activity)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;)V')
|
||||||
|
def onActivityPostPaused(self, activity):
|
||||||
|
self._callback('onActivityPostPaused', activity)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;)V')
|
||||||
|
def onActivityPostResumed(self, activity):
|
||||||
|
self._callback('onActivityPostResumed', activity)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;Landroid/os/Bundle;)V')
|
||||||
|
def onActivityPostSaveInstanceState(self, activity, outState):
|
||||||
|
self._callback('onActivityPostSaveInstanceState', activity, outState)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;)V')
|
||||||
|
def onActivityPostStarted(self, activity):
|
||||||
|
self._callback('onActivityPostStarted', activity)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;)V')
|
||||||
|
def onActivityPostStopped(self, activity):
|
||||||
|
self._callback('onActivityPostStopped', activity)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;Landroid/os/Bundle;)V')
|
||||||
|
def onActivityPreCreated(self, activity, savedInstanceState):
|
||||||
|
self._callback('onActivityPreCreated', activity, savedInstanceState)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;)V')
|
||||||
|
def onActivityPreDestroyed(self, activity):
|
||||||
|
self._callback('onActivityPreDestroyed', activity)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;)V')
|
||||||
|
def onActivityPrePaused(self, activity):
|
||||||
|
self._callback('onActivityPrePaused', activity)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;)V')
|
||||||
|
def onActivityPreResumed(self, activity):
|
||||||
|
self._callback('onActivityPreResumed', activity)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;Landroid/os/Bundle;)V')
|
||||||
|
def onActivityPreSaveInstanceState(self, activity, outState):
|
||||||
|
self._callback('onActivityPreSaveInstanceState', activity, outState)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;)V')
|
||||||
|
def onActivityPreStarted(self, activity):
|
||||||
|
self._callback('onActivityPreStarted', activity)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;)V')
|
||||||
|
def onActivityPreStopped(self, activity):
|
||||||
|
self._callback('onActivityPreStopped', activity)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;)V')
|
||||||
|
def onActivityResumed(self, activity):
|
||||||
|
self._callback('onActivityResumed', activity)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;Landroid/os/Bundle;)V')
|
||||||
|
def onActivitySaveInstanceState(self, activity, outState):
|
||||||
|
self._callback('onActivitySaveInstanceState', activity, outState)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;)V')
|
||||||
|
def onActivityStarted(self, activity):
|
||||||
|
self._callback('onActivityStarted', activity)
|
||||||
|
|
||||||
|
@java_method('(Landroid/app/Activity;)V')
|
||||||
|
def onActivityStopped(self, activity):
|
||||||
|
self._callback('onActivityStopped', activity)
|
||||||
|
|
||||||
|
|
||||||
|
def register_activity_lifecycle_callbacks(**callbacks):
|
||||||
|
"""Register ActivityLifecycleCallbacks instance
|
||||||
|
|
||||||
|
The callbacks are supplied as keyword arguments corresponding to the
|
||||||
|
Application.ActivityLifecycleCallbacks methods such as
|
||||||
|
onActivityStarted. See the ActivityLifecycleCallbacks documentation
|
||||||
|
for the signature of each method.
|
||||||
|
|
||||||
|
The ActivityLifecycleCallbacks instance is returned so it can be
|
||||||
|
supplied to unregister_activity_lifecycle_callbacks if needed.
|
||||||
|
"""
|
||||||
|
instance = ActivityLifecycleCallbacks(callbacks)
|
||||||
|
_lifecycle_callbacks.add(instance)
|
||||||
|
|
||||||
|
# Use the registerActivityLifecycleCallbacks method from the
|
||||||
|
# Activity class if it's available (API 29) since it guarantees the
|
||||||
|
# callbacks will only be run for that activity. Otherwise, fallback
|
||||||
|
# to the method on the Application class (API 14). In practice there
|
||||||
|
# should be no difference since p4a applications only have a single
|
||||||
|
# activity.
|
||||||
|
if hasattr(_activity, 'registerActivityLifecycleCallbacks'):
|
||||||
|
_activity.registerActivityLifecycleCallbacks(instance)
|
||||||
|
else:
|
||||||
|
app = _activity.getApplication()
|
||||||
|
app.registerActivityLifecycleCallbacks(instance)
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
def unregister_activity_lifecycle_callbacks(instance):
|
||||||
|
"""Unregister ActivityLifecycleCallbacks instance"""
|
||||||
|
if hasattr(_activity, 'unregisterActivityLifecycleCallbacks'):
|
||||||
|
_activity.unregisterActivityLifecycleCallbacks(instance)
|
||||||
|
else:
|
||||||
|
app = _activity.getApplication()
|
||||||
|
app.unregisterActivityLifecycleCallbacks(instance)
|
||||||
|
|
||||||
|
try:
|
||||||
|
_lifecycle_callbacks.remove(instance)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
|
@ -3,5 +3,3 @@ Android Billing API
|
||||||
===================
|
===================
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from android._android_billing import *
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# Broadcast receiver bridge
|
# Broadcast receiver bridge
|
||||||
|
|
||||||
from jnius import autoclass, PythonJavaClass, java_method
|
from jnius import autoclass, PythonJavaClass, java_method
|
||||||
from android.config import JAVA_NAMESPACE, JNI_NAMESPACE
|
from android.config import JAVA_NAMESPACE, JNI_NAMESPACE, ACTIVITY_CLASS_NAME, SERVICE_CLASS_NAME
|
||||||
|
|
||||||
|
|
||||||
class BroadcastReceiver(object):
|
class BroadcastReceiver(object):
|
||||||
|
@ -20,7 +20,7 @@ class BroadcastReceiver(object):
|
||||||
self.callback(context, intent)
|
self.callback(context, intent)
|
||||||
|
|
||||||
def __init__(self, callback, actions=None, categories=None):
|
def __init__(self, callback, actions=None, categories=None):
|
||||||
super(BroadcastReceiver, self).__init__()
|
super().__init__()
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
|
|
||||||
if not actions and not categories:
|
if not actions and not categories:
|
||||||
|
@ -28,7 +28,7 @@ class BroadcastReceiver(object):
|
||||||
|
|
||||||
def _expand_partial_name(partial_name):
|
def _expand_partial_name(partial_name):
|
||||||
if '.' in partial_name:
|
if '.' in partial_name:
|
||||||
return partial_name # Its actually a full dotted name
|
return partial_name # Its actually a full dotted name
|
||||||
else:
|
else:
|
||||||
name = 'ACTION_{}'.format(partial_name.upper())
|
name = 'ACTION_{}'.format(partial_name.upper())
|
||||||
if not hasattr(Intent, name):
|
if not hasattr(Intent, name):
|
||||||
|
@ -61,8 +61,8 @@ class BroadcastReceiver(object):
|
||||||
Handler = autoclass('android.os.Handler')
|
Handler = autoclass('android.os.Handler')
|
||||||
self.handlerthread.start()
|
self.handlerthread.start()
|
||||||
self.handler = Handler(self.handlerthread.getLooper())
|
self.handler = Handler(self.handlerthread.getLooper())
|
||||||
self.context.registerReceiver(self.receiver, self.receiver_filter, None,
|
self.context.registerReceiver(
|
||||||
self.handler)
|
self.receiver, self.receiver_filter, None, self.handler)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.context.unregisterReceiver(self.receiver)
|
self.context.unregisterReceiver(self.receiver)
|
||||||
|
@ -72,8 +72,7 @@ class BroadcastReceiver(object):
|
||||||
def context(self):
|
def context(self):
|
||||||
from os import environ
|
from os import environ
|
||||||
if 'PYTHON_SERVICE_ARGUMENT' in environ:
|
if 'PYTHON_SERVICE_ARGUMENT' in environ:
|
||||||
PythonService = autoclass(JAVA_NAMESPACE + '.PythonService')
|
PythonService = autoclass(SERVICE_CLASS_NAME)
|
||||||
return PythonService.mService
|
return PythonService.mService
|
||||||
PythonActivity = autoclass(JAVA_NAMESPACE + '.PythonActivity')
|
PythonActivity = autoclass(ACTIVITY_CLASS_NAME)
|
||||||
return PythonActivity.mActivity
|
return PythonActivity.mActivity
|
||||||
|
|
||||||
|
|
9
recipes/android/src/android/loadingscreen.py
Normal file
9
recipes/android/src/android/loadingscreen.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
from jnius import autoclass
|
||||||
|
|
||||||
|
from android.config import ACTIVITY_CLASS_NAME
|
||||||
|
|
||||||
|
|
||||||
|
def hide_loading_screen():
|
||||||
|
mActivity = autoclass(ACTIVITY_CLASS_NAME).mActivity
|
||||||
|
mActivity.removeLoadingScreen()
|
|
@ -8,36 +8,45 @@ import os
|
||||||
|
|
||||||
condition = threading.Condition()
|
condition = threading.Condition()
|
||||||
|
|
||||||
|
|
||||||
def periodic():
|
def periodic():
|
||||||
for i in range(0, num_channels):
|
for i in range(0, num_channels):
|
||||||
if i in channels:
|
if i in channels:
|
||||||
channels[i].periodic()
|
channels[i].periodic()
|
||||||
|
|
||||||
|
|
||||||
num_channels = 8
|
num_channels = 8
|
||||||
reserved_channels = 0
|
reserved_channels = 0
|
||||||
|
|
||||||
|
|
||||||
def init(frequency=22050, size=-16, channels=2, buffer=4096):
|
def init(frequency=22050, size=-16, channels=2, buffer=4096):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def pre_init(frequency=22050, size=-16, channels=2, buffersize=4096):
|
def pre_init(frequency=22050, size=-16, channels=2, buffersize=4096):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def quit():
|
def quit():
|
||||||
stop()
|
stop()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def stop():
|
def stop():
|
||||||
for i in range(0, num_channels):
|
for i in range(0, num_channels):
|
||||||
sound.stop(i)
|
sound.stop(i)
|
||||||
|
|
||||||
|
|
||||||
def pause():
|
def pause():
|
||||||
for i in range(0, num_channels):
|
for i in range(0, num_channels):
|
||||||
sound.pause(i)
|
sound.pause(i)
|
||||||
|
|
||||||
|
|
||||||
def unpause():
|
def unpause():
|
||||||
for i in range(0, num_channels):
|
for i in range(0, num_channels):
|
||||||
sound.unpause(i)
|
sound.unpause(i)
|
||||||
|
|
||||||
|
|
||||||
def get_busy():
|
def get_busy():
|
||||||
for i in range(0, num_channels):
|
for i in range(0, num_channels):
|
||||||
if sound.busy(i):
|
if sound.busy(i):
|
||||||
|
@ -45,28 +54,33 @@ def get_busy():
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def fadeout(time):
|
def fadeout(time):
|
||||||
# Fadeout doesn't work - it just immediately stops playback.
|
# Fadeout doesn't work - it just immediately stops playback.
|
||||||
stop()
|
stop()
|
||||||
|
|
||||||
|
|
||||||
# A map from channel number to Channel object.
|
# A map from channel number to Channel object.
|
||||||
channels = { }
|
channels = {}
|
||||||
|
|
||||||
|
|
||||||
def set_num_channels(count):
|
def set_num_channels(count):
|
||||||
global num_channels
|
global num_channels
|
||||||
num_channels = count
|
num_channels = count
|
||||||
|
|
||||||
|
|
||||||
def get_num_channels(count):
|
def get_num_channels(count):
|
||||||
return num_channels
|
return num_channels
|
||||||
|
|
||||||
|
|
||||||
def set_reserved(count):
|
def set_reserved(count):
|
||||||
global reserved_channels
|
global reserved_channels
|
||||||
reserved_channels = count
|
reserved_channels = count
|
||||||
|
|
||||||
|
|
||||||
def find_channel(force=False):
|
def find_channel(force=False):
|
||||||
|
|
||||||
busy = [ ]
|
busy = []
|
||||||
|
|
||||||
for i in range(reserved_channels, num_channels):
|
for i in range(reserved_channels, num_channels):
|
||||||
c = Channel(i)
|
c = Channel(i)
|
||||||
|
@ -79,7 +93,10 @@ def find_channel(force=False):
|
||||||
if not force:
|
if not force:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return min(busy, key=lambda x : x.play_time)
|
busy.sort(key=lambda x: x.play_time)
|
||||||
|
|
||||||
|
return busy[0]
|
||||||
|
|
||||||
|
|
||||||
class ChannelImpl(object):
|
class ChannelImpl(object):
|
||||||
|
|
||||||
|
@ -99,7 +116,6 @@ class ChannelImpl(object):
|
||||||
if self.loop is not None and sound.queue_depth(self.id) < 2:
|
if self.loop is not None and sound.queue_depth(self.id) < 2:
|
||||||
self.queue(self.loop, loops=1)
|
self.queue(self.loop, loops=1)
|
||||||
|
|
||||||
|
|
||||||
def play(self, s, loops=0, maxtime=0, fade_ms=0):
|
def play(self, s, loops=0, maxtime=0, fade_ms=0):
|
||||||
if loops:
|
if loops:
|
||||||
self.loop = s
|
self.loop = s
|
||||||
|
@ -179,7 +195,8 @@ def Channel(n):
|
||||||
|
|
||||||
|
|
||||||
sound_serial = 0
|
sound_serial = 0
|
||||||
sounds = { }
|
sounds = {}
|
||||||
|
|
||||||
|
|
||||||
class Sound(object):
|
class Sound(object):
|
||||||
|
|
||||||
|
@ -194,10 +211,10 @@ class Sound(object):
|
||||||
self.serial = str(sound_serial)
|
self.serial = str(sound_serial)
|
||||||
sound_serial += 1
|
sound_serial += 1
|
||||||
|
|
||||||
if isinstance(what, file):
|
if isinstance(what, file): # noqa F821
|
||||||
self.file = what
|
self.file = what
|
||||||
else:
|
else:
|
||||||
self.file = file(os.path.abspath(what), "rb")
|
self.file = file(os.path.abspath(what), "rb") # noqa F821
|
||||||
|
|
||||||
sounds[self.serial] = self
|
sounds[self.serial] = self
|
||||||
|
|
||||||
|
@ -212,7 +229,6 @@ class Sound(object):
|
||||||
channel.play(self, loops=loops)
|
channel.play(self, loops=loops)
|
||||||
return channel
|
return channel
|
||||||
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
for i in range(0, num_channels):
|
for i in range(0, num_channels):
|
||||||
if Channel(i).get_sound() is self:
|
if Channel(i).get_sound() is self:
|
||||||
|
@ -242,9 +258,11 @@ class Sound(object):
|
||||||
def get_length(self):
|
def get_length(self):
|
||||||
return 1.0
|
return 1.0
|
||||||
|
|
||||||
|
|
||||||
music_channel = Channel(256)
|
music_channel = Channel(256)
|
||||||
music_sound = None
|
music_sound = None
|
||||||
|
|
||||||
|
|
||||||
class music(object):
|
class music(object):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -304,6 +322,3 @@ class music(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def queue(filename):
|
def queue(filename):
|
||||||
return music_channel.queue(Sound(filename))
|
return music_channel.queue(Sound(filename))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
618
recipes/android/src/android/permissions.py
Normal file
618
recipes/android/src/android/permissions.py
Normal file
|
@ -0,0 +1,618 @@
|
||||||
|
import threading
|
||||||
|
|
||||||
|
try:
|
||||||
|
from jnius import autoclass, PythonJavaClass, java_method
|
||||||
|
except ImportError:
|
||||||
|
# To allow importing by build/manifest-creating code without
|
||||||
|
# pyjnius being present:
|
||||||
|
def autoclass(item):
|
||||||
|
raise RuntimeError("pyjnius not available")
|
||||||
|
|
||||||
|
|
||||||
|
from android.config import ACTIVITY_CLASS_NAME, ACTIVITY_CLASS_NAMESPACE
|
||||||
|
|
||||||
|
|
||||||
|
class Permission:
|
||||||
|
ACCEPT_HANDOVER = "android.permission.ACCEPT_HANDOVER"
|
||||||
|
ACCESS_BACKGROUND_LOCATION = "android.permission.ACCESS_BACKGROUND_LOCATION"
|
||||||
|
ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION"
|
||||||
|
ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION"
|
||||||
|
ACCESS_LOCATION_EXTRA_COMMANDS = (
|
||||||
|
"android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"
|
||||||
|
)
|
||||||
|
ACCESS_NETWORK_STATE = "android.permission.ACCESS_NETWORK_STATE"
|
||||||
|
ACCESS_NOTIFICATION_POLICY = (
|
||||||
|
"android.permission.ACCESS_NOTIFICATION_POLICY"
|
||||||
|
)
|
||||||
|
ACCESS_WIFI_STATE = "android.permission.ACCESS_WIFI_STATE"
|
||||||
|
ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL"
|
||||||
|
ANSWER_PHONE_CALLS = "android.permission.ANSWER_PHONE_CALLS"
|
||||||
|
BATTERY_STATS = "android.permission.BATTERY_STATS"
|
||||||
|
BIND_ACCESSIBILITY_SERVICE = (
|
||||||
|
"android.permission.BIND_ACCESSIBILITY_SERVICE"
|
||||||
|
)
|
||||||
|
BIND_AUTOFILL_SERVICE = "android.permission.BIND_AUTOFILL_SERVICE"
|
||||||
|
BIND_CARRIER_MESSAGING_SERVICE = ( # note: deprecated in api 23+
|
||||||
|
"android.permission.BIND_CARRIER_MESSAGING_SERVICE"
|
||||||
|
)
|
||||||
|
BIND_CARRIER_SERVICES = ( # replaces BIND_CARRIER_MESSAGING_SERVICE
|
||||||
|
"android.permission.BIND_CARRIER_SERVICES"
|
||||||
|
)
|
||||||
|
BIND_CHOOSER_TARGET_SERVICE = (
|
||||||
|
"android.permission.BIND_CHOOSER_TARGET_SERVICE"
|
||||||
|
)
|
||||||
|
BIND_CONDITION_PROVIDER_SERVICE = (
|
||||||
|
"android.permission.BIND_CONDITION_PROVIDER_SERVICE"
|
||||||
|
)
|
||||||
|
BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN"
|
||||||
|
BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE"
|
||||||
|
BIND_INCALL_SERVICE = "android.permission.BIND_INCALL_SERVICE"
|
||||||
|
BIND_INPUT_METHOD = (
|
||||||
|
"android.permission.BIND_INPUT_METHOD"
|
||||||
|
)
|
||||||
|
BIND_MIDI_DEVICE_SERVICE = (
|
||||||
|
"android.permission.BIND_MIDI_DEVICE_SERVICE"
|
||||||
|
)
|
||||||
|
BIND_NFC_SERVICE = (
|
||||||
|
"android.permission.BIND_NFC_SERVICE"
|
||||||
|
)
|
||||||
|
BIND_NOTIFICATION_LISTENER_SERVICE = (
|
||||||
|
"android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
|
||||||
|
)
|
||||||
|
BIND_PRINT_SERVICE = (
|
||||||
|
"android.permission.BIND_PRINT_SERVICE"
|
||||||
|
)
|
||||||
|
BIND_QUICK_SETTINGS_TILE = (
|
||||||
|
"android.permission.BIND_QUICK_SETTINGS_TILE"
|
||||||
|
)
|
||||||
|
BIND_REMOTEVIEWS = (
|
||||||
|
"android.permission.BIND_REMOTEVIEWS"
|
||||||
|
)
|
||||||
|
BIND_SCREENING_SERVICE = (
|
||||||
|
"android.permission.BIND_SCREENING_SERVICE"
|
||||||
|
)
|
||||||
|
BIND_TELECOM_CONNECTION_SERVICE = (
|
||||||
|
"android.permission.BIND_TELECOM_CONNECTION_SERVICE"
|
||||||
|
)
|
||||||
|
BIND_TEXT_SERVICE = (
|
||||||
|
"android.permission.BIND_TEXT_SERVICE"
|
||||||
|
)
|
||||||
|
BIND_TV_INPUT = (
|
||||||
|
"android.permission.BIND_TV_INPUT"
|
||||||
|
)
|
||||||
|
BIND_VISUAL_VOICEMAIL_SERVICE = (
|
||||||
|
"android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"
|
||||||
|
)
|
||||||
|
BIND_VOICE_INTERACTION = (
|
||||||
|
"android.permission.BIND_VOICE_INTERACTION"
|
||||||
|
)
|
||||||
|
BIND_VPN_SERVICE = (
|
||||||
|
"android.permission.BIND_VPN_SERVICE"
|
||||||
|
)
|
||||||
|
BIND_VR_LISTENER_SERVICE = (
|
||||||
|
"android.permission.BIND_VR_LISTENER_SERVICE"
|
||||||
|
)
|
||||||
|
BIND_WALLPAPER = (
|
||||||
|
"android.permission.BIND_WALLPAPER"
|
||||||
|
)
|
||||||
|
BLUETOOTH = (
|
||||||
|
"android.permission.BLUETOOTH"
|
||||||
|
)
|
||||||
|
BLUETOOTH_ADVERTISE = (
|
||||||
|
"android.permission.BLUETOOTH_ADVERTISE"
|
||||||
|
)
|
||||||
|
BLUETOOTH_CONNECT = (
|
||||||
|
"android.permission.BLUETOOTH_CONNECT"
|
||||||
|
)
|
||||||
|
BLUETOOTH_SCAN = (
|
||||||
|
"android.permission.BLUETOOTH_SCAN"
|
||||||
|
)
|
||||||
|
BLUETOOTH_ADMIN = (
|
||||||
|
"android.permission.BLUETOOTH_ADMIN"
|
||||||
|
)
|
||||||
|
BODY_SENSORS = (
|
||||||
|
"android.permission.BODY_SENSORS"
|
||||||
|
)
|
||||||
|
BROADCAST_PACKAGE_REMOVED = (
|
||||||
|
"android.permission.BROADCAST_PACKAGE_REMOVED"
|
||||||
|
)
|
||||||
|
BROADCAST_STICKY = (
|
||||||
|
"android.permission.BROADCAST_STICKY"
|
||||||
|
)
|
||||||
|
CALL_PHONE = (
|
||||||
|
"android.permission.CALL_PHONE"
|
||||||
|
)
|
||||||
|
CALL_PRIVILEGED = (
|
||||||
|
"android.permission.CALL_PRIVILEGED"
|
||||||
|
)
|
||||||
|
CAMERA = (
|
||||||
|
"android.permission.CAMERA"
|
||||||
|
)
|
||||||
|
CAPTURE_AUDIO_OUTPUT = (
|
||||||
|
"android.permission.CAPTURE_AUDIO_OUTPUT"
|
||||||
|
)
|
||||||
|
CAPTURE_SECURE_VIDEO_OUTPUT = (
|
||||||
|
"android.permission.CAPTURE_SECURE_VIDEO_OUTPUT"
|
||||||
|
)
|
||||||
|
CAPTURE_VIDEO_OUTPUT = (
|
||||||
|
"android.permission.CAPTURE_VIDEO_OUTPUT"
|
||||||
|
)
|
||||||
|
CHANGE_COMPONENT_ENABLED_STATE = (
|
||||||
|
"android.permission.CHANGE_COMPONENT_ENABLED_STATE"
|
||||||
|
)
|
||||||
|
CHANGE_CONFIGURATION = (
|
||||||
|
"android.permission.CHANGE_CONFIGURATION"
|
||||||
|
)
|
||||||
|
CHANGE_NETWORK_STATE = (
|
||||||
|
"android.permission.CHANGE_NETWORK_STATE"
|
||||||
|
)
|
||||||
|
CHANGE_WIFI_MULTICAST_STATE = (
|
||||||
|
"android.permission.CHANGE_WIFI_MULTICAST_STATE"
|
||||||
|
)
|
||||||
|
CHANGE_WIFI_STATE = (
|
||||||
|
"android.permission.CHANGE_WIFI_STATE"
|
||||||
|
)
|
||||||
|
CLEAR_APP_CACHE = (
|
||||||
|
"android.permission.CLEAR_APP_CACHE"
|
||||||
|
)
|
||||||
|
CONTROL_LOCATION_UPDATES = (
|
||||||
|
"android.permission.CONTROL_LOCATION_UPDATES"
|
||||||
|
)
|
||||||
|
DELETE_CACHE_FILES = (
|
||||||
|
"android.permission.DELETE_CACHE_FILES"
|
||||||
|
)
|
||||||
|
DELETE_PACKAGES = (
|
||||||
|
"android.permission.DELETE_PACKAGES"
|
||||||
|
)
|
||||||
|
DIAGNOSTIC = (
|
||||||
|
"android.permission.DIAGNOSTIC"
|
||||||
|
)
|
||||||
|
DISABLE_KEYGUARD = (
|
||||||
|
"android.permission.DISABLE_KEYGUARD"
|
||||||
|
)
|
||||||
|
DUMP = (
|
||||||
|
"android.permission.DUMP"
|
||||||
|
)
|
||||||
|
EXPAND_STATUS_BAR = (
|
||||||
|
"android.permission.EXPAND_STATUS_BAR"
|
||||||
|
)
|
||||||
|
FACTORY_TEST = (
|
||||||
|
"android.permission.FACTORY_TEST"
|
||||||
|
)
|
||||||
|
FOREGROUND_SERVICE = (
|
||||||
|
"android.permission.FOREGROUND_SERVICE"
|
||||||
|
)
|
||||||
|
GET_ACCOUNTS = (
|
||||||
|
"android.permission.GET_ACCOUNTS"
|
||||||
|
)
|
||||||
|
GET_ACCOUNTS_PRIVILEGED = (
|
||||||
|
"android.permission.GET_ACCOUNTS_PRIVILEGED"
|
||||||
|
)
|
||||||
|
GET_PACKAGE_SIZE = (
|
||||||
|
"android.permission.GET_PACKAGE_SIZE"
|
||||||
|
)
|
||||||
|
GET_TASKS = (
|
||||||
|
"android.permission.GET_TASKS"
|
||||||
|
)
|
||||||
|
GLOBAL_SEARCH = (
|
||||||
|
"android.permission.GLOBAL_SEARCH"
|
||||||
|
)
|
||||||
|
INSTALL_LOCATION_PROVIDER = (
|
||||||
|
"android.permission.INSTALL_LOCATION_PROVIDER"
|
||||||
|
)
|
||||||
|
INSTALL_PACKAGES = (
|
||||||
|
"android.permission.INSTALL_PACKAGES"
|
||||||
|
)
|
||||||
|
INSTALL_SHORTCUT = (
|
||||||
|
"com.android.launcher.permission.INSTALL_SHORTCUT"
|
||||||
|
)
|
||||||
|
INSTANT_APP_FOREGROUND_SERVICE = (
|
||||||
|
"android.permission.INSTANT_APP_FOREGROUND_SERVICE"
|
||||||
|
)
|
||||||
|
INTERNET = (
|
||||||
|
"android.permission.INTERNET"
|
||||||
|
)
|
||||||
|
KILL_BACKGROUND_PROCESSES = (
|
||||||
|
"android.permission.KILL_BACKGROUND_PROCESSES"
|
||||||
|
)
|
||||||
|
LOCATION_HARDWARE = (
|
||||||
|
"android.permission.LOCATION_HARDWARE"
|
||||||
|
)
|
||||||
|
MANAGE_DOCUMENTS = (
|
||||||
|
"android.permission.MANAGE_DOCUMENTS"
|
||||||
|
)
|
||||||
|
MANAGE_OWN_CALLS = (
|
||||||
|
"android.permission.MANAGE_OWN_CALLS"
|
||||||
|
)
|
||||||
|
MASTER_CLEAR = (
|
||||||
|
"android.permission.MASTER_CLEAR"
|
||||||
|
)
|
||||||
|
MEDIA_CONTENT_CONTROL = (
|
||||||
|
"android.permission.MEDIA_CONTENT_CONTROL"
|
||||||
|
)
|
||||||
|
MODIFY_AUDIO_SETTINGS = (
|
||||||
|
"android.permission.MODIFY_AUDIO_SETTINGS"
|
||||||
|
)
|
||||||
|
MODIFY_PHONE_STATE = (
|
||||||
|
"android.permission.MODIFY_PHONE_STATE"
|
||||||
|
)
|
||||||
|
MOUNT_FORMAT_FILESYSTEMS = (
|
||||||
|
"android.permission.MOUNT_FORMAT_FILESYSTEMS"
|
||||||
|
)
|
||||||
|
MOUNT_UNMOUNT_FILESYSTEMS = (
|
||||||
|
"android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
|
||||||
|
)
|
||||||
|
NEARBY_WIFI_DEVICES = (
|
||||||
|
"android.permission.NEARBY_WIFI_DEVICES"
|
||||||
|
)
|
||||||
|
NFC = (
|
||||||
|
"android.permission.NFC"
|
||||||
|
)
|
||||||
|
NFC_TRANSACTION_EVENT = (
|
||||||
|
"android.permission.NFC_TRANSACTION_EVENT"
|
||||||
|
)
|
||||||
|
PACKAGE_USAGE_STATS = (
|
||||||
|
"android.permission.PACKAGE_USAGE_STATS"
|
||||||
|
)
|
||||||
|
PERSISTENT_ACTIVITY = (
|
||||||
|
"android.permission.PERSISTENT_ACTIVITY"
|
||||||
|
)
|
||||||
|
POST_NOTIFICATIONS = (
|
||||||
|
"android.permission.POST_NOTIFICATIONS"
|
||||||
|
)
|
||||||
|
PROCESS_OUTGOING_CALLS = (
|
||||||
|
"android.permission.PROCESS_OUTGOING_CALLS"
|
||||||
|
)
|
||||||
|
READ_CALENDAR = (
|
||||||
|
"android.permission.READ_CALENDAR"
|
||||||
|
)
|
||||||
|
READ_CALL_LOG = (
|
||||||
|
"android.permission.READ_CALL_LOG"
|
||||||
|
)
|
||||||
|
READ_CONTACTS = (
|
||||||
|
"android.permission.READ_CONTACTS"
|
||||||
|
)
|
||||||
|
READ_EXTERNAL_STORAGE = (
|
||||||
|
"android.permission.READ_EXTERNAL_STORAGE"
|
||||||
|
)
|
||||||
|
READ_FRAME_BUFFER = (
|
||||||
|
"android.permission.READ_FRAME_BUFFER"
|
||||||
|
)
|
||||||
|
READ_INPUT_STATE = (
|
||||||
|
"android.permission.READ_INPUT_STATE"
|
||||||
|
)
|
||||||
|
READ_LOGS = (
|
||||||
|
"android.permission.READ_LOGS"
|
||||||
|
)
|
||||||
|
READ_MEDIA_AUDIO = (
|
||||||
|
"android.permission.READ_MEDIA_AUDIO"
|
||||||
|
)
|
||||||
|
READ_MEDIA_IMAGES = (
|
||||||
|
"android.permission.READ_MEDIA_IMAGES"
|
||||||
|
)
|
||||||
|
READ_MEDIA_VIDEO = (
|
||||||
|
"android.permission.READ_MEDIA_VIDEO"
|
||||||
|
)
|
||||||
|
READ_PHONE_NUMBERS = (
|
||||||
|
"android.permission.READ_PHONE_NUMBERS"
|
||||||
|
)
|
||||||
|
READ_PHONE_STATE = (
|
||||||
|
"android.permission.READ_PHONE_STATE"
|
||||||
|
)
|
||||||
|
READ_SMS = (
|
||||||
|
"android.permission.READ_SMS"
|
||||||
|
)
|
||||||
|
READ_SYNC_SETTINGS = (
|
||||||
|
"android.permission.READ_SYNC_SETTINGS"
|
||||||
|
)
|
||||||
|
READ_SYNC_STATS = (
|
||||||
|
"android.permission.READ_SYNC_STATS"
|
||||||
|
)
|
||||||
|
READ_VOICEMAIL = (
|
||||||
|
"com.android.voicemail.permission.READ_VOICEMAIL"
|
||||||
|
)
|
||||||
|
REBOOT = (
|
||||||
|
"android.permission.REBOOT"
|
||||||
|
)
|
||||||
|
RECEIVE_BOOT_COMPLETED = (
|
||||||
|
"android.permission.RECEIVE_BOOT_COMPLETED"
|
||||||
|
)
|
||||||
|
RECEIVE_MMS = (
|
||||||
|
"android.permission.RECEIVE_MMS"
|
||||||
|
)
|
||||||
|
RECEIVE_SMS = (
|
||||||
|
"android.permission.RECEIVE_SMS"
|
||||||
|
)
|
||||||
|
RECEIVE_WAP_PUSH = (
|
||||||
|
"android.permission.RECEIVE_WAP_PUSH"
|
||||||
|
)
|
||||||
|
RECORD_AUDIO = (
|
||||||
|
"android.permission.RECORD_AUDIO"
|
||||||
|
)
|
||||||
|
REORDER_TASKS = (
|
||||||
|
"android.permission.REORDER_TASKS"
|
||||||
|
)
|
||||||
|
REQUEST_COMPANION_RUN_IN_BACKGROUND = (
|
||||||
|
"android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND"
|
||||||
|
)
|
||||||
|
REQUEST_COMPANION_USE_DATA_IN_BACKGROUND = (
|
||||||
|
"android.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND"
|
||||||
|
)
|
||||||
|
REQUEST_DELETE_PACKAGES = (
|
||||||
|
"android.permission.REQUEST_DELETE_PACKAGES"
|
||||||
|
)
|
||||||
|
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = (
|
||||||
|
"android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"
|
||||||
|
)
|
||||||
|
REQUEST_INSTALL_PACKAGES = (
|
||||||
|
"android.permission.REQUEST_INSTALL_PACKAGES"
|
||||||
|
)
|
||||||
|
RESTART_PACKAGES = (
|
||||||
|
"android.permission.RESTART_PACKAGES"
|
||||||
|
)
|
||||||
|
SEND_RESPOND_VIA_MESSAGE = (
|
||||||
|
"android.permission.SEND_RESPOND_VIA_MESSAGE"
|
||||||
|
)
|
||||||
|
SEND_SMS = (
|
||||||
|
"android.permission.SEND_SMS"
|
||||||
|
)
|
||||||
|
SET_ALARM = (
|
||||||
|
"com.android.alarm.permission.SET_ALARM"
|
||||||
|
)
|
||||||
|
SET_ALWAYS_FINISH = (
|
||||||
|
"android.permission.SET_ALWAYS_FINISH"
|
||||||
|
)
|
||||||
|
SET_ANIMATION_SCALE = (
|
||||||
|
"android.permission.SET_ANIMATION_SCALE"
|
||||||
|
)
|
||||||
|
SET_DEBUG_APP = (
|
||||||
|
"android.permission.SET_DEBUG_APP"
|
||||||
|
)
|
||||||
|
SET_PREFERRED_APPLICATIONS = (
|
||||||
|
"android.permission.SET_PREFERRED_APPLICATIONS"
|
||||||
|
)
|
||||||
|
SET_PROCESS_LIMIT = (
|
||||||
|
"android.permission.SET_PROCESS_LIMIT"
|
||||||
|
)
|
||||||
|
SET_TIME = (
|
||||||
|
"android.permission.SET_TIME"
|
||||||
|
)
|
||||||
|
SET_TIME_ZONE = (
|
||||||
|
"android.permission.SET_TIME_ZONE"
|
||||||
|
)
|
||||||
|
SET_WALLPAPER = (
|
||||||
|
"android.permission.SET_WALLPAPER"
|
||||||
|
)
|
||||||
|
SET_WALLPAPER_HINTS = (
|
||||||
|
"android.permission.SET_WALLPAPER_HINTS"
|
||||||
|
)
|
||||||
|
SIGNAL_PERSISTENT_PROCESSES = (
|
||||||
|
"android.permission.SIGNAL_PERSISTENT_PROCESSES"
|
||||||
|
)
|
||||||
|
STATUS_BAR = (
|
||||||
|
"android.permission.STATUS_BAR"
|
||||||
|
)
|
||||||
|
SYSTEM_ALERT_WINDOW = (
|
||||||
|
"android.permission.SYSTEM_ALERT_WINDOW"
|
||||||
|
)
|
||||||
|
TRANSMIT_IR = (
|
||||||
|
"android.permission.TRANSMIT_IR"
|
||||||
|
)
|
||||||
|
UNINSTALL_SHORTCUT = (
|
||||||
|
"com.android.launcher.permission.UNINSTALL_SHORTCUT"
|
||||||
|
)
|
||||||
|
UPDATE_DEVICE_STATS = (
|
||||||
|
"android.permission.UPDATE_DEVICE_STATS"
|
||||||
|
)
|
||||||
|
USE_BIOMETRIC = (
|
||||||
|
"android.permission.USE_BIOMETRIC"
|
||||||
|
)
|
||||||
|
USE_FINGERPRINT = (
|
||||||
|
"android.permission.USE_FINGERPRINT"
|
||||||
|
)
|
||||||
|
USE_SIP = (
|
||||||
|
"android.permission.USE_SIP"
|
||||||
|
)
|
||||||
|
VIBRATE = (
|
||||||
|
"android.permission.VIBRATE"
|
||||||
|
)
|
||||||
|
WAKE_LOCK = (
|
||||||
|
"android.permission.WAKE_LOCK"
|
||||||
|
)
|
||||||
|
WRITE_APN_SETTINGS = (
|
||||||
|
"android.permission.WRITE_APN_SETTINGS"
|
||||||
|
)
|
||||||
|
WRITE_CALENDAR = (
|
||||||
|
"android.permission.WRITE_CALENDAR"
|
||||||
|
)
|
||||||
|
WRITE_CALL_LOG = (
|
||||||
|
"android.permission.WRITE_CALL_LOG"
|
||||||
|
)
|
||||||
|
WRITE_CONTACTS = (
|
||||||
|
"android.permission.WRITE_CONTACTS"
|
||||||
|
)
|
||||||
|
WRITE_EXTERNAL_STORAGE = (
|
||||||
|
"android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
|
)
|
||||||
|
WRITE_GSERVICES = (
|
||||||
|
"android.permission.WRITE_GSERVICES"
|
||||||
|
)
|
||||||
|
WRITE_SECURE_SETTINGS = (
|
||||||
|
"android.permission.WRITE_SECURE_SETTINGS"
|
||||||
|
)
|
||||||
|
WRITE_SETTINGS = (
|
||||||
|
"android.permission.WRITE_SETTINGS"
|
||||||
|
)
|
||||||
|
WRITE_SYNC_SETTINGS = (
|
||||||
|
"android.permission.WRITE_SYNC_SETTINGS"
|
||||||
|
)
|
||||||
|
WRITE_VOICEMAIL = (
|
||||||
|
"com.android.voicemail.permission.WRITE_VOICEMAIL"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
PERMISSION_GRANTED = 0
|
||||||
|
PERMISSION_DENIED = -1
|
||||||
|
|
||||||
|
|
||||||
|
class _onRequestPermissionsCallback(PythonJavaClass):
|
||||||
|
"""Callback class for registering a Python callback from
|
||||||
|
onRequestPermissionsResult in PythonActivity.
|
||||||
|
"""
|
||||||
|
__javainterfaces__ = [ACTIVITY_CLASS_NAMESPACE + '$PermissionsCallback']
|
||||||
|
__javacontext__ = 'app'
|
||||||
|
|
||||||
|
def __init__(self, func):
|
||||||
|
self.func = func
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
@java_method('(I[Ljava/lang/String;[I)V')
|
||||||
|
def onRequestPermissionsResult(self, requestCode,
|
||||||
|
permissions, grantResults):
|
||||||
|
self.func(requestCode, permissions, grantResults)
|
||||||
|
|
||||||
|
|
||||||
|
class _RequestPermissionsManager:
|
||||||
|
"""Internal class for requesting Android permissions.
|
||||||
|
|
||||||
|
Permissions are requested through the method 'request_permissions' which
|
||||||
|
accepts a list of permissions and an optional callback.
|
||||||
|
|
||||||
|
Any callback will asynchronously receive arguments from
|
||||||
|
onRequestPermissionsResult on PythonActivity after requestPermissions is
|
||||||
|
called.
|
||||||
|
|
||||||
|
The callback supplied must accept two arguments: 'permissions' and
|
||||||
|
'grantResults' (as supplied to onPermissionsCallbackResult).
|
||||||
|
|
||||||
|
Note that for SDK_INT < 23, run-time permissions are not required, and so
|
||||||
|
the callback will be called immediately.
|
||||||
|
|
||||||
|
The attribute '_java_callback' is initially None, but is set when the first
|
||||||
|
permissions request is made. It is set to an instance of
|
||||||
|
onRequestPermissionsCallback, which allows the Java callback to be
|
||||||
|
propagated to the class method 'python_callback'. This is then, in turn,
|
||||||
|
used to call an application callback if provided to request_permissions.
|
||||||
|
|
||||||
|
The attribute '_callback_id' is incremented with each call to
|
||||||
|
request_permissions which has a callback (the value '1' is used for any
|
||||||
|
call which does not pass a callback). This is passed to requestCode in
|
||||||
|
the Java call, and used to identify (via the _callbacks dictionary)
|
||||||
|
the matching call.
|
||||||
|
"""
|
||||||
|
_SDK_INT = None
|
||||||
|
_java_callback = None
|
||||||
|
_callbacks = {1: None}
|
||||||
|
_callback_id = 1
|
||||||
|
# Lock to prevent multiple calls to request_permissions being handled
|
||||||
|
# simultaneously (as incrementing _callback_id is not atomic)
|
||||||
|
_lock = threading.Lock()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def register_callback(cls):
|
||||||
|
"""Register Java callback for requestPermissions."""
|
||||||
|
cls._java_callback = _onRequestPermissionsCallback(cls.python_callback)
|
||||||
|
mActivity = autoclass(ACTIVITY_CLASS_NAME).mActivity
|
||||||
|
mActivity.addPermissionsCallback(cls._java_callback)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def request_permissions(cls, permissions, callback=None):
|
||||||
|
"""Requests Android permissions from PythonActivity.
|
||||||
|
If 'callback' is supplied, the request is made with a new requestCode
|
||||||
|
and the callback is stored in the _callbacks dict. When a Java callback
|
||||||
|
with the matching requestCode is received, callback will be called
|
||||||
|
with arguments of 'permissions' and 'grant_results'.
|
||||||
|
"""
|
||||||
|
if not cls._SDK_INT:
|
||||||
|
# Get the Android build version and store it
|
||||||
|
VERSION = autoclass('android.os.Build$VERSION')
|
||||||
|
cls.SDK_INT = VERSION.SDK_INT
|
||||||
|
if cls.SDK_INT < 23:
|
||||||
|
# No run-time permissions needed, return immediately.
|
||||||
|
if callback:
|
||||||
|
callback(permissions, [True for x in permissions])
|
||||||
|
return
|
||||||
|
# Request permissions
|
||||||
|
with cls._lock:
|
||||||
|
if not cls._java_callback:
|
||||||
|
cls.register_callback()
|
||||||
|
mActivity = autoclass(ACTIVITY_CLASS_NAME).mActivity
|
||||||
|
if not callback:
|
||||||
|
mActivity.requestPermissions(permissions)
|
||||||
|
else:
|
||||||
|
cls._callback_id += 1
|
||||||
|
mActivity.requestPermissionsWithRequestCode(
|
||||||
|
permissions, cls._callback_id)
|
||||||
|
cls._callbacks[cls._callback_id] = callback
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def python_callback(cls, requestCode, permissions, grantResults):
|
||||||
|
"""Calls the relevant callback with arguments of 'permissions'
|
||||||
|
and 'grantResults'."""
|
||||||
|
# Convert from Android codes to True/False
|
||||||
|
grant_results = [x == PERMISSION_GRANTED for x in grantResults]
|
||||||
|
if cls._callbacks.get(requestCode):
|
||||||
|
cls._callbacks[requestCode](permissions, grant_results)
|
||||||
|
|
||||||
|
|
||||||
|
# Public API methods for requesting permissions
|
||||||
|
|
||||||
|
def request_permissions(permissions, callback=None):
|
||||||
|
"""Requests Android permissions.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
permissions (str): A list of permissions to requests (str)
|
||||||
|
callback (callable, optional): A function to call when the request
|
||||||
|
is completed (callable)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
Permission strings can be imported from the 'Permission' class in this
|
||||||
|
module. For example:
|
||||||
|
|
||||||
|
from android import Permission
|
||||||
|
permissions_list = [Permission.CAMERA,
|
||||||
|
Permission.WRITE_EXTERNAL_STORAGE]
|
||||||
|
|
||||||
|
See the p4a source file 'permissions.py' for a list of valid permission
|
||||||
|
strings (pythonforandroid/recipes/android/src/android/permissions.py).
|
||||||
|
|
||||||
|
Any callback supplied must accept two arguments:
|
||||||
|
permissions (list of str): A list of permission strings
|
||||||
|
grant_results (list of bool): A list of bools indicating whether the
|
||||||
|
respective permission was granted.
|
||||||
|
See Android documentation for onPermissionsCallbackResult for
|
||||||
|
further information.
|
||||||
|
|
||||||
|
Note that if the request is interupted the callback may contain an empty
|
||||||
|
list of permissions, without permissions being granted; the App should
|
||||||
|
check that each permission requested has been granted.
|
||||||
|
|
||||||
|
Also note that when calling request_permission on SDK_INT < 23, the
|
||||||
|
callback will be returned immediately as requesting permissions is not
|
||||||
|
required.
|
||||||
|
"""
|
||||||
|
_RequestPermissionsManager.request_permissions(permissions, callback)
|
||||||
|
|
||||||
|
|
||||||
|
def request_permission(permission, callback=None):
|
||||||
|
request_permissions([permission], callback)
|
||||||
|
|
||||||
|
|
||||||
|
def check_permission(permission):
|
||||||
|
"""Checks if an app holds the passed permission.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
- permission An Android permission (str)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if the app holds the permission given, False otherwise.
|
||||||
|
"""
|
||||||
|
mActivity = autoclass(ACTIVITY_CLASS_NAME).mActivity
|
||||||
|
result = bool(mActivity.checkCurrentPermission(
|
||||||
|
permission + ""
|
||||||
|
))
|
||||||
|
return result
|
|
@ -1,14 +1,17 @@
|
||||||
'''
|
'''
|
||||||
Runnable
|
Runnable
|
||||||
========
|
========
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from jnius import PythonJavaClass, java_method, autoclass
|
from jnius import PythonJavaClass, java_method, autoclass
|
||||||
from android.config import JAVA_NAMESPACE
|
from android.config import ACTIVITY_CLASS_NAME
|
||||||
|
|
||||||
# reference to the activity
|
# Reference to the activity
|
||||||
_PythonActivity = autoclass(JAVA_NAMESPACE + '.PythonActivity')
|
_PythonActivity = autoclass(ACTIVITY_CLASS_NAME)
|
||||||
|
|
||||||
|
# Cache of functions table. In older Android versions the number of JNI references
|
||||||
|
# is limited, so by caching them we avoid running out.
|
||||||
|
__functionstable__ = {}
|
||||||
|
|
||||||
|
|
||||||
class Runnable(PythonJavaClass):
|
class Runnable(PythonJavaClass):
|
||||||
|
@ -20,7 +23,7 @@ class Runnable(PythonJavaClass):
|
||||||
__runnables__ = []
|
__runnables__ = []
|
||||||
|
|
||||||
def __init__(self, func):
|
def __init__(self, func):
|
||||||
super(Runnable, self).__init__()
|
super().__init__()
|
||||||
self.func = func
|
self.func = func
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def __call__(self, *args, **kwargs):
|
||||||
|
@ -33,16 +36,23 @@ class Runnable(PythonJavaClass):
|
||||||
def run(self):
|
def run(self):
|
||||||
try:
|
try:
|
||||||
self.func(*self.args, **self.kwargs)
|
self.func(*self.args, **self.kwargs)
|
||||||
except:
|
except: # noqa E722
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
Runnable.__runnables__.remove(self)
|
Runnable.__runnables__.remove(self)
|
||||||
|
|
||||||
|
|
||||||
def run_on_ui_thread(f):
|
def run_on_ui_thread(f):
|
||||||
'''Decorator to create automatically a :class:`Runnable` object with the
|
'''Decorator to create automatically a :class:`Runnable` object with the
|
||||||
function. The function will be delayed and call into the Activity thread.
|
function. The function will be delayed and call into the Activity thread.
|
||||||
'''
|
'''
|
||||||
|
if f not in __functionstable__:
|
||||||
|
rfunction = Runnable(f) # store the runnable function
|
||||||
|
__functionstable__[f] = {"rfunction": rfunction}
|
||||||
|
rfunction = __functionstable__[f]["rfunction"]
|
||||||
|
|
||||||
def f2(*args, **kwargs):
|
def f2(*args, **kwargs):
|
||||||
Runnable(f)(*args, **kwargs)
|
rfunction(*args, **kwargs)
|
||||||
|
|
||||||
return f2
|
return f2
|
||||||
|
|
117
recipes/android/src/android/storage.py
Normal file
117
recipes/android/src/android/storage.py
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
from jnius import autoclass, cast
|
||||||
|
import os
|
||||||
|
|
||||||
|
from android.config import ACTIVITY_CLASS_NAME, SERVICE_CLASS_NAME
|
||||||
|
|
||||||
|
|
||||||
|
Environment = autoclass('android.os.Environment')
|
||||||
|
File = autoclass('java.io.File')
|
||||||
|
|
||||||
|
|
||||||
|
def _android_has_is_removable_func():
|
||||||
|
VERSION = autoclass('android.os.Build$VERSION')
|
||||||
|
return (VERSION.SDK_INT >= 24)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_sdcard_path():
|
||||||
|
""" Internal function to return getExternalStorageDirectory()
|
||||||
|
path. This is internal because it may either return the internal,
|
||||||
|
or an external sd card, depending on the device.
|
||||||
|
Use primary_external_storage_path()
|
||||||
|
or secondary_external_storage_path() instead which try to
|
||||||
|
distinguish this properly.
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
Environment.getExternalStorageDirectory().getAbsolutePath()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_activity():
|
||||||
|
"""
|
||||||
|
Retrieves the activity from `PythonActivity` fallback to `PythonService`.
|
||||||
|
"""
|
||||||
|
PythonActivity = autoclass(ACTIVITY_CLASS_NAME)
|
||||||
|
activity = PythonActivity.mActivity
|
||||||
|
if activity is None:
|
||||||
|
# assume we're running from the background service
|
||||||
|
PythonService = autoclass(SERVICE_CLASS_NAME)
|
||||||
|
activity = PythonService.mService
|
||||||
|
return activity
|
||||||
|
|
||||||
|
|
||||||
|
def app_storage_path():
|
||||||
|
""" Locate the built-in device storage used for this app only.
|
||||||
|
|
||||||
|
This storage is APP-SPECIFIC, and not visible to other apps.
|
||||||
|
It will be wiped when your app is uninstalled.
|
||||||
|
|
||||||
|
Returns directory path to storage.
|
||||||
|
"""
|
||||||
|
activity = _get_activity()
|
||||||
|
currentActivity = cast('android.app.Activity', activity)
|
||||||
|
context = cast('android.content.ContextWrapper',
|
||||||
|
currentActivity.getApplicationContext())
|
||||||
|
file_p = cast('java.io.File', context.getFilesDir())
|
||||||
|
return os.path.normpath(os.path.abspath(
|
||||||
|
file_p.getAbsolutePath().replace("/", os.path.sep)))
|
||||||
|
|
||||||
|
|
||||||
|
def primary_external_storage_path():
|
||||||
|
""" Locate the built-in device storage that user can see via file browser.
|
||||||
|
Often found at: /sdcard/
|
||||||
|
|
||||||
|
This is storage is SHARED, and visible to other apps and the user.
|
||||||
|
It will remain untouched when your app is uninstalled.
|
||||||
|
|
||||||
|
Returns directory path to storage.
|
||||||
|
|
||||||
|
WARNING: You need storage permissions to access this storage.
|
||||||
|
"""
|
||||||
|
if _android_has_is_removable_func():
|
||||||
|
sdpath = _get_sdcard_path()
|
||||||
|
# Apparently this can both return primary (built-in) or
|
||||||
|
# secondary (removable) external storage depending on the device,
|
||||||
|
# therefore check that we got what we wanted:
|
||||||
|
if not Environment.isExternalStorageRemovable(File(sdpath)):
|
||||||
|
return sdpath
|
||||||
|
if "EXTERNAL_STORAGE" in os.environ:
|
||||||
|
return os.environ["EXTERNAL_STORAGE"]
|
||||||
|
raise RuntimeError(
|
||||||
|
"unexpectedly failed to determine " +
|
||||||
|
"primary external storage path"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def secondary_external_storage_path():
|
||||||
|
""" Locate the external SD Card storage, which may not be present.
|
||||||
|
Often found at: /sdcard/External_SD/
|
||||||
|
|
||||||
|
This storage is SHARED, visible to other apps, and may not be
|
||||||
|
be available if the user didn't put in an external SD card.
|
||||||
|
It will remain untouched when your app is uninstalled.
|
||||||
|
|
||||||
|
Returns None if not found, otherwise path to storage.
|
||||||
|
|
||||||
|
WARNING: You need storage permissions to access this storage.
|
||||||
|
If it is not writable and presents as empty even with
|
||||||
|
permissions, then the external sd card may not be present.
|
||||||
|
"""
|
||||||
|
if _android_has_is_removable_func:
|
||||||
|
# See if getExternalStorageDirectory() returns secondary ext storage:
|
||||||
|
sdpath = _get_sdcard_path()
|
||||||
|
# Apparently this can both return primary (built-in) or
|
||||||
|
# secondary (removable) external storage depending on the device,
|
||||||
|
# therefore check that we got what we wanted:
|
||||||
|
if Environment.isExternalStorageRemovable(File(sdpath)):
|
||||||
|
if os.path.exists(sdpath):
|
||||||
|
return sdpath
|
||||||
|
|
||||||
|
# See if we can take a guess based on environment variables:
|
||||||
|
p = None
|
||||||
|
if "SECONDARY_STORAGE" in os.environ:
|
||||||
|
p = os.environ["SECONDARY_STORAGE"]
|
||||||
|
elif "EXTERNAL_SDCARD_STORAGE" in os.environ:
|
||||||
|
p = os.environ["EXTERNAL_SDCARD_STORAGE"]
|
||||||
|
if p is not None and os.path.exists(p):
|
||||||
|
return p
|
||||||
|
return None
|
|
@ -3,15 +3,9 @@ import os
|
||||||
|
|
||||||
library_dirs = ['libs/' + os.environ['ARCH']]
|
library_dirs = ['libs/' + os.environ['ARCH']]
|
||||||
lib_dict = {
|
lib_dict = {
|
||||||
'pygame': ['sdl'],
|
|
||||||
'sdl2': ['SDL2', 'SDL2_image', 'SDL2_mixer', 'SDL2_ttf']
|
'sdl2': ['SDL2', 'SDL2_image', 'SDL2_mixer', 'SDL2_ttf']
|
||||||
}
|
}
|
||||||
sdl_libs = lib_dict[os.environ['BOOTSTRAP']] if os.environ['BOOTSTRAP'] == 'sdl2' else []
|
sdl_libs = lib_dict.get(os.environ['BOOTSTRAP'], ['main'])
|
||||||
|
|
||||||
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',
|
modules = [Extension('android._android',
|
||||||
['android/_android.c', 'android/_android_jni.c'],
|
['android/_android.c', 'android/_android_jni.c'],
|
||||||
|
@ -22,10 +16,6 @@ modules = [Extension('android._android',
|
||||||
libraries=['log'],
|
libraries=['log'],
|
||||||
library_dirs=library_dirs)]
|
library_dirs=library_dirs)]
|
||||||
|
|
||||||
if int(os.environ['IS_PYGAME']):
|
|
||||||
modules.append(renpy_sound)
|
|
||||||
|
|
||||||
|
|
||||||
setup(name='android',
|
setup(name='android',
|
||||||
version='1.0',
|
version='1.0',
|
||||||
packages=['android'],
|
packages=['android'],
|
||||||
|
|
Loading…
Reference in a new issue