diff --git a/buildozer.spec.travis b/buildozer.spec.travis index 6efdb38..5a6cb53 100644 --- a/buildozer.spec.travis +++ b/buildozer.spec.travis @@ -36,7 +36,7 @@ version = 0.1 # (list) Application requirements # comma seperated e.g. requirements = sqlite3,kivy -requirements = openssl, sqlite3, hostpython2, pycrypto==2.6.1, kivy, android, pyjnius, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pycrypto==2.6.1, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, txrequests==0.9.5, seccure==0.3.1.3, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, git+https://github.com/lbryio/lbryschema.git#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/akinwale/lbry.git#egg=lbry +requirements = openssl, sqlite3, hostpython2, pycrypto==2.6.1, android, pyjnius, constantly, incremental, functools32, miniupnpc==1.9, gmpy==1.17, twisted==16.6.0, appdirs==1.4.3, argparse==1.2.1, docopt==0.6.2, base58==0.2.2, colorama==0.3.7, dnspython==1.12.0, ecdsa==0.13, envparse==0.2.0, jsonrpc==1.2, jsonrpclib==0.1.7, jsonschema==2.5.1, pbkdf2==1.3, pycrypto==2.6.1, pyyaml==3.12, qrcode==5.2.2, requests==2.9.1, txrequests==0.9.5, seccure==0.3.1.3, service_identity==16.0.0, six==1.9.0, slowaes==0.1a1, txJSON-RPC==0.5, wsgiref==0.1.2, zope.interface==4.3.3, protobuf==3.2.0, git+https://github.com/lbryio/lbryschema.git#egg=lbryschema, git+https://github.com/lbryio/lbryum.git#egg=lbryum, git+https://github.com/akinwale/lbry.git#egg=lbry # (str) Custom source folders for requirements # Sets custom source for any requirements with recipes diff --git a/p4a/pythonforandroid/bootstraps/lbry/__init__.py b/p4a/pythonforandroid/bootstraps/lbry/__init__.py index 7cf3dd5..c16640f 100644 --- a/p4a/pythonforandroid/bootstraps/lbry/__init__.py +++ b/p4a/pythonforandroid/bootstraps/lbry/__init__.py @@ -7,13 +7,13 @@ import sh class LbryBootstrap(Bootstrap): name = 'lbry' - recipe_depends = ['sdl2', ('python2', 'python3crystax')] + recipe_depends = ['genericndkbuild', ('python2', 'python3crystax')] def run_distribute(self): info_main('# Creating Android project from build and {} bootstrap'.format( self.name)) - info('This currently just copies the SDL2 build stuff straight from the build dir.') + info('This currently just copies the build stuff straight from the build dir.') shprint(sh.rm, '-rf', self.dist_dir) shprint(sh.cp, '-r', self.build_dir, self.dist_dir) with current_directory(self.dist_dir): diff --git a/p4a/pythonforandroid/bootstraps/lbry/build/jni/src/Android.mk b/p4a/pythonforandroid/bootstraps/lbry/build/jni/src/Android.mk index 41d689d..018a7ca 100644 --- a/p4a/pythonforandroid/bootstraps/lbry/build/jni/src/Android.mk +++ b/p4a/pythonforandroid/bootstraps/lbry/build/jni/src/Android.mk @@ -4,19 +4,14 @@ include $(CLEAR_VARS) LOCAL_MODULE := main -SDL_PATH := ../SDL - -LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include - # Add your application source files here... -LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.c \ - start.c +LOCAL_SRC_FILES := start.c pyjniusjni.c LOCAL_CFLAGS += -I$(LOCAL_PATH)/../../../../other_builds/$(PYTHON2_NAME)/$(ARCH)/python2/python-install/include/python2.7 $(EXTRA_CFLAGS) -LOCAL_SHARED_LIBRARIES := SDL2 python_shared +LOCAL_SHARED_LIBRARIES := python_shared -LOCAL_LDLIBS := -lGLESv1_CM -lGLESv2 -llog $(EXTRA_LDLIBS) +LOCAL_LDLIBS := -llog $(EXTRA_LDLIBS) LOCAL_LDFLAGS += -L$(LOCAL_PATH)/../../../../other_builds/$(PYTHON2_NAME)/$(ARCH)/python2/python-install/lib $(APPLICATION_ADDITIONAL_LDFLAGS) diff --git a/p4a/pythonforandroid/bootstraps/lbry/build/jni/src/Android_static.mk b/p4a/pythonforandroid/bootstraps/lbry/build/jni/src/Android_static.mk index faed669..2de278e 100644 --- a/p4a/pythonforandroid/bootstraps/lbry/build/jni/src/Android_static.mk +++ b/p4a/pythonforandroid/bootstraps/lbry/build/jni/src/Android_static.mk @@ -6,7 +6,5 @@ LOCAL_MODULE := main LOCAL_SRC_FILES := YourSourceHere.c -LOCAL_STATIC_LIBRARIES := SDL2_static - include $(BUILD_SHARED_LIBRARY) $(call import-module,SDL)LOCAL_PATH := $(call my-dir) diff --git a/p4a/pythonforandroid/bootstraps/lbry/build/jni/src/pyjniusjni.c b/p4a/pythonforandroid/bootstraps/lbry/build/jni/src/pyjniusjni.c new file mode 100644 index 0000000..d67972a --- /dev/null +++ b/p4a/pythonforandroid/bootstraps/lbry/build/jni/src/pyjniusjni.c @@ -0,0 +1,103 @@ + +#include +#include + +#define LOGI(...) do {} while (0) +#define LOGE(...) do {} while (0) + +#include "android/log.h" + +/* These JNI management functions are taken from SDL2, but modified to refer to pyjnius */ + +/* #define LOG(n, x) __android_log_write(ANDROID_LOG_INFO, (n), (x)) */ +/* #define LOGP(x) LOG("python", (x)) */ +#define LOG_TAG "Python_android" +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) + + +/* Function headers */ +JNIEnv* Android_JNI_GetEnv(void); +static void Android_JNI_ThreadDestroyed(void*); + +static pthread_key_t mThreadKey; +static JavaVM* mJavaVM; + +int Android_JNI_SetupThread(void) +{ + Android_JNI_GetEnv(); + return 1; +} + +/* Library init */ +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) +{ + JNIEnv *env; + mJavaVM = vm; + LOGI("JNI_OnLoad called"); + if ((*mJavaVM)->GetEnv(mJavaVM, (void**) &env, JNI_VERSION_1_4) != JNI_OK) { + LOGE("Failed to get the environment using GetEnv()"); + return -1; + } + /* + * Create mThreadKey so we can keep track of the JNIEnv assigned to each thread + * Refer to http://developer.android.com/guide/practices/design/jni.html for the rationale behind this + */ + if (pthread_key_create(&mThreadKey, Android_JNI_ThreadDestroyed) != 0) { + + __android_log_print(ANDROID_LOG_ERROR, "pyjniusjni", "Error initializing pthread key"); + } + Android_JNI_SetupThread(); + + return JNI_VERSION_1_4; +} + +JNIEnv* Android_JNI_GetEnv(void) +{ + /* From http://developer.android.com/guide/practices/jni.html + * All threads are Linux threads, scheduled by the kernel. + * They're usually started from managed code (using Thread.start), but they can also be created elsewhere and then + * attached to the JavaVM. For example, a thread started with pthread_create can be attached with the + * JNI AttachCurrentThread or AttachCurrentThreadAsDaemon functions. Until a thread is attached, it has no JNIEnv, + * and cannot make JNI calls. + * Attaching a natively-created thread causes a java.lang.Thread object to be constructed and added to the "main" + * ThreadGroup, making it visible to the debugger. Calling AttachCurrentThread on an already-attached thread + * is a no-op. + * Note: You can call this function any number of times for the same thread, there's no harm in it + */ + + JNIEnv *env; + int status = (*mJavaVM)->AttachCurrentThread(mJavaVM, &env, NULL); + if(status < 0) { + LOGE("failed to attach current thread"); + return 0; + } + + /* From http://developer.android.com/guide/practices/jni.html + * Threads attached through JNI must call DetachCurrentThread before they exit. If coding this directly is awkward, + * in Android 2.0 (Eclair) and higher you can use pthread_key_create to define a destructor function that will be + * called before the thread exits, and call DetachCurrentThread from there. (Use that key with pthread_setspecific + * to store the JNIEnv in thread-local-storage; that way it'll be passed into your destructor as the argument.) + * Note: The destructor is not called unless the stored value is != NULL + * Note: You can call this function any number of times for the same thread, there's no harm in it + * (except for some lost CPU cycles) + */ + pthread_setspecific(mThreadKey, (void*) env); + + return env; +} + +static void Android_JNI_ThreadDestroyed(void* value) +{ + /* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */ + JNIEnv *env = (JNIEnv*) value; + if (env != NULL) { + (*mJavaVM)->DetachCurrentThread(mJavaVM); + pthread_setspecific(mThreadKey, NULL); + } +} + +void *WebView_AndroidGetJNIEnv() +{ + return Android_JNI_GetEnv(); +} diff --git a/p4a/pythonforandroid/bootstraps/lbry/build/jni/src/start.c b/p4a/pythonforandroid/bootstraps/lbry/build/jni/src/start.c index 7582348..3fd5d86 100644 --- a/p4a/pythonforandroid/bootstraps/lbry/build/jni/src/start.c +++ b/p4a/pythonforandroid/bootstraps/lbry/build/jni/src/start.c @@ -14,9 +14,7 @@ #include #include -#include "SDL.h" #include "android/log.h" -#include "SDL_opengles2.h" #define ENTRYPOINT_MAXLEN 128 #define LOG(n, x) __android_log_write(ANDROID_LOG_INFO, (n), (x)) @@ -84,7 +82,7 @@ int main(int argc, char *argv[]) { setenv("ANDROID_APP_PATH", env_argument, 1); env_entrypoint = getenv("ANDROID_ENTRYPOINT"); env_logname = getenv("PYTHON_NAME"); - + if (env_logname == NULL) { env_logname = "python"; setenv("PYTHON_NAME", "python", 1); diff --git a/p4a/pythonforandroid/bootstraps/lbry/build/templates/app.build.tmpl.gradle b/p4a/pythonforandroid/bootstraps/lbry/build/templates/app.build.tmpl.gradle new file mode 100644 index 0000000..514ba0f --- /dev/null +++ b/p4a/pythonforandroid/bootstraps/lbry/build/templates/app.build.tmpl.gradle @@ -0,0 +1,59 @@ +apply plugin: 'com.android.model.application' + +model { + android { + compileSdkVersion {{ args.sdk_version }} + buildToolsVersion "23.0.3" + + defaultConfig { + applicationId "{{ args.package }}" + minSdkVersion.apiLevel {{ args.min_sdk_version }} + targetSdkVersion.apiLevel {{ args.sdk_version }} + versionCode {{ args.numeric_version }} + versionName "{{ args.version }}" + + buildConfigFields { + create() { + type "int" + name "VALUE" + value "1" + } + } + } + ndk { + abiFilters.add("armeabi-v7a") + moduleName = "main" + toolchain = "gcc" + toolchainVersion = "4.9" + platformVersion = 16 + stl = "gnustl_shared" + renderscriptNdkMode = false + CFlags.add("-I" + file("src/main/jni/include/python2.7")) + ldFlags.add("-L" + file("src/main/jni/lib")) + ldLibs.addAll(["log", "python2.7"]) + } + // Configures source set directory. + sources { + main { + jniLibs { + dependencies { + library "gnustl_shared" + // add pre-built libraries here and locate them below: + } + } + } + } + } + repositories { + libs(PrebuiltLibraries) { + gnustl_shared { + binaries.withType(SharedLibraryBinary) { + sharedLibraryFile = file("src/main/jniLibs/${targetPlatform.getName()}/libgnustl_shared.so") + } + } + // more here + } + } +} + +// normal project dependencies here \ No newline at end of file diff --git a/recipes/pyjnius/__init__.py b/recipes/pyjnius/__init__.py index fa4c33a..d9eaf42 100644 --- a/recipes/pyjnius/__init__.py +++ b/recipes/pyjnius/__init__.py @@ -9,7 +9,7 @@ class PyjniusRecipe(CythonRecipe): version = 'master' url = 'https://github.com/kivy/pyjnius/archive/{version}.zip' name = 'pyjnius' - depends = [('python2', 'python3crystax'), ('sdl2', 'sdl', 'genericndkbuild'), 'six'] + depends = [('python2', 'python3crystax'), 'genericndkbuild', 'six'] site_packages_name = 'jnius' call_hostpython_via_targetpython = False diff --git a/src/main/java/io/lbry/lbrynet/LbrynetService.java b/src/main/java/io/lbry/lbrynet/LbrynetService.java index 9ef7fd7..60f7022 100644 --- a/src/main/java/io/lbry/lbrynet/LbrynetService.java +++ b/src/main/java/io/lbry/lbrynet/LbrynetService.java @@ -29,16 +29,6 @@ public class LbrynetService extends PythonService { public static String TAG = "LbrynetService"; - @Override - public int startType() { - return START_STICKY; - } - - @Override - public boolean canDisplayNotification() { - return false; - } - @Override public int onStartCommand(Intent intent, int flags, int startId) { // Extract files @@ -52,12 +42,22 @@ public class LbrynetService extends PythonService { return super.onStartCommand(intent, flags, startId); } - public String getAppRoot() { + @Override + public boolean canDisplayNotification() { + return false; + } + + @Override + public int startType() { + return START_STICKY; + } + + private String getAppRoot() { String app_root = getApplicationContext().getFilesDir().getAbsolutePath() + "/app"; return app_root; } - public void recursiveDelete(File f) { + private void recursiveDelete(File f) { if (f.isDirectory()) { for (File r : f.listFiles()) { recursiveDelete(r); @@ -66,7 +66,7 @@ public class LbrynetService extends PythonService { f.delete(); } - public void unpackData(final String resource, File target) { + private void unpackData(final String resource, File target) { Log.v(TAG, "UNPACKING!!! " + resource + " " + target.getName()); // The version of data in memory and on disk. diff --git a/src/main/python/lbrynetservice.py b/src/main/python/lbrynetservice.py index ce96ba9..4479ecd 100644 --- a/src/main/python/lbrynetservice.py +++ b/src/main/python/lbrynetservice.py @@ -1,50 +1,6 @@ import platform -# logging override (until we can figure out why logging doesn't work normally) -import logging -from kivy.logger import Logger -class InternalLogger(logging.Logger): - def __init__(self, name, level=logging.DEBUG): - self.name = name - return super(InternalLogger, self).__init__(name, level) - - def debug(self, msg, *args, **kwargs): - msg = '%s: %s' % (self.name, msg) - Logger.debug(msg, *args, **kwargs) - - def info(self, msg, *args, **kwargs): - msg = '%s: %s' % (self.name, msg) - Logger.info(msg, *args, **kwargs) - - def warning(self, msg, *args, **kwargs): - msg = '%s: %s' % (self.name, msg) - Logger.warning(msg, *args, **kwargs) - - def error(self, msg, *args, **kwargs): - msg = '%s: %s' % (self.name, msg) - Logger.error(msg, *args, **kwargs) - - def critical(self, msg, *args, **kwargs): - msg = '%s: %s' % (self.name, msg) - Logger.critical(msg, *args, **kwargs) - - def log(self, lvl, msg, *args, **kwargs): - msg = '%s: %s' % (self.name, msg) - Logger.log(lvl, msg, *args, **kwargs) - - def exception(self, msg, *args, **kwargs): - msg = '%s: %s' % (self.name, msg) - Logger.exception(msg, *args, **kwargs) - - # required by twisted for some reason. - def fail(self, *args, **kwargs): - pass - -def getLoggerOverride(name='root', loglevel=logging.DEBUG): - return InternalLogger(name, loglevel) - -logging.getLogger = getLoggerOverride - +import logging.handlers from lbrynet.core import log_support from twisted.internet import defer, reactor @@ -56,15 +12,13 @@ from lbrynet.core import utils, system_info from lbrynet.daemon.auth.client import LBRYAPIClient from lbrynet.daemon.DaemonServer import DaemonServer -import kivy import ssl # Fixes / patches / overrides # platform.platform() in libc_ver: IOError: [Errno 21] Is a directory -if (kivy.platform == 'android'): - from jnius import autoclass - util = autoclass('io.lbry.lbrynet.Utils') - platform.platform = lambda: 'Android %s (API %s)' % (util.getAndroidRelease(), util.getAndroidSdk()) +from jnius import autoclass +util = autoclass('io.lbry.lbrynet.Utils') +platform.platform = lambda: 'Android %s (API %s)' % (util.getAndroidRelease(), util.getAndroidSdk()) # https certificate verification # TODO: this is bad. Need to find a way to properly verify https requests diff --git a/src/main/python/main.py b/src/main/python/main.py index 3ddf003..9523e55 100644 --- a/src/main/python/main.py +++ b/src/main/python/main.py @@ -1,28 +1,16 @@ -from kivy.app import App -from kivy.lang import Builder -from kivy.utils import platform - -kv = ''' -Button: - text: 'push me!' -''' - class ServiceApp(App): def build(self): - if platform == 'android': - from jnius import autoclass + from jnius import autoclass - Intent = autoclass('android.content.Intent') - LbrynetService = autoclass('io.lbry.lbrynet.LbrynetService') - context = autoclass('org.kivy.android.PythonActivity').mActivity + Intent = autoclass('android.content.Intent') + LbrynetService = autoclass('io.lbry.lbrynet.LbrynetService') + #context = autoclass('org.kivy.android.PythonActivity').mActivity - #LbrynetService.start(context, '') + #LbrynetService.start(context, '') - # close the activity once the service starts - # ideally, we should have some form of service control for the activity - #context.finish() - - return Builder.load_string(kv) + # close the activity once the service starts + # ideally, we should have some form of service control for the activity + #context.finish() if __name__ == '__main__': ServiceApp().run()