Skip to content

Commit 5ab51d5

Browse files
committed
Merge branch 'master' of github.com:kivy/python-for-android
2 parents 1bbb83c + 3c2825b commit 5ab51d5

File tree

13 files changed

+198
-89
lines changed

13 files changed

+198
-89
lines changed

doc/source/buildoptions.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ Google's official NDK which includes many improvements. You
4242
python3. You can get it `here
4343
<https://www.crystax.net/en/download>`__.
4444

45-
The python3crystax build is is handled quite differently to python2 so
45+
The python3crystax build is handled quite differently to python2 so
4646
there may be bugs or surprising behaviours. If you come across any,
4747
feel free to `open an issue
4848
<https://github.com/kivy/python-for-android>`__.
@@ -56,7 +56,7 @@ python-for-android supports multiple app backends with different types
5656
of interface. These are called *bootstraps*.
5757

5858
Currently the following bootstraps are supported, but we hope that it
59-
it should be easy to add others if your project has different
59+
should be easy to add others if your project has different
6060
requirements. `Let us know
6161
<https://groups.google.com/forum/#!forum/python-android>`__ if you'd
6262
like help adding a new one.

doc/source/quickstart.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ install most of these with::
6767

6868
sudo dpkg --add-architecture i386
6969
sudo apt-get update
70-
sudo apt-get install -y build-essential ccache git zlib1g-dev python2.7 python2.7-dev libncurses5:i386 libstdc++6:i386 zlib1g:i386 openjdk-7-jdk unzip ant ccache
70+
sudo apt-get install -y build-essential git zlib1g-dev python2.7 python2.7-dev libncurses5:i386 libstdc++6:i386 zlib1g:i386 openjdk-7-jdk unzip ant ccache
7171

7272
On Arch Linux (64 bit) you should be able to run the following to
7373
install most of the dependencies (note: this list may not be

doc/source/recipes.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ this using the ``sh`` module as follows::
181181
def build_arch(self, arch):
182182
super(YourRecipe, self).build_arch(arch)
183183
env = self.get_recipe_env(arch)
184-
sh.echo('$PATH', _env=env) # Will print the PATH entry fron the
184+
sh.echo('$PATH', _env=env) # Will print the PATH entry from the
185185
# env dict
186186

187187
You can also use the ``shprint`` helper function from the p4a

doc/source/troubleshooting.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,11 @@ Exception in thread "main" java.lang.UnsupportedClassVersionError: com/android/d
152152

153153
This occurs due to a java version mismatch, it should be fixed by
154154
installing Java 8 (e.g. the openjdk-8-jdk package on Ubuntu).
155+
156+
JNI DETECTED ERROR IN APPLICATION: static jfieldID 0x0000000 not valid for class java.lang.Class<org.renpy.android.PythonActivity>
157+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
158+
159+
This error appears in the logcat log if you try to access
160+
``org.renpy.android.PythonActivity`` from within the new toolchain. To
161+
fix it, change your code to reference
162+
``org.kivy.android.PythonActivity`` instead.

pythonforandroid/bootstraps/sdl2/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ def run_distribute(self):
111111
shprint(sh.mv, filen, filen.split('.')[0] + '.so')
112112
site_packages_dir = join(abspath(curdir),
113113
site_packages_dir)
114+
if 'sqlite3' not in self.ctx.recipe_build_order:
115+
with open('blacklist.txt', 'a') as fileh:
116+
fileh.write('\nsqlite3/*\nlib-dynload/_sqlite3.so\n')
114117

115118

116119
self.strip_libraries(arch)

pythonforandroid/bootstraps/sdl2/build/blacklist.txt

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,3 @@ lib-dynload/_testcapi.so
8181

8282
# odd files
8383
plat-linux3/regen
84-
85-
#>sqlite3
86-
# conditionnal include depending if some recipes are included or not.
87-
sqlite3/*
88-
lib-dynload/_sqlite3.so
89-
#<sqlite3
90-

pythonforandroid/bootstraps/sdl2/build/src/org/kivy/android/PythonActivity.java

Lines changed: 96 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import android.content.Intent;
1919
import android.util.Log;
2020
import android.widget.Toast;
21+
import android.os.AsyncTask;
2122
import android.os.Bundle;
2223
import android.os.PowerManager;
2324
import android.graphics.PixelFormat;
@@ -61,83 +62,15 @@ public String getAppRoot() {
6162
protected void onCreate(Bundle savedInstanceState) {
6263
Log.v(TAG, "My oncreate running");
6364
resourceManager = new ResourceManager(this);
64-
this.showLoadingScreen();
65-
File app_root_file = new File(getAppRoot());
66-
67-
Log.v(TAG, "Ready to unpack");
68-
unpackData("private", app_root_file);
6965

7066
Log.v(TAG, "About to do super onCreate");
7167
super.onCreate(savedInstanceState);
7268
Log.v(TAG, "Did super onCreate");
7369

7470
this.mActivity = this;
7571
this.showLoadingScreen();
76-
77-
// Figure out the directory where the game is. If the game was
78-
// given to us via an intent, then we use the scheme-specific
79-
// part of that intent to determine the file to launch. We
80-
// also use the android.txt file to determine the orientation.
81-
//
82-
// Otherwise, we use the public data, if we have it, or the
83-
// private data if we do not.
84-
String app_root_dir = getAppRoot();
85-
if (getIntent() != null && getIntent().getAction() != null &&
86-
getIntent().getAction().equals("org.kivy.LAUNCH")) {
87-
File path = new File(getIntent().getData().getSchemeSpecificPart());
88-
89-
Project p = Project.scanDirectory(path);
90-
SDLActivity.nativeSetEnv("ANDROID_ENTRYPOINT", p.dir + "/main.py");
91-
SDLActivity.nativeSetEnv("ANDROID_ARGUMENT", p.dir);
92-
SDLActivity.nativeSetEnv("ANDROID_APP_PATH", p.dir);
93-
94-
if (p != null) {
95-
if (p.landscape) {
96-
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
97-
} else {
98-
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
99-
}
100-
}
101-
102-
// Let old apps know they started.
103-
try {
104-
FileWriter f = new FileWriter(new File(path, ".launch"));
105-
f.write("started");
106-
f.close();
107-
} catch (IOException e) {
108-
// pass
109-
}
110-
} else {
111-
SDLActivity.nativeSetEnv("ANDROID_ENTRYPOINT", "main.pyo");
112-
SDLActivity.nativeSetEnv("ANDROID_ARGUMENT", app_root_dir);
113-
SDLActivity.nativeSetEnv("ANDROID_APP_PATH", app_root_dir);
114-
}
115-
116-
String mFilesDirectory = mActivity.getFilesDir().getAbsolutePath();
117-
Log.v(TAG, "Setting env vars for start.c and Python to use");
118-
SDLActivity.nativeSetEnv("ANDROID_PRIVATE", mFilesDirectory);
119-
SDLActivity.nativeSetEnv("PYTHONHOME", app_root_dir);
120-
SDLActivity.nativeSetEnv("PYTHONPATH", app_root_dir + ":" + app_root_dir + "/lib");
121-
SDLActivity.nativeSetEnv("PYTHONOPTIMIZE", "2");
12272

123-
try {
124-
Log.v(TAG, "Access to our meta-data...");
125-
this.mMetaData = this.mActivity.getPackageManager().getApplicationInfo(
126-
this.mActivity.getPackageName(), PackageManager.GET_META_DATA).metaData;
127-
128-
PowerManager pm = (PowerManager) this.mActivity.getSystemService(Context.POWER_SERVICE);
129-
if ( this.mMetaData.getInt("wakelock") == 1 ) {
130-
this.mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "Screen On");
131-
}
132-
if ( this.mMetaData.getInt("surface.transparent") != 0 ) {
133-
Log.v(TAG, "Surface will be transparent.");
134-
getSurface().setZOrderOnTop(true);
135-
getSurface().getHolder().setFormat(PixelFormat.TRANSPARENT);
136-
} else {
137-
Log.i(TAG, "Surface will NOT be transparent");
138-
}
139-
} catch (PackageManager.NameNotFoundException e) {
140-
}
73+
new UnpackFilesTask().execute(getAppRoot());
14174
}
14275

14376
public void loadLibraries() {
@@ -178,6 +111,100 @@ public void run() {
178111
}
179112
}
180113

114+
private class UnpackFilesTask extends AsyncTask<String, Void, String> {
115+
@Override
116+
protected String doInBackground(String... params) {
117+
File app_root_file = new File(params[0]);
118+
Log.v(TAG, "Ready to unpack");
119+
unpackData("private", app_root_file);
120+
return null;
121+
}
122+
123+
@Override
124+
protected void onPostExecute(String result) {
125+
// Figure out the directory where the game is. If the game was
126+
// given to us via an intent, then we use the scheme-specific
127+
// part of that intent to determine the file to launch. We
128+
// also use the android.txt file to determine the orientation.
129+
//
130+
// Otherwise, we use the public data, if we have it, or the
131+
// private data if we do not.
132+
mActivity.finishLoad();
133+
134+
// finishLoad called setContentView with the SDL view, which
135+
// removed the loading screen. However, we still need it to
136+
// show until the app is ready to render, so pop it back up
137+
// on top of the SDL view.
138+
mActivity.showLoadingScreen();
139+
140+
String app_root_dir = getAppRoot();
141+
if (getIntent() != null && getIntent().getAction() != null &&
142+
getIntent().getAction().equals("org.kivy.LAUNCH")) {
143+
File path = new File(getIntent().getData().getSchemeSpecificPart());
144+
145+
Project p = Project.scanDirectory(path);
146+
SDLActivity.nativeSetEnv("ANDROID_ENTRYPOINT", p.dir + "/main.py");
147+
SDLActivity.nativeSetEnv("ANDROID_ARGUMENT", p.dir);
148+
SDLActivity.nativeSetEnv("ANDROID_APP_PATH", p.dir);
149+
150+
if (p != null) {
151+
if (p.landscape) {
152+
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
153+
} else {
154+
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
155+
}
156+
}
157+
158+
// Let old apps know they started.
159+
try {
160+
FileWriter f = new FileWriter(new File(path, ".launch"));
161+
f.write("started");
162+
f.close();
163+
} catch (IOException e) {
164+
// pass
165+
}
166+
} else {
167+
SDLActivity.nativeSetEnv("ANDROID_ENTRYPOINT", "main.pyo");
168+
SDLActivity.nativeSetEnv("ANDROID_ARGUMENT", app_root_dir);
169+
SDLActivity.nativeSetEnv("ANDROID_APP_PATH", app_root_dir);
170+
}
171+
172+
String mFilesDirectory = mActivity.getFilesDir().getAbsolutePath();
173+
Log.v(TAG, "Setting env vars for start.c and Python to use");
174+
SDLActivity.nativeSetEnv("ANDROID_PRIVATE", mFilesDirectory);
175+
SDLActivity.nativeSetEnv("PYTHONHOME", app_root_dir);
176+
SDLActivity.nativeSetEnv("PYTHONPATH", app_root_dir + ":" + app_root_dir + "/lib");
177+
SDLActivity.nativeSetEnv("PYTHONOPTIMIZE", "2");
178+
179+
try {
180+
Log.v(TAG, "Access to our meta-data...");
181+
mActivity.mMetaData = mActivity.getPackageManager().getApplicationInfo(
182+
mActivity.getPackageName(), PackageManager.GET_META_DATA).metaData;
183+
184+
PowerManager pm = (PowerManager) mActivity.getSystemService(Context.POWER_SERVICE);
185+
if ( mActivity.mMetaData.getInt("wakelock") == 1 ) {
186+
mActivity.mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "Screen On");
187+
}
188+
if ( mActivity.mMetaData.getInt("surface.transparent") != 0 ) {
189+
Log.v(TAG, "Surface will be transparent.");
190+
getSurface().setZOrderOnTop(true);
191+
getSurface().getHolder().setFormat(PixelFormat.TRANSPARENT);
192+
} else {
193+
Log.i(TAG, "Surface will NOT be transparent");
194+
}
195+
} catch (PackageManager.NameNotFoundException e) {
196+
}
197+
}
198+
199+
@Override
200+
protected void onPreExecute() {
201+
}
202+
203+
@Override
204+
protected void onProgressUpdate(Void... values) {
205+
}
206+
}
207+
181208
public void unpackData(final String resource, File target) {
182209

183210
Log.v(TAG, "UNPACKING!!! " + resource + " " + target.getName());

pythonforandroid/bootstraps/sdl2/build/src/org/libsdl/app/SDLActivity.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,11 @@ protected void onCreate(Bundle savedInstanceState) {
122122
SDLActivity.initialize();
123123
// So we can call stuff from static callbacks
124124
mSingleton = this;
125+
}
125126

127+
// We don't do this in onCreate because we unpack and load the app data on a thread
128+
// and we can't run setup tasks until that thread completes.
129+
protected void finishLoad() {
126130
// Load shared libraries
127131
String errorMsgBrokenLib = "";
128132
try {

pythonforandroid/recipe.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,13 @@ def get_recipe_env(self, arch, with_flags_in_cc=True):
10891089
self.ctx.python_recipe.version, 'include',
10901090
'python')) + env['CFLAGS']
10911091

1092+
# Temporarily hardcode the -lpython3.5 as this does not
1093+
# get applied automatically in some environments. This
1094+
# will need generalising, along with the other hardcoded
1095+
# py3.5 references, to support other python3 or crystax
1096+
# python versions.
1097+
env['LDFLAGS'] = env['LDFLAGS'] + ' -lpython3.5m'
1098+
10921099
return env
10931100

10941101

pythonforandroid/toolchain.py

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -352,26 +352,40 @@ def add_parser(subparsers, *args, **kwargs):
352352
"--compact", action="store_true", default=False,
353353
help="Produce a compact list suitable for scripting")
354354

355-
parser_bootstraps = add_parser(subparsers,
356-
'bootstraps', help='List the available bootstraps',
355+
parser_bootstraps = add_parser(
356+
subparsers, 'bootstraps',
357+
help='List the available bootstraps',
357358
parents=[generic_parser])
358-
parser_clean_all = add_parser(subparsers,
359-
'clean_all', aliases=['clean-all'],
359+
parser_clean_all = add_parser(
360+
subparsers, 'clean_all',
361+
aliases=['clean-all'],
360362
help='Delete all builds, dists and caches',
361363
parents=[generic_parser])
362-
parser_clean_dists = add_parser(subparsers,
364+
parser_clean_dists = add_parser(
365+
subparsers,
363366
'clean_dists', aliases=['clean-dists'],
364367
help='Delete all dists',
365368
parents=[generic_parser])
366-
parser_clean_bootstrap_builds = add_parser(subparsers,
369+
parser_clean_bootstrap_builds = add_parser(
370+
subparsers,
367371
'clean_bootstrap_builds', aliases=['clean-bootstrap-builds'],
368372
help='Delete all bootstrap builds',
369373
parents=[generic_parser])
370-
parser_clean_builds = add_parser(subparsers,
374+
parser_clean_builds = add_parser(
375+
subparsers,
371376
'clean_builds', aliases=['clean-builds'],
372377
help='Delete all builds',
373378
parents=[generic_parser])
374379

380+
parser_clean = add_parser(subparsers, 'clean',
381+
help='Delete build components.',
382+
parents=[generic_parser])
383+
parser_clean.add_argument(
384+
'component', nargs='+',
385+
help=('The build component(s) to delete. You can pass any '
386+
'number of arguments from "all", "builds", "dists", '
387+
'"distributions", "bootstrap_builds", "downloads".'))
388+
375389
parser_clean_recipe_build = add_parser(subparsers,
376390
'clean_recipe_build', aliases=['clean-recipe-build'],
377391
help=('Delete the build components of the given recipe. '
@@ -561,6 +575,24 @@ def bootstraps(self, args):
561575
print(' {Fore.GREEN}depends: {bs.recipe_depends}{Fore.RESET}'
562576
.format(bs=bs, Fore=Out_Fore))
563577

578+
def clean(self, args):
579+
components = args.component
580+
581+
component_clean_methods = {'all': self.clean_all,
582+
'dists': self.clean_dists,
583+
'distributions': self.clean_dists,
584+
'builds': self.clean_builds,
585+
'bootstrap_builds': self.clean_bootstrap_builds,
586+
'downloads': self.clean_download_cache}
587+
588+
for component in components:
589+
if component not in component_clean_methods:
590+
raise ValueError((
591+
'Asked to clean "{}" but this argument is not '
592+
'recognised'.format(component)))
593+
component_clean_methods[component](args)
594+
595+
564596
def clean_all(self, args):
565597
'''Delete all build components; the package cache, package builds,
566598
bootstrap builds and distributions.'''

testapps/setup_testapp_flask.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
2+
from distutils.core import setup
3+
from setuptools import find_packages
4+
5+
options = {'apk': {'debug': None,
6+
'requirements': 'python2,flask,pyjnius',
7+
'android-api': 19,
8+
'ndk-dir': '/home/asandy/android/crystax-ndk-10.3.2',
9+
'dist-name': 'testapp_flask',
10+
'ndk-version': '10.3.2',
11+
'bootstrap': 'webview',
12+
'permissions': ['INTERNET', 'VIBRATE'],
13+
'window': None,
14+
}}
15+
16+
package_data = {'': ['*.py',
17+
'*.png']
18+
}
19+
20+
packages = find_packages()
21+
print('packages are', packages)
22+
23+
setup(
24+
name='testapp_flask',
25+
version='1.0',
26+
description='p4a flask testapp',
27+
author='Alexander Taylor',
28+
author_email='alexanderjohntaylor@gmail.com',
29+
packages=find_packages(),
30+
options=options,
31+
package_data={'testapp_flask': ['*.py', '*.png'],
32+
'testapp_flask/static': ['*.png', '*.css'],
33+
'testapp_flask/templates': ['*.html']}
34+
)

0 commit comments

Comments
 (0)