Upgrade p4a #19

Open
jessopb wants to merge 36 commits from upgrade_p4a into master
3445 changed files with 456206 additions and 3803 deletions

View file

@ -2,7 +2,7 @@ name: Publish Assets
on:
push:
branches: [master]
branches: [master, upgrade_p4a]
jobs:
build_arm64_aar:
@ -16,20 +16,14 @@ jobs:
cp -r /root/.buildozer ~/.buildozer/
- name: setup
run: |
apt update
apt install libssl-dev zip unzip openjdk-11-jdk -y
export B_VERSION=$(cat $GITHUB_WORKSPACE/src/main/python/main.py | grep --color=never -oP '([0-9]+\.?)+')
echo "NEXUS_SIGNING_KEYRING_FILE=$GITHUB_WORKSPACE/signing2.pgp" >> $GITHUB_ENV
echo "BUILD_VERSION=${B_VERSION}" >> $GITHUB_ENV
export PATH=/usr/bin:$PATH
wget -q 'https://eu.crystax.net/download/crystax-ndk-10.3.2-linux-x86_64.tar.xz' -P ~/.buildozer/android/
tar -xf ~/.buildozer/android/crystax-ndk-10.3.2-linux-x86_64.tar.xz -C ~/.buildozer/android/
rm -rf ~/.buildozer/android/crystax-ndk-10.3.2/platforms/android-9
ln -s ~/.buildozer/android/crystax-ndk-10.3.2/platforms/android-21 ~/.buildozer/android/crystax-ndk-10.3.2/platforms/android-9
cp -f $GITHUB_WORKSPACE/scripts/build-target-python.sh ~/.buildozer/android/crystax-ndk-10.3.2/build/tools/build-target-python.sh
cp -f $GITHUB_WORKSPACE/scripts/mangled-glibc-syscalls__arm64.h ~/.buildozer/android/crystax-ndk-10.3.2/platforms/android-21/arch-arm64/usr/include/crystax/bionic/libc/include/sys/mangled-glibc-syscalls.h
cp -f $GITHUB_WORKSPACE/scripts/build-binary.mk ~/.buildozer/android/crystax-ndk-10.3.2/build/core/build-binary.mk
rm -rf ~/.buildozer/android/crystax-ndk-10.3.2/sources/sqlite
cp -Rf $GITHUB_WORKSPACE/scripts/crystax-sources/sqlite ~/.buildozer/android/crystax-ndk-10.3.2/sources/sqlite
rm ~/.buildozer/android/crystax-ndk-10.3.2-linux-x86_64.tar.xz
wget -q 'https://dl.google.com/android/repository/android-ndk-r25b-linux.zip' -P ~/.buildozer/android/
unzip ~/.buildozer/android/android-ndk-r25b-linux.zip -d ~/.buildozer/android/
mv buildozer.spec.arm64.ci buildozer.spec
chmod u+x ./build-release.sh
- name: build release
@ -77,6 +71,7 @@ jobs:
echo "NEXUS_SIGNING_KEYRING_FILE=$GITHUB_WORKSPACE/signing2.pgp" >> $GITHUB_ENV
export PATH=/usr/bin:$PATH
wget -q 'https://eu.crystax.net/download/crystax-ndk-10.3.2-linux-x86_64.tar.xz' -P ~/.buildozer/android/
unzip ~/
tar -xf ~/.buildozer/android/crystax-ndk-10.3.2-linux-x86_64.tar.xz -C ~/.buildozer/android/
rm -rf ~/.buildozer/android/crystax-ndk-10.3.2/platforms/android-9
ln -s ~/.buildozer/android/crystax-ndk-10.3.2/platforms/android-21 ~/.buildozer/android/crystax-ndk-10.3.2/platforms/android-9

1
.gitignore vendored
View file

@ -19,3 +19,4 @@ p4a/pythonforandroid/bootstraps/lbry/build/templates/google-services.json
p4a/*.apk
p4a/*.aar
venv

0
MovedRecipes/__init__.py Normal file
View file

View 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()
'''

View file

View file

@ -0,0 +1,8 @@
'''
Android module
==============
'''
# legacy import
from android._android import *

View 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()

View 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)

View 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;
}

View 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);
}

View 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

View 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);
}

View 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)

View file

@ -0,0 +1,7 @@
'''
Android Billing API
===================
'''
from android._android_billing import *

View 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

View 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))

View 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

View 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
)

View file

@ -8,7 +8,7 @@ class CffiRecipe(CompiledComponentsPythonRecipe):
version = '1.14.6'
url = 'https://pypi.python.org/packages/source/c/cffi/cffi-{version}.tar.gz'
depends = [('python2', 'python3crystax'), 'setuptools', 'pycparser', 'libffi']
depends = [('python2', 'python3'), 'setuptools', 'pycparser', 'libffi']
patches = ['disable-pkg-config.patch']
@ -43,16 +43,16 @@ class CffiRecipe(CompiledComponentsPythonRecipe):
self.ctx.get_site_packages_dir(),
env['BUILDLIB_PATH'],
])
if self.ctx.ndk == 'crystax':
# only keeps major.minor (discards patch)
python_version = self.ctx.python_recipe.version[0:3]
ndk_dir_python = os.path.join(self.ctx.ndk_dir, 'sources/python/', python_version)
env['LDFLAGS'] += ' -L{}'.format(os.path.join(ndk_dir_python, 'libs', arch.arch))
env['LDFLAGS'] += ' -lpython{}'.format(python_version)
# until `pythonforandroid/archs.py` gets merged upstream:
# https://github.com/kivy/python-for-android/pull/1250/files#diff-569e13021e33ced8b54385f55b49cbe6
env['CFLAGS'] += ' -I{}/include/python/'.format(ndk_dir_python)
return env
# if self.ctx.ndk == 'crystax':
# # only keeps major.minor (discards patch)
# python_version = self.ctx.python_recipe.version[0:3]
# ndk_dir_python = os.path.join(self.ctx.ndk_dir, 'sources/python/', python_version)
# env['LDFLAGS'] += ' -L{}'.format(os.path.join(ndk_dir_python, 'libs', arch.arch))
# env['LDFLAGS'] += ' -lpython{}'.format(python_version)
# # until `pythonforandroid/archs.py` gets merged upstream:
# # https://github.com/kivy/python-for-android/pull/1250/files#diff-569e13021e33ced8b54385f55b49cbe6
# env['CFLAGS'] += ' -I{}/include/python/'.format(ndk_dir_python)
# return env
recipe = CffiRecipe()

View file

@ -0,0 +1,74 @@
import os
from pythonforandroid.recipe import PythonRecipe, CompiledComponentsPythonRecipe
class CoincurveRecipe(CompiledComponentsPythonRecipe):
# version = '15.0.0'
# url = 'https://github.com/ofek/coincurve/archive/{version}.tar.gz'
# call_hostpython_via_targetpython = False
# depends = ['setuptools',
# 'libffi', 'cffi', 'libsecp256k1']
# patches = [
# "cross_compile.patch", "drop_setup_requires.patch",
# "find_lib.patch", "no-download.patch", "setup.py.patch"]
#
# def get_recipe_env(self, arch=None, with_flags_in_cc=True):
# env = super().get_recipe_env(arch, with_flags_in_cc)
# # sets linker to use the correct gcc (cross compiler)
# env['LDSHARED'] = env['CC'] + ' -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions'
# libsecp256k1 = self.get_recipe('libsecp256k1', self.ctx)
# libsecp256k1_dir = libsecp256k1.get_build_dir(arch.arch)
# env['LDFLAGS'] += ' -L{}'.format(os.path.join(libsecp256k1_dir, '.libs'))
# env['CFLAGS'] += ' -I' + os.path.join(libsecp256k1_dir, 'include')
# # only keeps major.minor (discards patch)
# python_version = self.ctx.python_recipe.version[0:3]
# # required additional library and path for Crystax
# if self.ctx.ndk == 'crystax':
# ndk_dir_python = os.path.join(self.ctx.ndk_dir, 'sources/python/', python_version)
# env['LDFLAGS'] += ' -L{}'.format(os.path.join(ndk_dir_python, 'libs', arch.arch))
# env['LDFLAGS'] += ' -lpython{}'.format(python_version)
# # until `pythonforandroid/archs.py` gets merged upstream:
# # https://github.com/kivy/python-for-android/pull/1250/files#diff-569e13021e33ced8b54385f55b49cbe6
# env['CFLAGS'] += ' -I{}/include/python/'.format(ndk_dir_python)
# else:
# env['PYTHON_ROOT'] = self.ctx.get_python_install_dir(arch.arch)
# env['CFLAGS'] += ' -I' + env['PYTHON_ROOT'] + '/include/python{}'.format(python_version)
# env['LDFLAGS'] += " -lpython{}".format(python_version)
# env['LDFLAGS'] += " -lsecp256k1"
# return env
version = 'v15.0.1'
url = 'https://github.com/ofek/coincurve/archive/refs/tags/v15.0.1.tar.gz'
call_hostpython_via_targetpython = False
depends = [('python2', 'python3'), 'setuptools',
'libffi', 'cffi', 'libsecp256k1']
patches = [
"cross_compile.patch", "drop_setup_requires.patch",
"find_lib.patch", "no-download.patch", "setup.py.patch"]
def get_recipe_env(self, arch=None, with_flags_in_cc=True):
env = super().get_recipe_env(arch, with_flags_in_cc)
# sets linker to use the correct gcc (cross compiler)
env['LDSHARED'] = env['CC'] + ' -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions'
libsecp256k1 = self.get_recipe('libsecp256k1', self.ctx)
libsecp256k1_dir = libsecp256k1.get_build_dir(arch.arch)
env['LDFLAGS'] += ' -L{}'.format(os.path.join(libsecp256k1_dir, '.libs'))
env['CFLAGS'] += ' -I' + os.path.join(libsecp256k1_dir, 'include')
# only keeps major.minor (discards patch)
python_version = self.ctx.python_recipe.version[0:3]
# required additional library and path for Crystax
# if self.ctx.ndk == 'crystax':
# ndk_dir_python = os.path.join(self.ctx.ndk_dir, 'sources/python/', python_version)
# env['LDFLAGS'] += ' -L{}'.format(os.path.join(ndk_dir_python, 'libs', arch.arch))
# env['LDFLAGS'] += ' -lpython{}m'.format(python_version)
# # until `pythonforandroid/archs.py` gets merged upstream:
# # https://github.com/kivy/python-for-android/pull/1250/files#diff-569e13021e33ced8b54385f55b49cbe6
# env['CFLAGS'] += ' -I{}/include/python/'.format(ndk_dir_python)
# else:
env['PYTHON_ROOT'] = self.ctx.get_python_install_dir(arch.arch)
env['CFLAGS'] += ' -I' + env['PYTHON_ROOT'] + '/include/python{}'.format(python_version)
env['LDFLAGS'] += " -lpython{}".format(python_version)
env['LDFLAGS'] += " -lsecp256k1"
return env
recipe = CoincurveRecipe()

View file

@ -0,0 +1,12 @@
diff --git a/setup.py b/setup.py
index c224fb2..bf925bd 100644
--- a/setup.py
+++ b/setup.py
@@ -182,6 +182,7 @@ class build_clib(_build_clib):
'--disable-dependency-tracking',
'--with-pic',
'--enable-module-recovery',
+ "--host=%s" % os.environ['TOOLCHAIN_PREFIX'],
'--disable-jni',
'--prefix',
os.path.abspath(self.build_clib),

View file

@ -0,0 +1,12 @@
diff --git a/setup.py b/setup.py
index c224fb2..466e789 100644
--- a/setup.py
+++ b/setup.py
@@ -250,7 +250,6 @@ else:
def has_c_libraries(self):
return not has_system_lib()
setup_kwargs = dict(
- setup_requires=['cffi>=1.3.0', 'pytest-runner>=2.6.2'],
ext_package='coincurve',
cffi_modules=['_cffi_build/build.py:ffi'],
cmdclass={

View file

@ -0,0 +1,13 @@
diff --git a/setup_support.py b/setup_support.py
index e7a4f2e..72f0c4d 100644
--- a/setup_support.py
+++ b/setup_support.py
@@ -68,6 +69,8 @@ def build_flags(library, type_, path):
def _find_lib():
+ # we're picking up the recipe one
+ return True
from cffi import FFI
ffi = FFI()
try:

View file

@ -0,0 +1,13 @@
diff --git a/setup.py b/setup.py
index c224fb2..d5f6d1a 100644
--- a/setup.py
+++ b/setup.py
@@ -51,6 +51,8 @@ if [int(i) for i in setuptools_version.split('.', 2)[:2]] < [3, 3]:
def download_library(command):
+ # we will use the custom libsecp256k1 recipe
+ return
if command.dry_run:
return
libdir = absolute('libsecp256k1')

Some files were not shown because too many files have changed in this diff Show more