From 7714682320ea0959a8f4f02742abb8f835fc38ad Mon Sep 17 00:00:00 2001 From: Mirko Galimberti Date: Mon, 5 Sep 2022 19:02:40 +0200 Subject: [PATCH 01/26] Bump minimal and recommended Android NDK version to 25b --- .github/workflows/push.yml | 18 ------------------ ci/makefiles/android.mk | 2 +- doc/source/quickstart.rst | 2 +- pythonforandroid/recommendations.py | 6 +++--- 4 files changed, 5 insertions(+), 23 deletions(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 741210775b..1faaa18d70 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -109,17 +109,11 @@ jobs: target: testapps-with-numpy - name: webview target: testapps-webview - include: - - runs_on: macos-latest - ndk_version: '23b' - - runs_on: apple-silicon-m1 - ndk_version: '24' env: ANDROID_HOME: ${HOME}/.android ANDROID_SDK_ROOT: ${HOME}/.android/android-sdk ANDROID_SDK_HOME: ${HOME}/.android/android-sdk ANDROID_NDK_HOME: ${HOME}/.android/android-ndk - ANDROID_NDK_VERSION: ${{ matrix.ndk_version }} steps: - name: Checkout python-for-android uses: actions/checkout@v2 @@ -247,17 +241,11 @@ jobs: target: testapps-with-numpy-aab - name: webview target: testapps-webview-aab - include: - - runs_on: macos-latest - ndk_version: '23b' - - runs_on: apple-silicon-m1 - ndk_version: '24' env: ANDROID_HOME: ${HOME}/.android ANDROID_SDK_ROOT: ${HOME}/.android/android-sdk ANDROID_SDK_HOME: ${HOME}/.android/android-sdk ANDROID_NDK_HOME: ${HOME}/.android/android-ndk - ANDROID_NDK_VERSION: ${{ matrix.ndk_version }} steps: - name: Checkout python-for-android uses: actions/checkout@v2 @@ -330,18 +318,12 @@ jobs: matrix: android_arch: ["arm64-v8a", "armeabi-v7a", "x86_64", "x86"] runs_on: [macos-latest, apple-silicon-m1] - include: - - runs_on: macos-latest - ndk_version: '23b' - - runs_on: apple-silicon-m1 - ndk_version: '24' env: ANDROID_HOME: ${HOME}/.android ANDROID_SDK_ROOT: ${HOME}/.android/android-sdk ANDROID_SDK_HOME: ${HOME}/.android/android-sdk ANDROID_NDK_HOME: ${HOME}/.android/android-ndk REBUILD_UPDATED_RECIPES_EXTRA_ARGS: --arch=${{ matrix.android_arch }} - ANDROID_NDK_VERSION: ${{ matrix.ndk_version }} steps: - name: Checkout python-for-android uses: actions/checkout@v2 diff --git a/ci/makefiles/android.mk b/ci/makefiles/android.mk index 6550daa0ba..2041a6ce76 100644 --- a/ci/makefiles/android.mk +++ b/ci/makefiles/android.mk @@ -1,7 +1,7 @@ # Downloads and installs the Android SDK depending on supplied platform: darwin or linux # Those android NDK/SDK variables can be override when running the file -ANDROID_NDK_VERSION ?= 23b +ANDROID_NDK_VERSION ?= 25b ANDROID_NDK_VERSION_LEGACY ?= 21e ANDROID_SDK_TOOLS_VERSION ?= 6514223 ANDROID_SDK_BUILD_TOOLS_VERSION ?= 29.0.3 diff --git a/doc/source/quickstart.rst b/doc/source/quickstart.rst index 2f3bcf208b..6797bcadc6 100644 --- a/doc/source/quickstart.rst +++ b/doc/source/quickstart.rst @@ -120,7 +120,7 @@ named ``tools``, and you will need to run extra commands to install the SDK packages needed. For Android NDK, note that modern releases will only work on a 64-bit -operating system. **The minimal, and recommended, NDK version to use is r23b:** +operating system. **The minimal, and recommended, NDK version to use is r25b:** - `Go to ndk downloads page `_ - Windows users should create a virtual machine with an GNU Linux os diff --git a/pythonforandroid/recommendations.py b/pythonforandroid/recommendations.py index 3b886d0d0f..a8b2f81a59 100644 --- a/pythonforandroid/recommendations.py +++ b/pythonforandroid/recommendations.py @@ -8,11 +8,11 @@ from pythonforandroid.util import BuildInterruptingException # We only check the NDK major version -MIN_NDK_VERSION = 23 -MAX_NDK_VERSION = 23 +MIN_NDK_VERSION = 25 +MAX_NDK_VERSION = 25 # DO NOT CHANGE LINE FORMAT: buildozer parses the existence of a RECOMMENDED_NDK_VERSION -RECOMMENDED_NDK_VERSION = "23b" +RECOMMENDED_NDK_VERSION = "25b" NDK_DOWNLOAD_URL = "https://developer.android.com/ndk/downloads/" From ed43cbb5554d44b99f90facecf9ef793ddf1fd59 Mon Sep 17 00:00:00 2001 From: Mirko Galimberti Date: Sat, 10 Sep 2022 09:07:25 +0200 Subject: [PATCH 02/26] toml may not be available on systemwide python (#2670) --- tests/test_pythonpackage_basic.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/test_pythonpackage_basic.py b/tests/test_pythonpackage_basic.py index 87f7759df7..63c4003f72 100644 --- a/tests/test_pythonpackage_basic.py +++ b/tests/test_pythonpackage_basic.py @@ -269,11 +269,15 @@ def test_systemwide_python(self): p2 = os.path.normpath(pybin) assert p1 == p2 except RuntimeError as e: + # (remember this is not in a virtualenv) + # Some deps may not be installed, so we just avoid to raise + # an exception here, as a missing dep should not make the test + # fail. if "pep517" in str(e.args): # System python probably doesn't have pep517 available! - # (remember this is not in a virtualenv) - # Not much we can do in that case since pythonpackage needs it, - # so we'll skip this particular check. + pass + elif "toml" in str(e.args): + # System python probably doesn't have toml available! pass else: raise From 6505cfc1283ef5082e49a39e4cbc88ca20ddc7e4 Mon Sep 17 00:00:00 2001 From: Mirko Galimberti Date: Mon, 19 Sep 2022 20:54:48 +0200 Subject: [PATCH 03/26] Fixes libvpx build (#2672) * Fixes libvpx build * Add --disable-neon-asm for armeabi-v7a --- ci/constants.py | 1 - pythonforandroid/recipes/libvpx/__init__.py | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ci/constants.py b/ci/constants.py index 13560dcd5d..021e64a74a 100644 --- a/ci/constants.py +++ b/ci/constants.py @@ -16,7 +16,6 @@ class TargetPython(Enum): # build_dir = glob.glob('build/lib.*')[0] # IndexError: list index out of range 'secp256k1', - 'ffpyplayer', # requires `libpq-dev` system dependency e.g. for `pg_config` binary 'psycopg2', # most likely some setup in the Docker container, because it works in host diff --git a/pythonforandroid/recipes/libvpx/__init__.py b/pythonforandroid/recipes/libvpx/__init__.py index f0d72f59d7..0173e366d9 100644 --- a/pythonforandroid/recipes/libvpx/__init__.py +++ b/pythonforandroid/recipes/libvpx/__init__.py @@ -44,9 +44,12 @@ def build_arch(self, arch): '--disable-docs', '--disable-install-docs', '--disable-realtime-only', - '--enable-external-build', f'--prefix={realpath(".")}', ] + + if arch.arch == 'armeabi-v7a': + flags.append('--disable-neon-asm') + configure = sh.Command('./configure') shprint(configure, *flags, _env=env) shprint(sh.make, '-j', str(cpu_count()), _env=env) From b379a1cd72c69548fcd221ce3118a2719b75d7af Mon Sep 17 00:00:00 2001 From: Dan Nicholson Date: Mon, 19 Sep 2022 13:56:25 -0600 Subject: [PATCH 04/26] android/activity: Add Application.ActivityLifecycleCallbacks helpers (#2669) The `Application.ActivityLifecycleCallbacks` interface is used to register a class that can receive callbacks on Activity lifecycle changes. Add a `PythonJavaClass` implementing the interface and helper functions to register python callbacks with it. This can be used to perform actions in the python app when the activity changes lifecycle states. --- doc/source/apis.rst | 45 ++++++ .../recipes/android/src/android/activity.py | 151 ++++++++++++++++++ .../test_app/app_flask.py | 2 + .../on_device_unit_tests/test_app/app_kivy.py | 4 + .../on_device_unit_tests/test_app/tools.py | 16 ++ 5 files changed, 218 insertions(+) diff --git a/doc/source/apis.rst b/doc/source/apis.rst index 54b4f18f00..a9f66ec933 100644 --- a/doc/source/apis.rst +++ b/doc/source/apis.rst @@ -190,6 +190,51 @@ Example:: # ... +Activity lifecycle handling +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. module:: android.activity + +The Android ``Application`` class provides the `ActivityLifecycleCallbacks +`_ +interface where callbacks can be registered corresponding to `activity +lifecycle +`_ +changes. These callbacks can be used to implement logic in the Python app when +the activity changes lifecycle states. + +Note that some of the callbacks are not useful in the Python app. For example, +an `onActivityCreated` callback will never be run since the the activity's +`onCreate` callback will complete before the Python app is running. Similarly, +saving instance state in an `onActivitySaveInstanceState` callback will not be +helpful since the Python app doesn't have access to the restored instance +state. + +.. function:: register_activity_lifecycle_callbacks(callbackname=callback, ...) + + This allows you to bind a callbacks to Activity lifecycle state changes. + The callback names correspond to ``ActivityLifecycleCallbacks`` method + names such as ``onActivityStarted``. See the `ActivityLifecycleCallbacks + `_ + documentation for names and function signatures for the callbacks. + +.. function:: unregister_activity_lifecycle_callbacks(instance) + + Unregister a ``ActivityLifecycleCallbacks`` instance previously registered + with :func:`register_activity_lifecycle_callbacks`. + +Example:: + + from android.activity import register_activity_lifecycle_callbacks + + def on_activity_stopped(activity): + print('Activity is stopping') + + register_activity_lifecycle_callbacks( + onActivityStopped=on_activity_stopped, + ) + + Receiving Broadcast message ~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/pythonforandroid/recipes/android/src/android/activity.py b/pythonforandroid/recipes/android/src/android/activity.py index 4e1581bbea..78d068c9f0 100644 --- a/pythonforandroid/recipes/android/src/android/activity.py +++ b/pythonforandroid/recipes/android/src/android/activity.py @@ -61,3 +61,154 @@ def unbind(**kwargs): _activity.unregisterNewIntentListener(listener) elif event == 'on_activity_result': _activity.unregisterActivityResultListener(listener) + + +# Keep a reference to all the registered classes so that python doesn't +# garbage collect them. +_lifecycle_callbacks = set() + + +class ActivityLifecycleCallbacks(PythonJavaClass): + """Callback class for handling PythonActivity lifecycle transitions""" + + __javainterfaces__ = ['android/app/Application$ActivityLifecycleCallbacks'] + + def __init__(self, callbacks): + super().__init__() + + # It would be nice to use keyword arguments, but PythonJavaClass + # doesn't allow that in its __cinit__ method. + if not isinstance(callbacks, dict): + raise ValueError('callbacks must be a dict instance') + self.callbacks = callbacks + + def _callback(self, name, *args): + func = self.callbacks.get(name) + if func: + return func(*args) + + @java_method('(Landroid/app/Activity;Landroid/os/Bundle;)V') + def onActivityCreated(self, activity, savedInstanceState): + self._callback('onActivityCreated', activity, savedInstanceState) + + @java_method('(Landroid/app/Activity;)V') + def onActivityDestroyed(self, activity): + self._callback('onActivityDestroyed', activity) + + @java_method('(Landroid/app/Activity;)V') + def onActivityPaused(self, activity): + self._callback('onActivityPaused', activity) + + @java_method('(Landroid/app/Activity;Landroid/os/Bundle;)V') + def onActivityPostCreated(self, activity, savedInstanceState): + self._callback('onActivityPostCreated', activity, savedInstanceState) + + @java_method('(Landroid/app/Activity;)V') + def onActivityPostDestroyed(self, activity): + self._callback('onActivityPostDestroyed', activity) + + @java_method('(Landroid/app/Activity;)V') + def onActivityPostPaused(self, activity): + self._callback('onActivityPostPaused', activity) + + @java_method('(Landroid/app/Activity;)V') + def onActivityPostResumed(self, activity): + self._callback('onActivityPostResumed', activity) + + @java_method('(Landroid/app/Activity;Landroid/os/Bundle;)V') + def onActivityPostSaveInstanceState(self, activity, outState): + self._callback('onActivityPostSaveInstanceState', activity, outState) + + @java_method('(Landroid/app/Activity;)V') + def onActivityPostStarted(self, activity): + self._callback('onActivityPostStarted', activity) + + @java_method('(Landroid/app/Activity;)V') + def onActivityPostStopped(self, activity): + self._callback('onActivityPostStopped', activity) + + @java_method('(Landroid/app/Activity;Landroid/os/Bundle;)V') + def onActivityPreCreated(self, activity, savedInstanceState): + self._callback('onActivityPreCreated', activity, savedInstanceState) + + @java_method('(Landroid/app/Activity;)V') + def onActivityPreDestroyed(self, activity): + self._callback('onActivityPreDestroyed', activity) + + @java_method('(Landroid/app/Activity;)V') + def onActivityPrePaused(self, activity): + self._callback('onActivityPrePaused', activity) + + @java_method('(Landroid/app/Activity;)V') + def onActivityPreResumed(self, activity): + self._callback('onActivityPreResumed', activity) + + @java_method('(Landroid/app/Activity;Landroid/os/Bundle;)V') + def onActivityPreSaveInstanceState(self, activity, outState): + self._callback('onActivityPreSaveInstanceState', activity, outState) + + @java_method('(Landroid/app/Activity;)V') + def onActivityPreStarted(self, activity): + self._callback('onActivityPreStarted', activity) + + @java_method('(Landroid/app/Activity;)V') + def onActivityPreStopped(self, activity): + self._callback('onActivityPreStopped', activity) + + @java_method('(Landroid/app/Activity;)V') + def onActivityResumed(self, activity): + self._callback('onActivityResumed', activity) + + @java_method('(Landroid/app/Activity;Landroid/os/Bundle;)V') + def onActivitySaveInstanceState(self, activity, outState): + self._callback('onActivitySaveInstanceState', activity, outState) + + @java_method('(Landroid/app/Activity;)V') + def onActivityStarted(self, activity): + self._callback('onActivityStarted', activity) + + @java_method('(Landroid/app/Activity;)V') + def onActivityStopped(self, activity): + self._callback('onActivityStopped', activity) + + +def register_activity_lifecycle_callbacks(**callbacks): + """Register ActivityLifecycleCallbacks instance + + The callbacks are supplied as keyword arguments corresponding to the + Application.ActivityLifecycleCallbacks methods such as + onActivityStarted. See the ActivityLifecycleCallbacks documentation + for the signature of each method. + + The ActivityLifecycleCallbacks instance is returned so it can be + supplied to unregister_activity_lifecycle_callbacks if needed. + """ + instance = ActivityLifecycleCallbacks(callbacks) + _lifecycle_callbacks.add(instance) + + # Use the registerActivityLifecycleCallbacks method from the + # Activity class if it's available (API 29) since it guarantees the + # callbacks will only be run for that activity. Otherwise, fallback + # to the method on the Application class (API 14). In practice there + # should be no difference since p4a applications only have a single + # activity. + if hasattr(_activity, 'registerActivityLifecycleCallbacks'): + _activity.registerActivityLifecycleCallbacks(instance) + else: + app = _activity.getApplication() + app.registerActivityLifecycleCallbacks(instance) + return instance + + +def unregister_activity_lifecycle_callbacks(instance): + """Unregister ActivityLifecycleCallbacks instance""" + if hasattr(_activity, 'unregisterActivityLifecycleCallbacks'): + _activity.unregisterActivityLifecycleCallbacks(instance) + else: + app = _activity.getApplication() + app.unregisterActivityLifecycleCallbacks(instance) + + try: + _lifecycle_callbacks.remove(instance) + except KeyError: + pass diff --git a/testapps/on_device_unit_tests/test_app/app_flask.py b/testapps/on_device_unit_tests/test_app/app_flask.py index 17f9300a57..27ae7d4ad4 100644 --- a/testapps/on_device_unit_tests/test_app/app_flask.py +++ b/testapps/on_device_unit_tests/test_app/app_flask.py @@ -25,10 +25,12 @@ vibrate_with_pyjnius, get_android_python_activity, set_device_orientation, + setup_lifecycle_callbacks, ) app = Flask(__name__) +setup_lifecycle_callbacks() service_running = False TESTS_TO_PERFORM = dict() NON_ANDROID_DEVICE_MSG = 'Not running from Android device' diff --git a/testapps/on_device_unit_tests/test_app/app_kivy.py b/testapps/on_device_unit_tests/test_app/app_kivy.py index 8095998819..94ae5fe511 100644 --- a/testapps/on_device_unit_tests/test_app/app_kivy.py +++ b/testapps/on_device_unit_tests/test_app/app_kivy.py @@ -22,6 +22,7 @@ load_kv_from, raise_error, run_test_suites_into_buffer, + setup_lifecycle_callbacks, vibrate_with_pyjnius, ) from widgets import TestImage @@ -53,6 +54,9 @@ def build(self): self.sm = Builder.load_string(screen_manager_app) return self.sm + def on_start(self): + setup_lifecycle_callbacks() + def reset_unittests_results(self, refresh_ui=False): for img in get_images_with_extension(): subprocess.call(["rm", "-r", img]) diff --git a/testapps/on_device_unit_tests/test_app/tools.py b/testapps/on_device_unit_tests/test_app/tools.py index 16dac01022..398919d243 100644 --- a/testapps/on_device_unit_tests/test_app/tools.py +++ b/testapps/on_device_unit_tests/test_app/tools.py @@ -160,3 +160,19 @@ def set_device_orientation(direction): else: activity.setRequestedOrientation( ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + + +@skip_if_not_running_from_android_device +def setup_lifecycle_callbacks(): + """ + Register example ActivityLifecycleCallbacks + """ + from android.activity import register_activity_lifecycle_callbacks + + register_activity_lifecycle_callbacks( + onActivityStarted=lambda activity: print('onActivityStarted'), + onActivityPaused=lambda activity: print('onActivityPaused'), + onActivityResumed=lambda activity: print('onActivityResumed'), + onActivityStopped=lambda activity: print('onActivityStopped'), + onActivityDestroyed=lambda activity: print('onActivityDestroyed'), + ) From 6cf860495ba62038efda5e7694a0081cea68b547 Mon Sep 17 00:00:00 2001 From: Dan Nicholson Date: Tue, 20 Sep 2022 10:19:34 -0600 Subject: [PATCH 05/26] Include HOME in build environment (#2582) Many of the tools run by p4a store intermediate files in the user's home directory. Passing the HOME environment variable to the build environment has 2 positive effects: 1. To completely encapsulate the build, HOME can be set to an alternate directory than the user's actual home directory. Many tools such as p4a have options to override these paths, but that must be done on a case by case basis and it would require that p4a pass through these options or environment variables. 2. In containerized environments the user may not be registered in the accounts database. If the user's home directory can't be found from the HOME environment variable or the accounts database, many tools will fail. An example of this is python2.7 when run by `ndk-build`. --- pythonforandroid/archs.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pythonforandroid/archs.py b/pythonforandroid/archs.py index d5ef40ad4a..b960ca6b4d 100644 --- a/pythonforandroid/archs.py +++ b/pythonforandroid/archs.py @@ -109,6 +109,18 @@ def get_clang_exe(self, with_target=False, plus_plus=False): def get_env(self, with_flags_in_cc=True): env = {} + # HOME: User's home directory + # + # Many tools including p4a store outputs in the user's home + # directory. This is found from the HOME environment variable + # and falls back to the system account database. Setting HOME + # can be used to globally divert these tools to use a different + # path. Furthermore, in containerized environments the user may + # not exist in the account database, so if HOME isn't set than + # these tools will fail. + if 'HOME' in environ: + env['HOME'] = environ['HOME'] + # CFLAGS/CXXFLAGS: the processor flags env['CFLAGS'] = ' '.join(self.common_cflags).format(target=self.target) if self.arch_cflags: From 9ca92f4c21a663050a375d186c87666c8cf10730 Mon Sep 17 00:00:00 2001 From: Kevin Ollivier Date: Wed, 22 Apr 2020 11:29:19 -0700 Subject: [PATCH 06/26] Resize webview when keyboard is shown When the adjust mode is unspecified, Android will not resize the webview when showing the on screen keyboard to keep the input element on screen. Explicitly instruct it to use `adjustResize` mode. Possibly this is because the webview bootstrap uses an `AbsoluteLayout` as the top level widget. See https://developer.android.com/guide/topics/manifest/activity-element#wsoft for details. --- .../bootstraps/webview/build/templates/AndroidManifest.tmpl.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pythonforandroid/bootstraps/webview/build/templates/AndroidManifest.tmpl.xml b/pythonforandroid/bootstraps/webview/build/templates/AndroidManifest.tmpl.xml index e99c66d439..f77533b1e6 100644 --- a/pythonforandroid/bootstraps/webview/build/templates/AndroidManifest.tmpl.xml +++ b/pythonforandroid/bootstraps/webview/build/templates/AndroidManifest.tmpl.xml @@ -70,6 +70,7 @@ {% if args.activity_launch_mode %} android:launchMode="{{ args.activity_launch_mode }}" {% endif %} + android:windowSoftInputMode="adjustResize" > From f4a91b06e24ce9f137b996e5eff5f319b9a6ae80 Mon Sep 17 00:00:00 2001 From: RobertF <34464649+RobertFlatt@users.noreply.github.com> Date: Tue, 27 Sep 2022 08:48:18 -1000 Subject: [PATCH 07/26] Add some permissions from API 31 to API 33 (#2677) --- .../android/src/android/permissions.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/pythonforandroid/recipes/android/src/android/permissions.py b/pythonforandroid/recipes/android/src/android/permissions.py index 6477657ddb..0ce568fbe4 100644 --- a/pythonforandroid/recipes/android/src/android/permissions.py +++ b/pythonforandroid/recipes/android/src/android/permissions.py @@ -98,6 +98,15 @@ class Permission: BLUETOOTH = ( "android.permission.BLUETOOTH" ) + BLUETOOTH_ADVERTISE = ( + "android.permission.BLUETOOTH_ADVERTISE" + ) + BLUETOOTH_CONNECT = ( + "android.permission.BLUETOOTH_CONNECT" + ) + BLUETOOTH_SCAN = ( + "android.permission.BLUETOOTH_SCAN" + ) BLUETOOTH_ADMIN = ( "android.permission.BLUETOOTH_ADMIN" ) @@ -233,6 +242,9 @@ class Permission: MOUNT_UNMOUNT_FILESYSTEMS = ( "android.permission.MOUNT_UNMOUNT_FILESYSTEMS" ) + NEARBY_WIFI_DEVICES = ( + "android.permission.NEARBY_WIFI_DEVICES" + ) NFC = ( "android.permission.NFC" ) @@ -245,6 +257,9 @@ class Permission: PERSISTENT_ACTIVITY = ( "android.permission.PERSISTENT_ACTIVITY" ) + POST_NOTIFICATIONS = ( + "android.permission.POST_NOTIFICATIONS" + ) PROCESS_OUTGOING_CALLS = ( "android.permission.PROCESS_OUTGOING_CALLS" ) @@ -269,6 +284,15 @@ class Permission: READ_LOGS = ( "android.permission.READ_LOGS" ) + READ_MEDIA_AUDIO = ( + "android.permission.READ_MEDIA_AUDIO" + ) + READ_MEDIA_IMAGES = ( + "android.permission.READ_MEDIA_IMAGES" + ) + READ_MEDIA_VIDEO = ( + "android.permission.READ_MEDIA_VIDEO" + ) READ_PHONE_NUMBERS = ( "android.permission.READ_PHONE_NUMBERS" ) From 25f3a534d861b4f55a51277590b7042920edfad8 Mon Sep 17 00:00:00 2001 From: ghost43 Date: Thu, 6 Oct 2022 16:42:32 +0000 Subject: [PATCH 08/26] requirements: relax version bound on "pep517" (#2680) The "<0.7.0" bound was added in https://github.com/kivy/python-for-android/commit/9f6d6fcce748be96ed606815aca9294644ac90cc to fix https://github.com/kivy/python-for-android/issues/1994 : > The `TestGetSystemPythonExecutable.test_virtualenv` and `TestGetSystemPythonExecutable.test_venv` tests started failing all of a sudden. Error was: > > ``` > ModuleNotFoundError: No module named \'pytoml\'\n' > ``` > > This ca be reproduced in local via: > > ```shell > pytest tests/test_pythonpackage_basic.py::TestGetSystemPythonExecutable::test_virtualenv > ``` I think this no longer applies, `$ pytest tests/test_pythonpackage_basic.py::TestGetSystemPythonExecutable` runs successfully for me, using latest `pep517==0.13.0`. --- setup.py | 2 +- tests/test_pythonpackage_basic.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index fb3f24d535..eaaad56883 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ install_reqs = [ 'appdirs', 'colorama>=0.3.3', 'jinja2', 'sh>=1.10; sys_platform!="nt"', - 'pep517<0.7.0', 'toml', + 'pep517', 'toml', ] # (pep517 and toml are used by pythonpackage.py) diff --git a/tests/test_pythonpackage_basic.py b/tests/test_pythonpackage_basic.py index 63c4003f72..b05344b56b 100644 --- a/tests/test_pythonpackage_basic.py +++ b/tests/test_pythonpackage_basic.py @@ -304,7 +304,7 @@ def test_venv(self): ]) subprocess.check_output([ os.path.join(test_dir, "venv", "bin", "pip"), - "install", "-U", "pep517<0.7.0" + "install", "-U", "pep517" ]) subprocess.check_output([ os.path.join(test_dir, "venv", "bin", "pip"), From f18dd83009d7707d06851efac1f70bcf869f383c Mon Sep 17 00:00:00 2001 From: SomberNight Date: Fri, 7 Oct 2022 16:01:15 +0000 Subject: [PATCH 09/26] recipe.download_file: implement shallow git cloning When a recipe uses a `git+...` url, and has a `version` specified, only do a shallow git clone. This saves disk space and bandwidth. Tested with a custom qt5 recipe. Without this patch, the git clone on disk was 8.5 GB, now it is 5.0 GB. ``` class Qt5Recipe(BootstrapNDKRecipe): url = 'git+https://code.qt.io/qt/qt5.git' #version = '5.15.2' version = '9b43a43ee96198674060c6b9591e515e2d27c28f' ``` --- pythonforandroid/recipe.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index b4cd9bb3cf..130db7a559 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -213,24 +213,26 @@ def report_hook(index, blksize, size): break return target elif parsed_url.scheme in ('git', 'git+file', 'git+ssh', 'git+http', 'git+https'): - if isdir(target): - with current_directory(target): - shprint(sh.git, 'fetch', '--tags', '--recurse-submodules') - if self.version: - shprint(sh.git, 'checkout', self.version) - branch = sh.git('branch', '--show-current') - if branch: - shprint(sh.git, 'pull') - shprint(sh.git, 'pull', '--recurse-submodules') - shprint(sh.git, 'submodule', 'update', '--recursive') - else: + if not isdir(target): if url.startswith('git+'): url = url[4:] - shprint(sh.git, 'clone', '--recursive', url, target) + # if 'version' is specified, do a shallow clone if self.version: + shprint(sh.mkdir, '-p', target) with current_directory(target): - shprint(sh.git, 'checkout', self.version) - shprint(sh.git, 'submodule', 'update', '--recursive') + shprint(sh.git, 'init') + shprint(sh.git, 'remote', 'add', 'origin', url) + else: + shprint(sh.git, 'clone', '--recursive', url, target) + with current_directory(target): + if self.version: + shprint(sh.git, 'fetch', '--depth', '1', 'origin', self.version) + shprint(sh.git, 'checkout', self.version) + branch = sh.git('branch', '--show-current') + if branch: + shprint(sh.git, 'pull') + shprint(sh.git, 'pull', '--recurse-submodules') + shprint(sh.git, 'submodule', 'update', '--recursive', '--init', '--depth', '1') return target def apply_patch(self, filename, arch, build_dir=None): From 5e7a1fa05484c8c264988ecacb0edfdeed30cb00 Mon Sep 17 00:00:00 2001 From: Mirko Galimberti Date: Sat, 8 Oct 2022 13:56:11 +0200 Subject: [PATCH 10/26] Update SDL2, SDL2_ttf, SDL2_mixer, SDL2_image to latest releases (#2673) --- .../java/org/kivy/android/PythonActivity.java | 2 +- .../build/src/patches/SDLActivity.java.patch | 86 ++++++++----------- pythonforandroid/recipes/kivy/__init__.py | 22 ++++- .../kivy/sdl-gl-swapwindow-nogil.patch | 32 +++++++ pythonforandroid/recipes/sdl2/__init__.py | 8 +- .../recipes/sdl2_image/__init__.py | 21 +++-- .../add_ndk_platform_include_dir.patch | 13 --- .../recipes/sdl2_image/enable-webp.patch | 12 +++ .../recipes/sdl2_image/extra_cflags.patch | 11 --- .../sdl2_image/toggle_jpg_png_webp.patch | 25 ------ .../recipes/sdl2_mixer/__init__.py | 6 +- .../toggle_modplug_mikmod_smpeg_ogg.patch | 25 ------ pythonforandroid/recipes/sdl2_ttf/__init__.py | 4 +- setup.py | 2 +- 14 files changed, 126 insertions(+), 143 deletions(-) create mode 100644 pythonforandroid/recipes/kivy/sdl-gl-swapwindow-nogil.patch delete mode 100644 pythonforandroid/recipes/sdl2_image/add_ndk_platform_include_dir.patch create mode 100644 pythonforandroid/recipes/sdl2_image/enable-webp.patch delete mode 100644 pythonforandroid/recipes/sdl2_image/extra_cflags.patch delete mode 100644 pythonforandroid/recipes/sdl2_image/toggle_jpg_png_webp.patch delete mode 100644 pythonforandroid/recipes/sdl2_mixer/toggle_modplug_mikmod_smpeg_ogg.patch diff --git a/pythonforandroid/bootstraps/sdl2/build/src/main/java/org/kivy/android/PythonActivity.java b/pythonforandroid/bootstraps/sdl2/build/src/main/java/org/kivy/android/PythonActivity.java index f1fd943a34..ded381f161 100644 --- a/pythonforandroid/bootstraps/sdl2/build/src/main/java/org/kivy/android/PythonActivity.java +++ b/pythonforandroid/bootstraps/sdl2/build/src/main/java/org/kivy/android/PythonActivity.java @@ -199,7 +199,7 @@ protected void onPostExecute(String result) { ))) { // Because sometimes the app will get stuck here and never // actually run, ensure that it gets launched if we're active: - mActivity.onResume(); + mActivity.resumeNativeThread(); } } diff --git a/pythonforandroid/bootstraps/sdl2/build/src/patches/SDLActivity.java.patch b/pythonforandroid/bootstraps/sdl2/build/src/patches/SDLActivity.java.patch index 4c24e458b6..c7aa4bf666 100644 --- a/pythonforandroid/bootstraps/sdl2/build/src/patches/SDLActivity.java.patch +++ b/pythonforandroid/bootstraps/sdl2/build/src/patches/SDLActivity.java.patch @@ -1,18 +1,18 @@ --- a/src/main/java/org/libsdl/app/SDLActivity.java +++ b/src/main/java/org/libsdl/app/SDLActivity.java -@@ -94,6 +94,8 @@ +@@ -225,6 +225,8 @@ // This is what SDL runs in. It invokes SDL_main(), eventually protected static Thread mSDLThread; - + + public static int keyboardInputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; + protected static SDLGenericMotionListener_API12 getMotionListener() { if (mMotionListener == null) { if (Build.VERSION.SDK_INT >= 26) { -@@ -196,6 +198,15 @@ +@@ -323,6 +325,15 @@ Log.v(TAG, "onCreate()"); super.onCreate(savedInstanceState); - + + SDLActivity.initialize(); + // So we can call stuff from static callbacks + mSingleton = this; @@ -22,70 +22,54 @@ + // and we can't run setup tasks until that thread completes. + protected void finishLoad() { + - // Load shared libraries - String errorMsgBrokenLib = ""; try { -@@ -639,7 +650,7 @@ + Thread.currentThread().setName("SDLActivity"); + } catch (Exception e) { +@@ -824,7 +835,7 @@ Handler commandHandler = new SDLCommandHandler(); - + // Send a message from the SDLMain thread - boolean sendCommand(int command, Object data) { + protected boolean sendCommand(int command, Object data) { Message msg = commandHandler.obtainMessage(); msg.arg1 = command; msg.obj = data; -@@ -1051,6 +1062,21 @@ - return Arrays.copyOf(filtered, used); +@@ -1302,6 +1313,20 @@ + return SDLActivity.mSurface.getNativeSurface(); } - -+ /** -+ * Calls turnActive() on singleton to keep loading screen active -+ */ -+ public static void triggerAppConfirmedActive() { -+ mSingleton.appConfirmedActive(); -+ } -+ -+ /** -+ * Trick needed for loading screen, overridden by PythonActivity -+ * to keep loading screen active -+ */ -+ public void appConfirmedActive() { -+ } -+ + ++ /** ++ * Calls turnActive() on singleton to keep loading screen active ++ */ ++ public static void triggerAppConfirmedActive() { ++ mSingleton.appConfirmedActive(); ++ } ++ ++ /** ++ * Trick needed for loading screen, overridden by PythonActivity ++ * to keep loading screen active ++ */ ++ public void appConfirmedActive() { ++ } + - // APK expansion files support - - /** com.android.vending.expansion.zipfile.ZipResourceFile object or null. */ -@@ -1341,14 +1367,13 @@ - }; - - public void onSystemUiVisibilityChange(int visibility) { -- if (SDLActivity.mFullscreenModeActive && (visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0 || (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) { -- -+ // SDL2 BUGFIX (see sdl bug #4424 ) - REMOVE WHEN FIXED IN UPSTREAM !! -+ if (SDLActivity.mFullscreenModeActive && ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0 || (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0)) { - Handler handler = getWindow().getDecorView().getHandler(); - if (handler != null) { - handler.removeCallbacks(rehideSystemUi); // Prevent a hide loop. - handler.postDelayed(rehideSystemUi, 2000); - } -- + // Input + + /** +@@ -1795,7 +1820,7 @@ } - } - -@@ -1475,6 +1500,7 @@ - String[] arguments = SDLActivity.mSingleton.getArguments(); - + Log.v("SDL", "Running main function " + function + " from library " + library); +- + SDLActivity.mSingleton.appConfirmedActive(); SDLActivity.nativeRunMain(library, function, arguments); - + Log.v("SDL", "Finished main function"); -@@ -2002,7 +2028,7 @@ +@@ -2316,7 +2341,7 @@ public InputConnection onCreateInputConnection(EditorInfo outAttrs) { ic = new SDLInputConnection(this, true); - -- outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; + +- outAttrs.inputType = InputType.TYPE_CLASS_TEXT; + outAttrs.inputType = SDLActivity.keyboardInputType; outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */; + diff --git a/pythonforandroid/recipes/kivy/__init__.py b/pythonforandroid/recipes/kivy/__init__.py index 82e22dfda4..bece49ee6f 100644 --- a/pythonforandroid/recipes/kivy/__init__.py +++ b/pythonforandroid/recipes/kivy/__init__.py @@ -1,11 +1,26 @@ import glob from os.path import basename, exists, join +import sys +import packaging.version import sh from pythonforandroid.recipe import CythonRecipe from pythonforandroid.toolchain import current_directory, shprint +def is_kivy_affected_by_deadlock_issue(recipe=None, arch=None): + with current_directory(join(recipe.get_build_dir(arch.arch), "kivy")): + kivy_version = shprint( + sh.Command(sys.executable), + "-c", + "import _version; print(_version.__version__)", + ) + + return packaging.version.parse( + str(kivy_version) + ) < packaging.version.Version("2.2.0.dev0") + + class KivyRecipe(CythonRecipe): version = '2.1.0' url = 'https://github.com/kivy/kivy/archive/{version}.zip' @@ -14,6 +29,11 @@ class KivyRecipe(CythonRecipe): depends = ['sdl2', 'pyjnius', 'setuptools'] python_depends = ['certifi'] + # sdl-gl-swapwindow-nogil.patch is needed to avoid a deadlock. + # See: https://github.com/kivy/kivy/pull/8025 + # WARNING: Remove this patch when a new Kivy version is released. + patches = [("sdl-gl-swapwindow-nogil.patch", is_kivy_affected_by_deadlock_issue)] + def cythonize_build(self, env, build_dir='.'): super().cythonize_build(env, build_dir=build_dir) @@ -48,7 +68,7 @@ def get_recipe_env(self, arch): env['KIVY_SDL2_PATH'] = ':'.join([ join(self.ctx.bootstrap.build_dir, 'jni', 'SDL', 'include'), join(self.ctx.bootstrap.build_dir, 'jni', 'SDL2_image'), - join(self.ctx.bootstrap.build_dir, 'jni', 'SDL2_mixer'), + join(self.ctx.bootstrap.build_dir, 'jni', 'SDL2_mixer', 'include'), join(self.ctx.bootstrap.build_dir, 'jni', 'SDL2_ttf'), ]) diff --git a/pythonforandroid/recipes/kivy/sdl-gl-swapwindow-nogil.patch b/pythonforandroid/recipes/kivy/sdl-gl-swapwindow-nogil.patch new file mode 100644 index 0000000000..8a7c33a8be --- /dev/null +++ b/pythonforandroid/recipes/kivy/sdl-gl-swapwindow-nogil.patch @@ -0,0 +1,32 @@ +diff --git a/kivy/core/window/_window_sdl2.pyx b/kivy/core/window/_window_sdl2.pyx +index 46e15ec63..5002cd0f9 100644 +--- a/kivy/core/window/_window_sdl2.pyx ++++ b/kivy/core/window/_window_sdl2.pyx +@@ -746,7 +746,13 @@ cdef class _WindowSDL2Storage: + pass + + def flip(self): +- SDL_GL_SwapWindow(self.win) ++ # On Android (and potentially other platforms), SDL_GL_SwapWindow may ++ # lock the thread waiting for a mutex from another thread to be ++ # released. Calling SDL_GL_SwapWindow with the GIL released allow the ++ # other thread to run (e.g. to process the event filter callback) and ++ # release the mutex SDL_GL_SwapWindow is waiting for. ++ with nogil: ++ SDL_GL_SwapWindow(self.win) + + def save_bytes_in_png(self, filename, data, int width, int height): + cdef SDL_Surface *surface = SDL_CreateRGBSurfaceFrom( +diff --git a/kivy/lib/sdl2.pxi b/kivy/lib/sdl2.pxi +index 6a539de6d..3a5a69d23 100644 +--- a/kivy/lib/sdl2.pxi ++++ b/kivy/lib/sdl2.pxi +@@ -627,7 +627,7 @@ cdef extern from "SDL.h": + cdef SDL_GLContext SDL_GL_GetCurrentContext() + cdef int SDL_GL_SetSwapInterval(int interval) + cdef int SDL_GL_GetSwapInterval() +- cdef void SDL_GL_SwapWindow(SDL_Window * window) ++ cdef void SDL_GL_SwapWindow(SDL_Window * window) nogil + cdef void SDL_GL_DeleteContext(SDL_GLContext context) + + cdef int SDL_NumJoysticks() diff --git a/pythonforandroid/recipes/sdl2/__init__.py b/pythonforandroid/recipes/sdl2/__init__.py index 114a9ea8d4..d1929d06d8 100644 --- a/pythonforandroid/recipes/sdl2/__init__.py +++ b/pythonforandroid/recipes/sdl2/__init__.py @@ -6,9 +6,9 @@ class LibSDL2Recipe(BootstrapNDKRecipe): - version = "2.0.9" - url = "https://www.libsdl.org/release/SDL2-{version}.tar.gz" - md5sum = 'f2ecfba915c54f7200f504d8b48a5dfe' + version = "2.24.0" + url = "https://github.com/libsdl-org/SDL/releases/download/release-{version}/SDL2-{version}.tar.gz" + md5sum = 'cf539ffe9e0dd6f943ac9de75fd2e56e' dir_name = 'SDL' @@ -22,7 +22,7 @@ def get_recipe_env(self, arch=None, with_flags_in_cc=True, with_python=True): def should_build(self, arch): libdir = join(self.get_build_dir(arch.arch), "../..", "libs", arch.arch) - libs = ['libhidapi.so', 'libmain.so', 'libSDL2.so', 'libSDL2_image.so', 'libSDL2_mixer.so', 'libSDL2_ttf.so'] + libs = ['libmain.so', 'libSDL2.so', 'libSDL2_image.so', 'libSDL2_mixer.so', 'libSDL2_ttf.so'] return not all(exists(join(libdir, x)) for x in libs) def build_arch(self, arch): diff --git a/pythonforandroid/recipes/sdl2_image/__init__.py b/pythonforandroid/recipes/sdl2_image/__init__.py index 920b3ae648..a91c6f1bc2 100644 --- a/pythonforandroid/recipes/sdl2_image/__init__.py +++ b/pythonforandroid/recipes/sdl2_image/__init__.py @@ -1,14 +1,25 @@ +import os +import sh +from pythonforandroid.logger import shprint from pythonforandroid.recipe import BootstrapNDKRecipe +from pythonforandroid.util import current_directory class LibSDL2Image(BootstrapNDKRecipe): - version = '2.0.4' - url = 'https://www.libsdl.org/projects/SDL_image/release/SDL2_image-{version}.tar.gz' + version = '2.6.2' + url = 'https://github.com/libsdl-org/SDL_image/releases/download/release-{version}/SDL2_image-{version}.tar.gz' dir_name = 'SDL2_image' - patches = ['toggle_jpg_png_webp.patch', - 'extra_cflags.patch', - ] + patches = ['enable-webp.patch'] + + def prebuild_arch(self, arch): + # We do not have a folder for each arch on BootstrapNDKRecipe, so we + # need to skip the external deps download if we already have done it. + external_deps_dir = os.path.join(self.get_build_dir(arch.arch), "external") + if not os.path.exists(os.path.join(external_deps_dir, "libwebp")): + with current_directory(external_deps_dir): + shprint(sh.Command("./download.sh")) + super().prebuild_arch(arch) recipe = LibSDL2Image() diff --git a/pythonforandroid/recipes/sdl2_image/add_ndk_platform_include_dir.patch b/pythonforandroid/recipes/sdl2_image/add_ndk_platform_include_dir.patch deleted file mode 100644 index 123e5c573a..0000000000 --- a/pythonforandroid/recipes/sdl2_image/add_ndk_platform_include_dir.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/Android.mk b/Android.mk -index d48111b..3108b2c 100644 ---- a/Android.mk -+++ b/Android.mk -@@ -22,7 +22,7 @@ SUPPORT_WEBP := false - WEBP_LIBRARY_PATH := external/libwebp-0.3.0 - - --LOCAL_C_INCLUDES := $(LOCAL_PATH) -+LOCAL_C_INCLUDES := $(LOCAL_PATH) $(NDK_PLATFORM_INCLUDE_DIR) - LOCAL_CFLAGS := -DLOAD_BMP -DLOAD_GIF -DLOAD_LBM -DLOAD_PCX -DLOAD_PNM \ - -DLOAD_TGA -DLOAD_XCF -DLOAD_XPM -DLOAD_XV - LOCAL_CFLAGS += -O3 -fstrict-aliasing -fprefetch-loop-arrays $(EXTRA_CFLAGS) diff --git a/pythonforandroid/recipes/sdl2_image/enable-webp.patch b/pythonforandroid/recipes/sdl2_image/enable-webp.patch new file mode 100644 index 0000000000..98d72f2017 --- /dev/null +++ b/pythonforandroid/recipes/sdl2_image/enable-webp.patch @@ -0,0 +1,12 @@ +diff -Naur SDL2_image.orig/Android.mk SDL2_image/Android.mk +--- SDL2_image.orig/Android.mk 2022-10-03 20:51:52.000000000 +0200 ++++ SDL2_image/Android.mk 2022-10-03 20:52:48.000000000 +0200 +@@ -32,7 +32,7 @@ + + # Enable this if you want to support loading WebP images + # The library path should be a relative path to this directory. +-SUPPORT_WEBP ?= false ++SUPPORT_WEBP := true + WEBP_LIBRARY_PATH := external/libwebp + + diff --git a/pythonforandroid/recipes/sdl2_image/extra_cflags.patch b/pythonforandroid/recipes/sdl2_image/extra_cflags.patch deleted file mode 100644 index c2b875bbe4..0000000000 --- a/pythonforandroid/recipes/sdl2_image/extra_cflags.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- SDL2_image-2.0.4/Android.mk.orig 2018-10-31 15:58:52.000000000 +0100 -+++ SDL2_image-2.0.4/Android.mk 2019-02-07 21:57:29.552365123 +0100 -@@ -61,6 +61,8 @@ LOCAL_SRC_FILES := \ - - LOCAL_CFLAGS := -DLOAD_BMP -DLOAD_GIF -DLOAD_LBM -DLOAD_PCX -DLOAD_PNM \ - -DLOAD_SVG -DLOAD_TGA -DLOAD_XCF -DLOAD_XPM -DLOAD_XV -+LOCAL_CFLAGS += $(EXTRA_CFLAGS) -+ - LOCAL_LDLIBS := - LOCAL_STATIC_LIBRARIES := - LOCAL_SHARED_LIBRARIES := SDL2 diff --git a/pythonforandroid/recipes/sdl2_image/toggle_jpg_png_webp.patch b/pythonforandroid/recipes/sdl2_image/toggle_jpg_png_webp.patch deleted file mode 100644 index f7dce8ac62..0000000000 --- a/pythonforandroid/recipes/sdl2_image/toggle_jpg_png_webp.patch +++ /dev/null @@ -1,25 +0,0 @@ ---- SDL2_image-2.0.4/Android.mk.orig 2018-10-31 15:58:52.000000000 +0100 -+++ SDL2_image-2.0.4/Android.mk 2019-02-07 23:51:51.740299680 +0100 -@@ -3,18 +3,18 @@ SDL_IMAGE_LOCAL_PATH := $(call my-dir) - - # Enable this if you want to support loading JPEG images - # The library path should be a relative path to this directory. --SUPPORT_JPG ?= true -+SUPPORT_JPG := true - JPG_LIBRARY_PATH := external/jpeg-9b - - # Enable this if you want to support loading PNG images - # The library path should be a relative path to this directory. --SUPPORT_PNG ?= true -+SUPPORT_PNG := true - PNG_LIBRARY_PATH := external/libpng-1.6.32 - - # Enable this if you want to support loading WebP images - # The library path should be a relative path to this directory. --SUPPORT_WEBP ?= true --WEBP_LIBRARY_PATH := external/libwebp-0.6.0 -+SUPPORT_WEBP := true -+WEBP_LIBRARY_PATH := external/libwebp-1.0.0 - - - # Build the library diff --git a/pythonforandroid/recipes/sdl2_mixer/__init__.py b/pythonforandroid/recipes/sdl2_mixer/__init__.py index 32369f21ef..733ef21148 100644 --- a/pythonforandroid/recipes/sdl2_mixer/__init__.py +++ b/pythonforandroid/recipes/sdl2_mixer/__init__.py @@ -2,11 +2,9 @@ class LibSDL2Mixer(BootstrapNDKRecipe): - version = '2.0.4' - url = 'https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-{version}.tar.gz' + version = '2.6.2' + url = 'https://github.com/libsdl-org/SDL_mixer/releases/download/release-{version}/SDL2_mixer-{version}.tar.gz' dir_name = 'SDL2_mixer' - patches = ['toggle_modplug_mikmod_smpeg_ogg.patch'] - recipe = LibSDL2Mixer() diff --git a/pythonforandroid/recipes/sdl2_mixer/toggle_modplug_mikmod_smpeg_ogg.patch b/pythonforandroid/recipes/sdl2_mixer/toggle_modplug_mikmod_smpeg_ogg.patch deleted file mode 100644 index 9c3c9e52f4..0000000000 --- a/pythonforandroid/recipes/sdl2_mixer/toggle_modplug_mikmod_smpeg_ogg.patch +++ /dev/null @@ -1,25 +0,0 @@ ---- orig/Android.mk 2018-10-31 15:58:59.000000000 +0100 -+++ patched/Android.mk 2019-03-08 12:11:35.271258500 +0100 -@@ -3,7 +3,7 @@ - - - # Enable this if you want to support loading FLAC music with libFLAC --SUPPORT_FLAC ?= true -+SUPPORT_FLAC := false - FLAC_LIBRARY_PATH := external/flac-1.3.2 - - # Enable this if you want to support loading OGG Vorbis music via Tremor -@@ -12,11 +12,11 @@ - VORBIS_LIBRARY_PATH := external/libvorbisidec-1.2.1 - - # Enable this if you want to support loading MP3 music via MPG123 --SUPPORT_MP3_MPG123 ?= true -+SUPPORT_MP3_MPG123 := false - MPG123_LIBRARY_PATH := external/mpg123-1.25.6 - - # Enable this if you want to support loading MOD music via modplug --SUPPORT_MOD_MODPLUG ?= true -+SUPPORT_MOD_MODPLUG := false - MODPLUG_LIBRARY_PATH := external/libmodplug-0.8.9.0 - - # Enable this if you want to support TiMidity diff --git a/pythonforandroid/recipes/sdl2_ttf/__init__.py b/pythonforandroid/recipes/sdl2_ttf/__init__.py index bbebaac6a1..4934bd4a67 100644 --- a/pythonforandroid/recipes/sdl2_ttf/__init__.py +++ b/pythonforandroid/recipes/sdl2_ttf/__init__.py @@ -2,8 +2,8 @@ class LibSDL2TTF(BootstrapNDKRecipe): - version = '2.0.15' - url = 'https://www.libsdl.org/projects/SDL_ttf/release/SDL2_ttf-{version}.tar.gz' + version = '2.20.1' + url = 'https://github.com/libsdl-org/SDL_ttf/releases/download/release-{version}/SDL2_ttf-{version}.tar.gz' dir_name = 'SDL2_ttf' diff --git a/setup.py b/setup.py index eaaad56883..1f61113818 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ install_reqs = [ 'appdirs', 'colorama>=0.3.3', 'jinja2', 'sh>=1.10; sys_platform!="nt"', - 'pep517', 'toml', + 'pep517', 'toml', 'packaging', ] # (pep517 and toml are used by pythonpackage.py) From 11322742d0c9d3d2b7b4642110c911b394425ee1 Mon Sep 17 00:00:00 2001 From: Mirko Galimberti Date: Mon, 10 Oct 2022 18:32:14 +0200 Subject: [PATCH 11/26] Update MIN_TARGET_API and RECOMMENDED_TARGET_API (#2683) --- pythonforandroid/recommendations.py | 6 +++--- tests/test_recommendations.py | 2 +- tests/test_toolchain.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pythonforandroid/recommendations.py b/pythonforandroid/recommendations.py index a8b2f81a59..040c96234a 100644 --- a/pythonforandroid/recommendations.py +++ b/pythonforandroid/recommendations.py @@ -135,15 +135,15 @@ def read_ndk_version(ndk_dir): return ndk_version -MIN_TARGET_API = 26 +MIN_TARGET_API = 30 # highest version tested to work fine with SDL2 # should be a good default for other bootstraps too -RECOMMENDED_TARGET_API = 27 +RECOMMENDED_TARGET_API = 33 ARMEABI_MAX_TARGET_API = 21 OLD_API_MESSAGE = ( - 'Target APIs lower than 26 are no longer supported on Google Play, ' + 'Target APIs lower than 30 are no longer supported on Google Play, ' 'and are not recommended. Note that the Target API can be higher than ' 'your device Android version, and should usually be as high as possible.') diff --git a/tests/test_recommendations.py b/tests/test_recommendations.py index 3e9db9fe9e..df68bbe3e0 100644 --- a/tests/test_recommendations.py +++ b/tests/test_recommendations.py @@ -163,7 +163,7 @@ def test_check_target_api_warning_target_api(self): self.assertEqual( cm.output, [ - "WARNING:p4a:[WARNING]: Target API 25 < 26", + "WARNING:p4a:[WARNING]: Target API 29 < 30", "WARNING:p4a:[WARNING]: {old_api_msg}".format( old_api_msg=OLD_API_MESSAGE ), diff --git a/tests/test_toolchain.py b/tests/test_toolchain.py index 0cc2b1a7f5..874453f981 100644 --- a/tests/test_toolchain.py +++ b/tests/test_toolchain.py @@ -74,7 +74,7 @@ def test_create(self): 'pythonforandroid.bootstraps.service_only.' 'ServiceOnlyBootstrap.assemble_distribution' ) as m_run_distribute: - m_get_available_apis.return_value = [27] + m_get_available_apis.return_value = [33] tchain = ToolchainCL() assert tchain.ctx.activity_class_name == 'abc.myapp.android.CustomPythonActivity' assert tchain.ctx.service_class_name == 'xyz.myapp.android.CustomPythonService' From 7de5d2b8293b61e969925b7b29de09c11e3ca7dc Mon Sep 17 00:00:00 2001 From: Xavier Fiechter <31884704+xavierfiechter@users.noreply.github.com> Date: Thu, 20 Oct 2022 08:44:36 +0200 Subject: [PATCH 12/26] Make CI compile aiohttp again. (#2690) * Make CI compile aiohttp again. References: https://stackoverflow.com/a/64755338/20124004, https://stackoverflow.com/a/64755052/20124004, https://github.com/kivy/python-for-android/pull/2518 * version variable and github url * lost a newline * added missing line for pep * Changed URL --- pythonforandroid/recipes/aiohttp/__init__.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/pythonforandroid/recipes/aiohttp/__init__.py b/pythonforandroid/recipes/aiohttp/__init__.py index 74f6a07c7c..f32c653fcb 100644 --- a/pythonforandroid/recipes/aiohttp/__init__.py +++ b/pythonforandroid/recipes/aiohttp/__init__.py @@ -1,16 +1,20 @@ """Build AIOHTTP""" from typing import List -from pythonforandroid.recipe import CythonRecipe # type: ignore +from pythonforandroid.recipe import CppCompiledComponentsPythonRecipe -class AIOHTTPRecipe(CythonRecipe): # type: ignore # pylint: disable=R0903 - """Build AIOHTTP""" - - version = "v3.6.2" - url = "https://github.com/aio-libs/aiohttp/archive/{version}.zip" +class AIOHTTPRecipe(CppCompiledComponentsPythonRecipe): # type: ignore # pylint: disable=R0903 + version = "3.8.3" + url = "https://pypi.python.org/packages/source/a/aiohttp/aiohttp-{version}.tar.gz" name = "aiohttp" - depends: List[str] = ["setuptools"] + call_hostpython_via_targetpython = False + install_in_hostpython = True + + def get_recipe_env(self, arch): + env = super().get_recipe_env(arch) + env['LDFLAGS'] += ' -lc++_shared' + return env recipe = AIOHTTPRecipe() From f6473fb37682aac3c650cc70f1bd76eee56bf1a0 Mon Sep 17 00:00:00 2001 From: RobertF <34464649+RobertFlatt@users.noreply.github.com> Date: Tue, 25 Oct 2022 11:44:04 -1000 Subject: [PATCH 13/26] WRITE_EXTERNAL_STORAGE maxSdk --- .../bootstraps/sdl2/build/templates/AndroidManifest.tmpl.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/bootstraps/sdl2/build/templates/AndroidManifest.tmpl.xml b/pythonforandroid/bootstraps/sdl2/build/templates/AndroidManifest.tmpl.xml index 27b2f19433..b5ddde3874 100644 --- a/pythonforandroid/bootstraps/sdl2/build/templates/AndroidManifest.tmpl.xml +++ b/pythonforandroid/bootstraps/sdl2/build/templates/AndroidManifest.tmpl.xml @@ -25,7 +25,7 @@ - + {% for perm in args.permissions %} {% if '.' in perm %} From 53d77fc26c9e37eb6ce05f8899f4dae8334842b1 Mon Sep 17 00:00:00 2001 From: Mirko Galimberti Date: Fri, 4 Nov 2022 18:21:49 +0100 Subject: [PATCH 14/26] Fixes an issue regarding blacklist and bytecode compile + some cleanup (#2693) --- doc/source/buildoptions.rst | 2 +- doc/source/launcher.rst | 2 +- pythonforandroid/bdistapk.py | 2 +- .../bootstraps/common/build/build.py | 82 ++++++++----------- .../common/build/jni/application/src/start.c | 12 +-- .../java/org/kivy/android/PythonActivity.java | 3 +- .../java/org/kivy/android/PythonActivity.java | 3 +- .../java/org/kivy/android/PythonActivity.java | 3 +- pythonforandroid/build.py | 1 - pythonforandroid/recipes/python3/__init__.py | 6 +- 10 files changed, 44 insertions(+), 72 deletions(-) diff --git a/doc/source/buildoptions.rst b/doc/source/buildoptions.rst index d9f97175a2..ebe2d14e4f 100644 --- a/doc/source/buildoptions.rst +++ b/doc/source/buildoptions.rst @@ -89,7 +89,7 @@ options (this list may not be exhaustive): - ``--service``: A service name and the Python script it should run. See :ref:`arbitrary_scripts_services`. - ``--add-source``: Add a source directory to the app's Java code. -- ``--no-compile-pyo``: Do not optimise .py files to .pyo. +- ``--no-byte-compile-python``: Skip byte compile for .py files. - ``--enable-androidx``: Enable AndroidX support library. diff --git a/doc/source/launcher.rst b/doc/source/launcher.rst index f863e493fc..cc4ccf6ea5 100644 --- a/doc/source/launcher.rst +++ b/doc/source/launcher.rst @@ -48,7 +48,7 @@ grab an old (cached) package instead of a fresh one. .. warning:: Do not use any of `--private`, `--public`, `--dir` or other arguments for - adding `main.py` or `main.pyo` to the app. The argument `--launcher` is + adding `main.py` or `main.pyc` to the app. The argument `--launcher` is above them and tells the p4a to build the launcher version of the APK. Usage diff --git a/pythonforandroid/bdistapk.py b/pythonforandroid/bdistapk.py index cc1487c85e..bcf77cd60d 100644 --- a/pythonforandroid/bdistapk.py +++ b/pythonforandroid/bdistapk.py @@ -108,7 +108,7 @@ def prepare_build_dir(self): makedirs(new_dir) print('Including {}'.format(filen)) copyfile(filen, join(bdist_dir, filen)) - if basename(filen) in ('main.py', 'main.pyo'): + if basename(filen) in ('main.py', 'main.pyc'): main_py_dirs.append(filen) # This feels ridiculous, but how else to define the main.py dir? diff --git a/pythonforandroid/bootstraps/common/build/build.py b/pythonforandroid/bootstraps/common/build/build.py index 6885a333df..213abfa8a5 100644 --- a/pythonforandroid/bootstraps/common/build/build.py +++ b/pythonforandroid/bootstraps/common/build/build.py @@ -40,10 +40,6 @@ def get_hostpython(): return get_dist_info_for('hostpython') -def get_python_version(): - return get_dist_info_for('python_version') - - def get_bootstrap_name(): return get_dist_info_for('bootstrap') @@ -58,7 +54,6 @@ def get_bootstrap_name(): curdir = dirname(__file__) PYTHON = get_hostpython() -PYTHON_VERSION = get_python_version() if PYTHON is not None and not exists(PYTHON): PYTHON = None @@ -73,17 +68,16 @@ def get_bootstrap_name(): '~', '*.bak', '*.swp', + + # Android artifacts + '*.apk', + '*.aab', ] -# pyc/py -if PYTHON is not None: - BLACKLIST_PATTERNS.append('*.py') WHITELIST_PATTERNS = [] if get_bootstrap_name() in ('sdl2', 'webview', 'service_only'): WHITELIST_PATTERNS.append('pyconfig.h') -python_files = [] - environment = jinja2.Environment(loader=jinja2.FileSystemLoader( join(curdir, 'templates'))) @@ -150,23 +144,11 @@ def listfiles(d): yield fn -def make_tar(tfn, source_dirs, ignore_path=[], optimize_python=True): +def make_tar(tfn, source_dirs, byte_compile_python=False, optimize_python=True): ''' Make a zip file `fn` from the contents of source_dis. ''' - # selector function - def select(fn): - rfn = realpath(fn) - for p in ignore_path: - if p.endswith('/'): - p = p[:-1] - if rfn.startswith(p): - return False - if rfn in python_files: - return False - return not is_blacklist(fn) - def clean(tinfo): """cleaning function (for reproducible builds)""" tinfo.uid = tinfo.gid = 0 @@ -178,9 +160,12 @@ def clean(tinfo): files = [] for sd in source_dirs: sd = realpath(sd) - compile_dir(sd, optimize_python=optimize_python) - files += [(x, relpath(realpath(x), sd)) for x in listfiles(sd) - if select(x)] + for fn in listfiles(sd): + if is_blacklist(fn): + continue + if fn.endswith('.py') and byte_compile_python: + fn = compile_py_file(fn, optimize_python=optimize_python) + files.append((fn, relpath(realpath(fn), sd))) files.sort() # deterministic # create tar.gz of thoses files @@ -210,18 +195,15 @@ def clean(tinfo): gf.close() -def compile_dir(dfn, optimize_python=True): +def compile_py_file(python_file, optimize_python=True): ''' - Compile *.py in directory `dfn` to *.pyo + Compile python_file to *.pyc and return the filename of the *.pyc file. ''' if PYTHON is None: return - if int(PYTHON_VERSION[0]) >= 3: - args = [PYTHON, '-m', 'compileall', '-b', '-f', dfn] - else: - args = [PYTHON, '-m', 'compileall', '-f', dfn] + args = [PYTHON, '-m', 'compileall', '-b', '-f', python_file] if optimize_python: # -OO = strip docstrings args.insert(1, '-OO') @@ -233,16 +215,18 @@ def compile_dir(dfn, optimize_python=True): 'error, see logs above') exit(1) + return ".".join([os.path.splitext(python_file)[0], "pyc"]) + def make_package(args): - # If no launcher is specified, require a main.py/main.pyo: + # If no launcher is specified, require a main.py/main.pyc: if (get_bootstrap_name() != "sdl" or args.launcher is None) and \ get_bootstrap_name() not in ["webview", "service_library"]: # (webview doesn't need an entrypoint, apparently) if args.private is None or ( not exists(join(realpath(args.private), 'main.py')) and - not exists(join(realpath(args.private), 'main.pyo'))): - print('''BUILD FAILURE: No main.py(o) found in your app directory. This + not exists(join(realpath(args.private), 'main.pyc'))): + print('''BUILD FAILURE: No main.py(c) found in your app directory. This file must exist to act as the entry point for you app. If your app is started by a file with a different name, rename it to main.py or add a main.py that loads it.''') @@ -290,7 +274,6 @@ def make_package(args): variants = [ copy_path, copy_path.partition(".")[0] + ".pyc", - copy_path.partition(".")[0] + ".pyo", ] # Check in all variants with all possible endings: for variant in variants: @@ -326,11 +309,17 @@ def make_package(args): for arch in get_dist_info_for("archs"): libs_dir = f"libs/{arch}" make_tar( - join(libs_dir, 'libpybundle.so'), [f'_python_bundle__{arch}'], args.ignore_path, - optimize_python=args.optimize_python) + join(libs_dir, "libpybundle.so"), + [f"_python_bundle__{arch}"], + byte_compile_python=args.byte_compile_python, + optimize_python=args.optimize_python, + ) make_tar( - join(assets_dir, 'private.tar'), private_tar_dirs, args.ignore_path, - optimize_python=args.optimize_python) + join(assets_dir, "private.tar"), + private_tar_dirs, + byte_compile_python=args.byte_compile_python, + optimize_python=args.optimize_python, + ) finally: for directory in _temp_dirs_to_clean: shutil.rmtree(directory) @@ -824,8 +813,6 @@ def parse_args_and_make_package(args=None): ap.add_argument('--try-system-python-compile', dest='try_system_python_compile', action='store_true', help='Use the system python during compileall if possible.') - ap.add_argument('--no-compile-pyo', dest='no_compile_pyo', action='store_true', - help='Do not optimise .py files to .pyo.') ap.add_argument('--sign', action='store_true', help=('Try to sign the APK with your credentials. You must set ' 'the appropriate environment variables.')) @@ -844,9 +831,12 @@ def parse_args_and_make_package(args=None): 'files (containing your main.py entrypoint). ' 'See https://developer.android.com/guide/topics/data/' 'autobackup#IncludingFiles for more information')) + ap.add_argument('--no-byte-compile-python', dest='byte_compile_python', + action='store_false', default=True, + help='Skip byte compile for .py files.') ap.add_argument('--no-optimize-python', dest='optimize_python', action='store_false', default=True, - help=('Whether to compile to optimised .pyo files, using -OO ' + help=('Whether to compile to optimised .pyc files, using -OO ' '(strips docstrings and asserts)')) ap.add_argument('--extra-manifest-xml', default='', help=('Extra xml to write directly inside the element of' @@ -881,8 +871,6 @@ def _read_configuration(): args = ap.parse_args(args) - args.ignore_path = [] - if args.name and args.name[0] == '"' and args.name[-1] == '"': args.name = args.name[1:-1] @@ -925,10 +913,6 @@ def _read_configuration(): else: PYTHON = python_executable - if args.no_compile_pyo: - PYTHON = None - BLACKLIST_PATTERNS.remove('*.py') - if args.blacklist: with open(args.blacklist) as fd: patterns = [x.strip() for x in fd.read().splitlines() diff --git a/pythonforandroid/bootstraps/common/build/jni/application/src/start.c b/pythonforandroid/bootstraps/common/build/jni/application/src/start.c index d46b1fae0d..bad52186f8 100644 --- a/pythonforandroid/bootstraps/common/build/jni/application/src/start.c +++ b/pythonforandroid/bootstraps/common/build/jni/application/src/start.c @@ -251,14 +251,10 @@ int main(int argc, char *argv[]) { */ LOGP("Run user program, change dir and execute entrypoint"); - /* Get the entrypoint, search the .pyo then .py + /* Get the entrypoint, search the .pyc then .py */ char *dot = strrchr(env_entrypoint, '.'); -#if PY_MAJOR_VERSION > 2 char *ext = ".pyc"; -#else - char *ext = ".pyo"; -#endif if (dot <= 0) { LOGP("Invalid entrypoint, abort."); return -1; @@ -281,14 +277,10 @@ int main(int argc, char *argv[]) { strcpy(entrypoint, env_entrypoint); } } else if (!strcmp(dot, ".py")) { - /* if .py is passed, check the pyo version first */ + /* if .py is passed, check the pyc version first */ strcpy(entrypoint, env_entrypoint); entrypoint[strlen(env_entrypoint) + 1] = '\0'; -#if PY_MAJOR_VERSION > 2 entrypoint[strlen(env_entrypoint)] = 'c'; -#else - entrypoint[strlen(env_entrypoint)] = 'o'; -#endif if (!file_exists(entrypoint)) { /* fallback on pure python version */ if (!file_exists(env_entrypoint)) { diff --git a/pythonforandroid/bootstraps/sdl2/build/src/main/java/org/kivy/android/PythonActivity.java b/pythonforandroid/bootstraps/sdl2/build/src/main/java/org/kivy/android/PythonActivity.java index ded381f161..361975a4cf 100644 --- a/pythonforandroid/bootstraps/sdl2/build/src/main/java/org/kivy/android/PythonActivity.java +++ b/pythonforandroid/bootstraps/sdl2/build/src/main/java/org/kivy/android/PythonActivity.java @@ -419,11 +419,10 @@ public void run() { } public String getEntryPoint(String search_dir) { - /* Get the main file (.pyc|.pyo|.py) depending on if we + /* Get the main file (.pyc|.py) depending on if we * have a compiled version or not. */ List entryPoints = new ArrayList(); - entryPoints.add("main.pyo"); // python 2 compiled files entryPoints.add("main.pyc"); // python 3 compiled files for (String value : entryPoints) { File mainFile = new File(search_dir + "/" + value); diff --git a/pythonforandroid/bootstraps/service_only/build/src/main/java/org/kivy/android/PythonActivity.java b/pythonforandroid/bootstraps/service_only/build/src/main/java/org/kivy/android/PythonActivity.java index 87ea061c41..57112dd555 100644 --- a/pythonforandroid/bootstraps/service_only/build/src/main/java/org/kivy/android/PythonActivity.java +++ b/pythonforandroid/bootstraps/service_only/build/src/main/java/org/kivy/android/PythonActivity.java @@ -46,11 +46,10 @@ public String getAppRoot() { } public String getEntryPoint(String search_dir) { - /* Get the main file (.pyc|.pyo|.py) depending on if we + /* Get the main file (.pyc|.py) depending on if we * have a compiled version or not. */ List entryPoints = new ArrayList(); - entryPoints.add("main.pyo"); // python 2 compiled files entryPoints.add("main.pyc"); // python 3 compiled files for (String value : entryPoints) { File mainFile = new File(search_dir + "/" + value); diff --git a/pythonforandroid/bootstraps/webview/build/src/main/java/org/kivy/android/PythonActivity.java b/pythonforandroid/bootstraps/webview/build/src/main/java/org/kivy/android/PythonActivity.java index 8aa308b24a..2f0afdc6f4 100644 --- a/pythonforandroid/bootstraps/webview/build/src/main/java/org/kivy/android/PythonActivity.java +++ b/pythonforandroid/bootstraps/webview/build/src/main/java/org/kivy/android/PythonActivity.java @@ -68,11 +68,10 @@ public String getAppRoot() { } public String getEntryPoint(String search_dir) { - /* Get the main file (.pyc|.pyo|.py) depending on if we + /* Get the main file (.pyc|.py) depending on if we * have a compiled version or not. */ List entryPoints = new ArrayList(); - entryPoints.add("main.pyo"); // python 2 compiled files entryPoints.add("main.pyc"); // python 3 compiled files for (String value : entryPoints) { File mainFile = new File(search_dir + "/" + value); diff --git a/pythonforandroid/build.py b/pythonforandroid/build.py index 42b6b52add..645b368d00 100644 --- a/pythonforandroid/build.py +++ b/pythonforandroid/build.py @@ -456,7 +456,6 @@ def has_package(self, name, arch=None): return (exists(join(site_packages_dir, name)) or exists(join(site_packages_dir, name + '.py')) or exists(join(site_packages_dir, name + '.pyc')) or - exists(join(site_packages_dir, name + '.pyo')) or exists(join(site_packages_dir, name + '.so')) or glob.glob(join(site_packages_dir, name + '-*.egg'))) diff --git a/pythonforandroid/recipes/python3/__init__.py b/pythonforandroid/recipes/python3/__init__.py index c3c28c70fb..5894b3f9b0 100644 --- a/pythonforandroid/recipes/python3/__init__.py +++ b/pythonforandroid/recipes/python3/__init__.py @@ -364,11 +364,11 @@ def create_python_bundle(self, dirn, arch): self.major_minor_version_string )) - # Compile to *.pyc/*.pyo the python modules + # Compile to *.pyc the python modules self.compile_python_files(modules_build_dir) - # Compile to *.pyc/*.pyo the standard python library + # Compile to *.pyc the standard python library self.compile_python_files(join(self.get_build_dir(arch.arch), 'Lib')) - # Compile to *.pyc/*.pyo the other python packages (site-packages) + # Compile to *.pyc the other python packages (site-packages) self.compile_python_files(self.ctx.get_python_install_dir(arch.arch)) # Bundle compiled python modules to a folder From 5ca3a52b4a91d7b6f933e6b2f4b60fb4ed2d3c9a Mon Sep 17 00:00:00 2001 From: RobertF <34464649+RobertFlatt@users.noreply.github.com> Date: Fri, 4 Nov 2022 07:56:38 -1000 Subject: [PATCH 15/26] Added `--add-resource` option (#2684) * add_resources * add_resource * Update build.py * stateless * multiple kinds * pep8 * --add_resource --- doc/source/buildoptions.rst | 1 + .../bootstraps/common/build/build.py | 22 +++++++++++++++++++ pythonforandroid/toolchain.py | 12 ++++++++++ 3 files changed, 35 insertions(+) diff --git a/doc/source/buildoptions.rst b/doc/source/buildoptions.rst index ebe2d14e4f..05ce4ef699 100644 --- a/doc/source/buildoptions.rst +++ b/doc/source/buildoptions.rst @@ -91,6 +91,7 @@ options (this list may not be exhaustive): - ``--add-source``: Add a source directory to the app's Java code. - ``--no-byte-compile-python``: Skip byte compile for .py files. - ``--enable-androidx``: Enable AndroidX support library. +- ``--add-resource``: Put this file or directory in the apk res directory. webview diff --git a/pythonforandroid/bootstraps/common/build/build.py b/pythonforandroid/bootstraps/common/build/build.py index 213abfa8a5..c49d18fc4d 100644 --- a/pythonforandroid/bootstraps/common/build/build.py +++ b/pythonforandroid/bootstraps/common/build/build.py @@ -329,6 +329,24 @@ def make_package(args): # Prepare some variables for templating process res_dir = "src/main/res" + res_dir_initial = "src/res_initial" + # make res_dir stateless + if exists(res_dir_initial): + shutil.rmtree(res_dir, ignore_errors=True) + shutil.copytree(res_dir_initial, res_dir) + else: + shutil.copytree(res_dir, res_dir_initial) + + # Add user resouces + for resource in args.resources: + resource_src, resource_dest = resource.split(":") + if isfile(realpath(resource_src)): + ensure_dir(dirname(join(res_dir, resource_dest))) + shutil.copy(realpath(resource_src), join(res_dir, resource_dest)) + else: + shutil.copytree(realpath(resource_src), + join(res_dir, resource_dest), dirs_exist_ok=True) + default_icon = 'templates/kivy-icon.png' default_presplash = 'templates/kivy-presplash.jpg' shutil.copy( @@ -687,6 +705,10 @@ def parse_args_and_make_package(args=None): action="append", default=[], metavar="/path/to/source:dest", help='Put this in the assets folder at assets/dest') + ap.add_argument('--resource', dest='resources', + action="append", default=[], + metavar="/path/to/source:kind/asset", + help='Put this in the res folder at res/kind') ap.add_argument('--icon', dest='icon', help=('A png file to use as the icon for ' 'the application.')) diff --git a/pythonforandroid/toolchain.py b/pythonforandroid/toolchain.py index 1b81aa923c..daf895e897 100644 --- a/pythonforandroid/toolchain.py +++ b/pythonforandroid/toolchain.py @@ -512,6 +512,10 @@ def add_parser(subparsers, *args, **kwargs): '--add-asset', dest='assets', action="append", default=[], help='Put this in the assets folder in the apk.') + parser_packaging.add_argument( + '--add-resource', dest='resources', + action="append", default=[], + help='Put this in the res folder in the apk.') parser_packaging.add_argument( '--private', dest='private', help='the directory with the app source code files' + @@ -1000,6 +1004,14 @@ def _fix_args(args): asset_src = asset_dest = asset # take abspath now, because build.py will be run in bootstrap dir unknown_args += ["--asset", os.path.abspath(asset_src)+":"+asset_dest] + for resource in args.resources: + if ":" in resource: + resource_src, resource_dest = resource.split(":") + else: + resource_src = resource + resource_dest = "" + # take abspath now, because build.py will be run in bootstrap dir + unknown_args += ["--resource", os.path.abspath(resource_src)+":"+resource_dest] for i, arg in enumerate(unknown_args): argx = arg.split('=') if argx[0] in fix_args: From 538266d5169458f52641d618c5ca1782b4e5f295 Mon Sep 17 00:00:00 2001 From: Mirko Galimberti Date: Sat, 12 Nov 2022 10:25:20 +0100 Subject: [PATCH 16/26] Include paths for sdl2_mixer have changed. Added a method to return the correct one. (#2700) --- pythonforandroid/recipes/audiostream/__init__.py | 12 ++++++++---- pythonforandroid/recipes/ffpyplayer/__init__.py | 6 +++++- pythonforandroid/recipes/kivy/__init__.py | 3 ++- pythonforandroid/recipes/pygame/__init__.py | 7 ++++++- pythonforandroid/recipes/sdl2_mixer/__init__.py | 7 +++++++ tests/recipes/test_sdl2_mixer.py | 14 ++++++++++++++ 6 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 tests/recipes/test_sdl2_mixer.py diff --git a/pythonforandroid/recipes/audiostream/__init__.py b/pythonforandroid/recipes/audiostream/__init__.py index 888aca443c..00c92b3864 100644 --- a/pythonforandroid/recipes/audiostream/__init__.py +++ b/pythonforandroid/recipes/audiostream/__init__.py @@ -17,14 +17,18 @@ class AudiostreamRecipe(CythonRecipe): def get_recipe_env(self, arch): env = super().get_recipe_env(arch) sdl_include = 'SDL2' - sdl_mixer_include = 'SDL2_mixer' + env['USE_SDL2'] = 'True' env['SDL2_INCLUDE_DIR'] = join(self.ctx.bootstrap.build_dir, 'jni', 'SDL', 'include') - env['CFLAGS'] += ' -I{jni_path}/{sdl_include}/include -I{jni_path}/{sdl_mixer_include}'.format( + env['CFLAGS'] += ' -I{jni_path}/{sdl_include}/include'.format( jni_path=join(self.ctx.bootstrap.build_dir, 'jni'), - sdl_include=sdl_include, - sdl_mixer_include=sdl_mixer_include) + sdl_include=sdl_include) + + sdl2_mixer_recipe = self.get_recipe('sdl2_mixer', self.ctx) + for include_dir in sdl2_mixer_recipe.get_include_dirs(arch): + env['CFLAGS'] += ' -I{include_dir}'.format(include_dir=include_dir) + # NDKPLATFORM is our switch for detecting Android platform, so can't be None env['NDKPLATFORM'] = "NOTNONE" env['LIBLINK'] = 'NOTNONE' # Hacky fix. Needed by audiostream setup.py diff --git a/pythonforandroid/recipes/ffpyplayer/__init__.py b/pythonforandroid/recipes/ffpyplayer/__init__.py index 7aa8b0db2e..6260037a70 100644 --- a/pythonforandroid/recipes/ffpyplayer/__init__.py +++ b/pythonforandroid/recipes/ffpyplayer/__init__.py @@ -20,7 +20,11 @@ def get_recipe_env(self, arch, with_flags_in_cc=True): env["SDL_LIB_DIR"] = join(self.ctx.bootstrap.build_dir, 'libs', arch.arch) env["USE_SDL2_MIXER"] = '1' - env["SDL2_MIXER_INCLUDE_DIR"] = join(self.ctx.bootstrap.build_dir, 'jni', 'SDL2_mixer') + + # ffpyplayer does not allow to pass more than one include dir for sdl2_mixer (and ATM is + # not needed), so we only pass the first one. + sdl2_mixer_recipe = self.get_recipe('sdl2_mixer', self.ctx) + env["SDL2_MIXER_INCLUDE_DIR"] = sdl2_mixer_recipe.get_include_dirs(arch)[0] # NDKPLATFORM and LIBLINK are our switches for detecting Android platform, so can't be empty # FIXME: We may want to introduce a cleaner approach to this? diff --git a/pythonforandroid/recipes/kivy/__init__.py b/pythonforandroid/recipes/kivy/__init__.py index bece49ee6f..bc9041a318 100644 --- a/pythonforandroid/recipes/kivy/__init__.py +++ b/pythonforandroid/recipes/kivy/__init__.py @@ -65,10 +65,11 @@ def get_recipe_env(self, arch): if 'sdl2' in self.ctx.recipe_build_order: env['USE_SDL2'] = '1' env['KIVY_SPLIT_EXAMPLES'] = '1' + sdl2_mixer_recipe = self.get_recipe('sdl2_mixer', self.ctx) env['KIVY_SDL2_PATH'] = ':'.join([ join(self.ctx.bootstrap.build_dir, 'jni', 'SDL', 'include'), join(self.ctx.bootstrap.build_dir, 'jni', 'SDL2_image'), - join(self.ctx.bootstrap.build_dir, 'jni', 'SDL2_mixer', 'include'), + *sdl2_mixer_recipe.get_include_dirs(arch), join(self.ctx.bootstrap.build_dir, 'jni', 'SDL2_ttf'), ]) diff --git a/pythonforandroid/recipes/pygame/__init__.py b/pythonforandroid/recipes/pygame/__init__.py index 72e624ea32..99124deff7 100644 --- a/pythonforandroid/recipes/pygame/__init__.py +++ b/pythonforandroid/recipes/pygame/__init__.py @@ -37,6 +37,11 @@ def prebuild_arch(self, arch): jpeg = self.get_recipe('jpeg', self.ctx) jpeg_inc_dir = jpeg_lib_dir = jpeg.get_build_dir(arch.arch) + sdl_mixer_includes = "" + sdl2_mixer_recipe = self.get_recipe('sdl2_mixer', self.ctx) + for include_dir in sdl2_mixer_recipe.get_include_dirs(arch): + sdl_mixer_includes += f"-I{include_dir} " + setup_file = setup_template.format( sdl_includes=( " -I" + join(self.ctx.bootstrap.build_dir, 'jni', 'SDL', 'include') + @@ -44,7 +49,7 @@ def prebuild_arch(self, arch): " -L" + png_lib_dir + " -L" + jpeg_lib_dir + " -L" + arch.ndk_lib_dir_versioned), sdl_ttf_includes="-I"+join(self.ctx.bootstrap.build_dir, 'jni', 'SDL2_ttf'), sdl_image_includes="-I"+join(self.ctx.bootstrap.build_dir, 'jni', 'SDL2_image'), - sdl_mixer_includes="-I"+join(self.ctx.bootstrap.build_dir, 'jni', 'SDL2_mixer'), + sdl_mixer_includes=sdl_mixer_includes, jpeg_includes="-I"+jpeg_inc_dir, png_includes="-I"+png_inc_dir, freetype_includes="" diff --git a/pythonforandroid/recipes/sdl2_mixer/__init__.py b/pythonforandroid/recipes/sdl2_mixer/__init__.py index 733ef21148..0f02c4c3a4 100644 --- a/pythonforandroid/recipes/sdl2_mixer/__init__.py +++ b/pythonforandroid/recipes/sdl2_mixer/__init__.py @@ -1,3 +1,5 @@ +import os + from pythonforandroid.recipe import BootstrapNDKRecipe @@ -6,5 +8,10 @@ class LibSDL2Mixer(BootstrapNDKRecipe): url = 'https://github.com/libsdl-org/SDL_mixer/releases/download/release-{version}/SDL2_mixer-{version}.tar.gz' dir_name = 'SDL2_mixer' + def get_include_dirs(self, arch): + return [ + os.path.join(self.ctx.bootstrap.build_dir, "jni", "SDL2_mixer", "include") + ] + recipe = LibSDL2Mixer() diff --git a/tests/recipes/test_sdl2_mixer.py b/tests/recipes/test_sdl2_mixer.py new file mode 100644 index 0000000000..a583d9aa63 --- /dev/null +++ b/tests/recipes/test_sdl2_mixer.py @@ -0,0 +1,14 @@ +import unittest +from tests.recipes.recipe_lib_test import RecipeCtx + + +class TestSDL2MixerRecipe(RecipeCtx, unittest.TestCase): + """ + An unittest for recipe :mod:`~pythonforandroid.recipes.sdl2_mixer` + """ + recipe_name = "sdl2_mixer" + + def test_get_include_dirs(self): + list_of_includes = self.recipe.get_include_dirs(self.arch) + self.assertIsInstance(list_of_includes, list) + self.assertTrue(list_of_includes[0].endswith("include")) From 63e6fb614ad033a9848bb1d64be7ff3885cb8966 Mon Sep 17 00:00:00 2001 From: Mirko Galimberti Date: Mon, 21 Nov 2022 20:45:16 +0100 Subject: [PATCH 17/26] Gradle: Run the clean task before anything else to make sure nothing is "cached". (#2705) * Gradle: Run the clean task before anything else to make sure nothing is cached. * Reference the PR for the future --- pythonforandroid/toolchain.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pythonforandroid/toolchain.py b/pythonforandroid/toolchain.py index daf895e897..85404a2359 100644 --- a/pythonforandroid/toolchain.py +++ b/pythonforandroid/toolchain.py @@ -1108,7 +1108,10 @@ def _build_package(self, args, package_type): else: raise BuildInterruptingException( "Unknown build mode {} for apk()".format(args.build_mode)) - output = shprint(gradlew, gradle_task, _tail=20, + + # WARNING: We should make sure to clean the build directory before building. + # See PR: kivy/python-for-android#2705 + output = shprint(gradlew, "clean", gradle_task, _tail=20, _critical=True, _env=env) return output, build_args From e14bdfc92f8fd369b21b4f3a8dfd601ea1a3f90d Mon Sep 17 00:00:00 2001 From: Mirko Galimberti Date: Mon, 28 Nov 2022 19:59:58 +0100 Subject: [PATCH 18/26] Bump to a version of `SDL` with patches for the TextInput / TextEditing (SDL `2.26.0`) (#2692) * Testing: SDL with patches for the new TextInput / TextEditing * Bump to SDL 2.26.0 --- .../build/src/patches/SDLActivity.java.patch | 26 +++++++++---------- pythonforandroid/recipes/sdl2/__init__.py | 6 +++-- .../recipes/sdl2/remove-extra-include.patch | 12 +++++++++ 3 files changed, 29 insertions(+), 15 deletions(-) create mode 100644 pythonforandroid/recipes/sdl2/remove-extra-include.patch diff --git a/pythonforandroid/bootstraps/sdl2/build/src/patches/SDLActivity.java.patch b/pythonforandroid/bootstraps/sdl2/build/src/patches/SDLActivity.java.patch index c7aa4bf666..d061be8463 100644 --- a/pythonforandroid/bootstraps/sdl2/build/src/patches/SDLActivity.java.patch +++ b/pythonforandroid/bootstraps/sdl2/build/src/patches/SDLActivity.java.patch @@ -1,6 +1,6 @@ --- a/src/main/java/org/libsdl/app/SDLActivity.java +++ b/src/main/java/org/libsdl/app/SDLActivity.java -@@ -225,6 +225,8 @@ +@@ -222,6 +222,8 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh // This is what SDL runs in. It invokes SDL_main(), eventually protected static Thread mSDLThread; @@ -9,7 +9,7 @@ protected static SDLGenericMotionListener_API12 getMotionListener() { if (mMotionListener == null) { if (Build.VERSION.SDK_INT >= 26) { -@@ -323,6 +325,15 @@ +@@ -324,6 +326,15 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh Log.v(TAG, "onCreate()"); super.onCreate(savedInstanceState); @@ -25,7 +25,7 @@ try { Thread.currentThread().setName("SDLActivity"); } catch (Exception e) { -@@ -824,7 +835,7 @@ +@@ -835,7 +846,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh Handler commandHandler = new SDLCommandHandler(); // Send a message from the SDLMain thread @@ -34,11 +34,11 @@ Message msg = commandHandler.obtainMessage(); msg.arg1 = command; msg.obj = data; -@@ -1302,6 +1313,20 @@ +@@ -1384,6 +1395,20 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh return SDLActivity.mSurface.getNativeSurface(); } -+ /** ++ /** + * Calls turnActive() on singleton to keep loading screen active + */ + public static void triggerAppConfirmedActive() { @@ -55,21 +55,21 @@ // Input /** -@@ -1795,7 +1820,7 @@ - } +@@ -1878,6 +1903,7 @@ class SDLMain implements Runnable { Log.v("SDL", "Running main function " + function + " from library " + library); -- + + SDLActivity.mSingleton.appConfirmedActive(); SDLActivity.nativeRunMain(library, function, arguments); Log.v("SDL", "Finished main function"); -@@ -2316,7 +2341,7 @@ +@@ -1935,8 +1961,7 @@ class DummyEdit extends View implements View.OnKeyListener { public InputConnection onCreateInputConnection(EditorInfo outAttrs) { ic = new SDLInputConnection(this, true); -- outAttrs.inputType = InputType.TYPE_CLASS_TEXT; -+ outAttrs.inputType = SDLActivity.keyboardInputType; - outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI - | EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */; +- outAttrs.inputType = InputType.TYPE_CLASS_TEXT | +- InputType.TYPE_TEXT_FLAG_MULTI_LINE; ++ outAttrs.inputType = SDLActivity.keyboardInputType | InputType.TYPE_TEXT_FLAG_MULTI_LINE; + outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI | + EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */; diff --git a/pythonforandroid/recipes/sdl2/__init__.py b/pythonforandroid/recipes/sdl2/__init__.py index d1929d06d8..78e5e1fbbc 100644 --- a/pythonforandroid/recipes/sdl2/__init__.py +++ b/pythonforandroid/recipes/sdl2/__init__.py @@ -6,12 +6,14 @@ class LibSDL2Recipe(BootstrapNDKRecipe): - version = "2.24.0" + version = "2.26.0" url = "https://github.com/libsdl-org/SDL/releases/download/release-{version}/SDL2-{version}.tar.gz" - md5sum = 'cf539ffe9e0dd6f943ac9de75fd2e56e' + md5sum = '35bc58cfe41b8fb6c8e6646be26fa47e' dir_name = 'SDL' + patches = ['remove-extra-include.patch'] + depends = ['sdl2_image', 'sdl2_mixer', 'sdl2_ttf'] def get_recipe_env(self, arch=None, with_flags_in_cc=True, with_python=True): diff --git a/pythonforandroid/recipes/sdl2/remove-extra-include.patch b/pythonforandroid/recipes/sdl2/remove-extra-include.patch new file mode 100644 index 0000000000..f0fda0851e --- /dev/null +++ b/pythonforandroid/recipes/sdl2/remove-extra-include.patch @@ -0,0 +1,12 @@ +diff -Naur SDL.orig/Android.mk SDL/Android.mk +--- SDL.orig/Android.mk 2022-11-22 07:41:32 ++++ SDL/Android.mk 2022-11-22 07:42:00 +@@ -12,7 +12,7 @@ + + LOCAL_C_INCLUDES := $(LOCAL_PATH)/include + +-LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)/include ++LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES) + + LOCAL_SRC_FILES := \ + $(subst $(LOCAL_PATH)/,, \ From 8f8cf6a4c89d0f8a53b700c6b5d299d0080ca4ec Mon Sep 17 00:00:00 2001 From: Mirko Galimberti Date: Tue, 29 Nov 2022 17:39:38 +0100 Subject: [PATCH 19/26] Flake8 does not support inline comments for any of the keys. (#2708) * Flake8 does not support inline comments for any of the keys. * Fix flake8 E741 ambiguous variable name --- pythonforandroid/recipe.py | 2 +- tox.ini | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index 130db7a559..67c309e197 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -631,7 +631,7 @@ def install_libs(self, arch, *libs): shprint(sh.cp, *args) def has_libs(self, arch, *libs): - return all(map(lambda l: self.ctx.has_lib(arch.arch, l), libs)) + return all(map(lambda lib: self.ctx.has_lib(arch.arch, lib), libs)) def get_libraries(self, arch_name, in_context=False): """Return the full path of the library depending on the architecture. diff --git a/tox.ini b/tox.ini index ceafe446b5..9b0432c82b 100644 --- a/tox.ini +++ b/tox.ini @@ -27,11 +27,19 @@ commands = flake8 pythonforandroid/ tests/ ci/ setup.py [flake8] ignore = - E123, # Closing bracket does not match indentation of opening bracket's line - E124, # Closing bracket does not match visual indentation - E126, # Continuation line over-indented for hanging indent - E226, # Missing whitespace around arithmetic operator - E402, # Module level import not at top of file - E501, # Line too long (82 > 79 characters) - W503, # Line break occurred before a binary operator - W504 # Line break occurred after a binary operator + # Closing bracket does not match indentation of opening bracket's line + E123, + # Closing bracket does not match visual indentation + E124, + # Continuation line over-indented for hanging indent + E126, + # Missing whitespace around arithmetic operator + E226, + # Module level import not at top of file + E402, + # Line too long (82 > 79 characters) + E501, + # Line break occurred before a binary operator + W503, + # Line break occurred after a binary operator + W504 From 10fa82b51ed0575c9c8393d90066655675d49a16 Mon Sep 17 00:00:00 2001 From: Mirko Galimberti Date: Thu, 8 Dec 2022 16:57:39 +0100 Subject: [PATCH 20/26] Bump sdl2 version to 2.26.1 (#2712) --- pythonforandroid/recipes/sdl2/__init__.py | 6 ++---- .../recipes/sdl2/remove-extra-include.patch | 12 ------------ 2 files changed, 2 insertions(+), 16 deletions(-) delete mode 100644 pythonforandroid/recipes/sdl2/remove-extra-include.patch diff --git a/pythonforandroid/recipes/sdl2/__init__.py b/pythonforandroid/recipes/sdl2/__init__.py index 78e5e1fbbc..f6f3d391af 100644 --- a/pythonforandroid/recipes/sdl2/__init__.py +++ b/pythonforandroid/recipes/sdl2/__init__.py @@ -6,14 +6,12 @@ class LibSDL2Recipe(BootstrapNDKRecipe): - version = "2.26.0" + version = "2.26.1" url = "https://github.com/libsdl-org/SDL/releases/download/release-{version}/SDL2-{version}.tar.gz" - md5sum = '35bc58cfe41b8fb6c8e6646be26fa47e' + md5sum = 'fba211fe2c67609df6fa3cf55d3c74dc' dir_name = 'SDL' - patches = ['remove-extra-include.patch'] - depends = ['sdl2_image', 'sdl2_mixer', 'sdl2_ttf'] def get_recipe_env(self, arch=None, with_flags_in_cc=True, with_python=True): diff --git a/pythonforandroid/recipes/sdl2/remove-extra-include.patch b/pythonforandroid/recipes/sdl2/remove-extra-include.patch deleted file mode 100644 index f0fda0851e..0000000000 --- a/pythonforandroid/recipes/sdl2/remove-extra-include.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -Naur SDL.orig/Android.mk SDL/Android.mk ---- SDL.orig/Android.mk 2022-11-22 07:41:32 -+++ SDL/Android.mk 2022-11-22 07:42:00 -@@ -12,7 +12,7 @@ - - LOCAL_C_INCLUDES := $(LOCAL_PATH)/include - --LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)/include -+LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES) - - LOCAL_SRC_FILES := \ - $(subst $(LOCAL_PATH)/,, \ From 8cb497dd89e402478011df61f4690b963a0c96da Mon Sep 17 00:00:00 2001 From: RobertF <34464649+RobertFlatt@users.noreply.github.com> Date: Fri, 9 Dec 2022 07:36:25 -1000 Subject: [PATCH 21/26] Custom Service notification (#2703) * add_resources * add_resource * Update build.py * stateless * multiple kinds * pep8 * --add_resource * Custom notification * Update Service.tmpl.java * Custom Notification * Custom Notification * service notification * customize notification * share code * share code * Update Service.tmpl.java --- doc/source/services.rst | 8 +++++ .../java/org/kivy/android/PythonService.java | 35 ++++++++++++++----- .../common/build/templates/Service.tmpl.java | 28 ++++++++++++--- .../build/templates/Service.tmpl.java | 34 ++++++++++++++---- .../build/templates/Service.tmpl.java | 28 +++++++++++++-- 5 files changed, 110 insertions(+), 23 deletions(-) diff --git a/doc/source/services.rst b/doc/source/services.rst index b5519a5cb7..8b5d9a7851 100644 --- a/doc/source/services.rst +++ b/doc/source/services.rst @@ -107,6 +107,14 @@ the json module to encode and decode more complex data. from os import environ argument = environ.get('PYTHON_SERVICE_ARGUMENT', '') + +To customize the notification icon, title, and text use three optional +arguments to service.start():: + + service.start(mActivity, 'small_icon', 'title', 'content' , argument) + +Where 'small_icon' is the name of an Android drawable or mipmap resource, +and 'title' and 'content' are strings in the notification. Services support a range of options and interactions not yet documented here but all accessible via calling other methods of the diff --git a/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonService.java b/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonService.java index dd6f307ec7..cb89e3bdcd 100644 --- a/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonService.java +++ b/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonService.java @@ -102,30 +102,47 @@ protected Intent getThisDefaultIntent(Context ctx, String pythonServiceArgument) protected void doStartForeground(Bundle extras) { String serviceTitle = extras.getString("serviceTitle"); - String serviceDescription = extras.getString("serviceDescription"); + String smallIconName = extras.getString("smallIconName"); + String contentTitle = extras.getString("contentTitle"); + String contentText = extras.getString("contentText"); Notification notification; Context context = getApplicationContext(); Intent contextIntent = new Intent(context, PythonActivity.class); PendingIntent pIntent = PendingIntent.getActivity(context, 0, contextIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + // Unspecified icon uses default. + int smallIconId = context.getApplicationInfo().icon; + if (!smallIconName.equals("")){ + int resId = getResources().getIdentifier(smallIconName, "mipmap", + getPackageName()); + if (resId ==0) { + resId = getResources().getIdentifier(smallIconName, "drawable", + getPackageName()); + } + if (resId !=0) { + smallIconId = resId; + } + } + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + // This constructor is deprecated notification = new Notification( - context.getApplicationInfo().icon, serviceTitle, System.currentTimeMillis()); + smallIconId, serviceTitle, System.currentTimeMillis()); try { // prevent using NotificationCompat, this saves 100kb on apk Method func = notification.getClass().getMethod( "setLatestEventInfo", Context.class, CharSequence.class, CharSequence.class, PendingIntent.class); - func.invoke(notification, context, serviceTitle, serviceDescription, pIntent); + func.invoke(notification, context, contentTitle, contentText, pIntent); } catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { } } else { // for android 8+ we need to create our own channel // https://stackoverflow.com/questions/47531742/startforeground-fail-after-upgrade-to-android-8-1 - String NOTIFICATION_CHANNEL_ID = "org.kivy.p4a"; //TODO: make this configurable - String channelName = "Background Service"; //TODO: make this configurable + String NOTIFICATION_CHANNEL_ID = "org.kivy.p4a" + getServiceId(); + String channelName = "Background Service" + getServiceId(); NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE); @@ -135,10 +152,10 @@ protected void doStartForeground(Bundle extras) { manager.createNotificationChannel(chan); Notification.Builder builder = new Notification.Builder(context, NOTIFICATION_CHANNEL_ID); - builder.setContentTitle(serviceTitle); - builder.setContentText(serviceDescription); + builder.setContentTitle(contentTitle); + builder.setContentText(contentText); builder.setContentIntent(pIntent); - builder.setSmallIcon(context.getApplicationInfo().icon); + builder.setSmallIcon(smallIconId); notification = builder.build(); } startForeground(getServiceId(), notification); diff --git a/pythonforandroid/bootstraps/common/build/templates/Service.tmpl.java b/pythonforandroid/bootstraps/common/build/templates/Service.tmpl.java index de84ac42bf..9406f91d89 100644 --- a/pythonforandroid/bootstraps/common/build/templates/Service.tmpl.java +++ b/pythonforandroid/bootstraps/common/build/templates/Service.tmpl.java @@ -17,31 +17,49 @@ public int startType() { protected int getServiceId() { return {{ service_id }}; } + + static private void _start(Context ctx, String smallIconName, + String contentTitle, String contentText, + String pythonServiceArgument) { + Intent intent = getDefaultIntent(ctx, smallIconName, contentTitle, + contentText, pythonServiceArgument); + ctx.startService(intent); + } static public void start(Context ctx, String pythonServiceArgument) { - Intent intent = getDefaultIntent(ctx, pythonServiceArgument); - ctx.startService(intent); + _start(ctx, "", "{{ args.name }}", "{{ name|capitalize }}", pythonServiceArgument); } - static public Intent getDefaultIntent(Context ctx, String pythonServiceArgument) { + static public void start(Context ctx, String smallIconName, + String contentTitle, String contentText, + String pythonServiceArgument) { + _start(ctx, smallIconName, contentTitle, contentText, pythonServiceArgument); + } + + static public Intent getDefaultIntent(Context ctx, String smallIconName, + String contentTitle, String contentText, + String pythonServiceArgument) { Intent intent = new Intent(ctx, Service{{ name|capitalize }}.class); String argument = ctx.getFilesDir().getAbsolutePath() + "/app"; intent.putExtra("androidPrivate", ctx.getFilesDir().getAbsolutePath()); intent.putExtra("androidArgument", argument); intent.putExtra("serviceTitle", "{{ args.name }}"); - intent.putExtra("serviceDescription", "{{ name|capitalize }}"); intent.putExtra("serviceEntrypoint", "{{ entrypoint }}"); intent.putExtra("pythonName", "{{ name }}"); intent.putExtra("serviceStartAsForeground", "{{ foreground|lower }}"); intent.putExtra("pythonHome", argument); intent.putExtra("pythonPath", argument + ":" + argument + "/lib"); intent.putExtra("pythonServiceArgument", pythonServiceArgument); + intent.putExtra("smallIconName", smallIconName); + intent.putExtra("contentTitle", contentTitle); + intent.putExtra("contentText", contentText); return intent; } @Override protected Intent getThisDefaultIntent(Context ctx, String pythonServiceArgument) { - return Service{{ name|capitalize }}.getDefaultIntent(ctx, pythonServiceArgument); + return Service{{ name|capitalize }}.getDefaultIntent(ctx, "", "", "", + pythonServiceArgument); } static public void stop(Context ctx) { diff --git a/pythonforandroid/bootstraps/service_library/build/templates/Service.tmpl.java b/pythonforandroid/bootstraps/service_library/build/templates/Service.tmpl.java index f1eaf0702d..ff889b462c 100644 --- a/pythonforandroid/bootstraps/service_library/build/templates/Service.tmpl.java +++ b/pythonforandroid/bootstraps/service_library/build/templates/Service.tmpl.java @@ -34,10 +34,13 @@ public static void prepare(Context ctx) { PythonUtil.unpackAsset(ctx, "private", app_root_file, true); PythonUtil.unpackPyBundle(ctx, ctx.getApplicationInfo().nativeLibraryDir + "/" + "libpybundle", app_root_file, false); } - - public static void start(Context ctx, String pythonServiceArgument) { - Intent intent = getDefaultIntent(ctx, pythonServiceArgument); - + + static private void _start(Context ctx, String smallIconName, + String contentTitle, + String contentText, + String pythonServiceArgument) { + Intent intent = getDefaultIntent(ctx, smallIconName, contentTitle, + contentText, pythonServiceArgument); //foreground: {{foreground}} {% if foreground %} if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -50,26 +53,43 @@ public static void start(Context ctx, String pythonServiceArgument) { {% endif %} } - static public Intent getDefaultIntent(Context ctx, String pythonServiceArgument) { + public static void start(Context ctx, String pythonServiceArgument) { + _start(ctx, "", "{{ args.name }}", "{{ name|capitalize }}", pythonServiceArgument); + } + + static public void start(Context ctx, String smallIconName, + String contentTitle, + String contentText, + String pythonServiceArgument) { + _start(ctx, smallIconName, contentTitle, contentText, pythonServiceArgument); + } + + static public Intent getDefaultIntent(Context ctx, String smallIconName, + String contentTitle, + String contentText, + String pythonServiceArgument) { String appRoot = PythonUtil.getAppRoot(ctx); Intent intent = new Intent(ctx, Service{{ name|capitalize }}.class); intent.putExtra("androidPrivate", appRoot); intent.putExtra("androidArgument", appRoot); intent.putExtra("serviceEntrypoint", "{{ entrypoint }}"); intent.putExtra("serviceTitle", "{{ name|capitalize }}"); - intent.putExtra("serviceDescription", ""); intent.putExtra("pythonName", "{{ name }}"); intent.putExtra("serviceStartAsForeground", "{{ foreground|lower }}"); intent.putExtra("pythonHome", appRoot); intent.putExtra("androidUnpack", appRoot); intent.putExtra("pythonPath", appRoot + ":" + appRoot + "/lib"); intent.putExtra("pythonServiceArgument", pythonServiceArgument); + intent.putExtra("smallIconName", smallIconName); + intent.putExtra("contentTitle", contentTitle); + intent.putExtra("contentText", contentText); return intent; } @Override protected Intent getThisDefaultIntent(Context ctx, String pythonServiceArgument) { - return Service{{ name|capitalize }}.getDefaultIntent(ctx, pythonServiceArgument); + return Service{{ name|capitalize }}.getDefaultIntent(ctx, "", "", "", + pythonServiceArgument); } diff --git a/pythonforandroid/bootstraps/service_only/build/templates/Service.tmpl.java b/pythonforandroid/bootstraps/service_only/build/templates/Service.tmpl.java index 598549d345..eeda810bef 100644 --- a/pythonforandroid/bootstraps/service_only/build/templates/Service.tmpl.java +++ b/pythonforandroid/bootstraps/service_only/build/templates/Service.tmpl.java @@ -34,16 +34,40 @@ public static void start(Context ctx, String pythonServiceArgument) { intent.putExtra("androidArgument", argument); intent.putExtra("serviceEntrypoint", "{{ entrypoint }}"); intent.putExtra("serviceTitle", "{{ name|capitalize }}"); - intent.putExtra("serviceDescription", ""); intent.putExtra("pythonName", "{{ name }}"); intent.putExtra("serviceStartAsForeground", "{{ foreground|lower }}"); intent.putExtra("pythonHome", argument); intent.putExtra("androidUnpack", argument); intent.putExtra("pythonPath", argument + ":" + argument + "/lib"); intent.putExtra("pythonServiceArgument", pythonServiceArgument); + intent.putExtra("smallIconName", ""); + intent.putExtra("contentTitle", "{{ name|capitalize }}"); + intent.putExtra("contentText", ""); ctx.startService(intent); } - + + public static void start(Context ctx, String smallIconName, + String contentTitle, + String contentText, + String pythonServiceArgument) { + String argument = ctx.getFilesDir().getAbsolutePath() + "/app"; + Intent intent = new Intent(ctx, Service{{ name|capitalize }}.class); + intent.putExtra("androidPrivate", argument); + intent.putExtra("androidArgument", argument); + intent.putExtra("serviceEntrypoint", "{{ entrypoint }}"); + intent.putExtra("serviceTitle", "{{ name|capitalize }}"); + intent.putExtra("pythonName", "{{ name }}"); + intent.putExtra("serviceStartAsForeground", "{{ foreground|lower }}"); + intent.putExtra("pythonHome", argument); + intent.putExtra("androidUnpack", argument); + intent.putExtra("pythonPath", argument + ":" + argument + "/lib"); + intent.putExtra("pythonServiceArgument", pythonServiceArgument); + intent.putExtra("smallIconName", smallIconName); + intent.putExtra("contentTitle", contentTitle); + intent.putExtra("contentText", contentText); + ctx.startService(intent); + } + public static void stop(Context ctx) { Intent intent = new Intent(ctx, Service{{ name|capitalize }}.class); ctx.stopService(intent); From 2a2677895a9830380e548d3d0905037c42712969 Mon Sep 17 00:00:00 2001 From: RobertF <34464649+RobertFlatt@users.noreply.github.com> Date: Fri, 9 Dec 2022 15:22:07 -1000 Subject: [PATCH 22/26] Update "--host=" --- pythonforandroid/recipes/secp256k1/cross_compile.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/recipes/secp256k1/cross_compile.patch b/pythonforandroid/recipes/secp256k1/cross_compile.patch index bfef228193..bcff1955fb 100644 --- a/pythonforandroid/recipes/secp256k1/cross_compile.patch +++ b/pythonforandroid/recipes/secp256k1/cross_compile.patch @@ -6,7 +6,7 @@ index bba4bce..b86b369 100644 "--disable-dependency-tracking", "--with-pic", "--enable-module-recovery", -+ "--host=%s" % os.environ['TOOLCHAIN_PREFIX'], ++ "--host=" + arch.command_prefix, "--prefix", os.path.abspath(self.build_clib), ] From cd1c6550cfae754e109b424cd05230ae3332f5e5 Mon Sep 17 00:00:00 2001 From: RobertF <34464649+RobertFlatt@users.noreply.github.com> Date: Fri, 9 Dec 2022 15:31:04 -1000 Subject: [PATCH 23/26] Delete pythonforandroid/recipes/cdecimal directory Depreciated since Python 3.3 --- pythonforandroid/recipes/cdecimal/__init__.py | 25 --- .../recipes/cdecimal/cross-compile.patch | 12 -- .../recipes/cdecimal/locale.patch | 172 ------------------ 3 files changed, 209 deletions(-) delete mode 100644 pythonforandroid/recipes/cdecimal/__init__.py delete mode 100644 pythonforandroid/recipes/cdecimal/cross-compile.patch delete mode 100644 pythonforandroid/recipes/cdecimal/locale.patch diff --git a/pythonforandroid/recipes/cdecimal/__init__.py b/pythonforandroid/recipes/cdecimal/__init__.py deleted file mode 100644 index a444eb1b25..0000000000 --- a/pythonforandroid/recipes/cdecimal/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -from pythonforandroid.recipe import CompiledComponentsPythonRecipe -from pythonforandroid.patching import is_darwin - - -class CdecimalRecipe(CompiledComponentsPythonRecipe): - name = 'cdecimal' - version = '2.3' - url = 'http://www.bytereef.org/software/mpdecimal/releases/cdecimal-{version}.tar.gz' - - depends = [] - - patches = ['locale.patch', - 'cross-compile.patch'] - - def prebuild_arch(self, arch): - super().prebuild_arch(arch) - if not is_darwin(): - if '64' in arch.arch: - machine = 'ansi64' - else: - machine = 'ansi32' - self.setup_extra_args = ['--with-machine=' + machine] - - -recipe = CdecimalRecipe() diff --git a/pythonforandroid/recipes/cdecimal/cross-compile.patch b/pythonforandroid/recipes/cdecimal/cross-compile.patch deleted file mode 100644 index cc15f33ba2..0000000000 --- a/pythonforandroid/recipes/cdecimal/cross-compile.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -Naur cdecimal/setup.py b/setup.py ---- cdecimal/setup.py 2015-12-14 13:48:23.085997956 -0600 -+++ b/setup.py 2015-12-14 13:48:11.413805121 -0600 -@@ -229,7 +229,7 @@ - def configure(machine, cc, py_size_t): - os.chmod("./configure", 0x1ed) # pip removes execute permissions. - if machine: # string has been validated. -- os.system("./configure MACHINE=%s" % machine) -+ os.system("./configure --host=%s MACHINE=%s" % (os.environ['TOOLCHAIN_PREFIX'], machine)) - elif 'sunos' in SYSTEM and py_size_t == 8: - # cc is from sysconfig. - os.system("./configure CC='%s -m64'" % cc) diff --git a/pythonforandroid/recipes/cdecimal/locale.patch b/pythonforandroid/recipes/cdecimal/locale.patch deleted file mode 100644 index 4b8df6b373..0000000000 --- a/pythonforandroid/recipes/cdecimal/locale.patch +++ /dev/null @@ -1,172 +0,0 @@ -diff -Naur a/io.c b/io.c ---- a/io.c 2012-02-01 14:29:49.000000000 -0600 -+++ b/io.c 2015-12-09 17:04:00.060579230 -0600 -@@ -34,7 +34,7 @@ - #include - #include - #include --#include -+#include "locale.h" - #include "bits.h" - #include "constants.h" - #include "memory.h" -@@ -792,15 +792,14 @@ - } - else if (*cp == 'N' || *cp == 'n') { - /* locale specific conversion */ -- struct lconv *lc; - spec->type = *cp++; - /* separator has already been specified */ - if (*spec->sep) return 0; - spec->type = (spec->type == 'N') ? 'G' : 'g'; -- lc = localeconv(); -- spec->dot = lc->decimal_point; -- spec->sep = lc->thousands_sep; -- spec->grouping = lc->grouping; -+ /* TODO: Android does not have localeconv(); we'll just use C locale values for now */ -+ spec->dot = "."; -+ spec->sep = ""; -+ spec->grouping = ""; - } - - /* check correctness */ -diff -Naur a/locale.h b/locale.h ---- a/locale.h 1969-12-31 18:00:00.000000000 -0600 -+++ b/locale.h 2015-12-09 17:04:11.128762784 -0600 -@@ -0,0 +1,136 @@ -+/* -+ * Copyright (C) 2008 The Android Open Source Project -+ * All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * * Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * * Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in -+ * the documentation and/or other materials provided with the -+ * distribution. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS -+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED -+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -+ * SUCH DAMAGE. -+ */ -+#ifndef _LOCALE_H_ -+#define _LOCALE_H_ -+ -+#include -+ -+__BEGIN_DECLS -+ -+enum { -+ LC_CTYPE = 0, -+ LC_NUMERIC = 1, -+ LC_TIME = 2, -+ LC_COLLATE = 3, -+ LC_MONETARY = 4, -+ LC_MESSAGES = 5, -+ LC_ALL = 6, -+ LC_PAPER = 7, -+ LC_NAME = 8, -+ LC_ADDRESS = 9, -+ -+ LC_TELEPHONE = 10, -+ LC_MEASUREMENT = 11, -+ LC_IDENTIFICATION = 12 -+}; -+ -+extern char *setlocale(int category, const char *locale); -+ -+#if 1 /* MISSING FROM BIONIC - DEFINED TO MAKE libstdc++-v3 happy */ -+/*struct lconv { };*/ -+ -+__BEGIN_NAMESPACE_STD; -+ -+/* Structure giving information about numeric and monetary notation. */ -+struct lconv -+{ -+ /* Numeric (non-monetary) information. */ -+ -+ char *decimal_point; /* Decimal point character. */ -+ char *thousands_sep; /* Thousands separator. */ -+ /* Each element is the number of digits in each group; -+ elements with higher indices are farther left. -+ An element with value CHAR_MAX means that no further grouping is done. -+ An element with value 0 means that the previous element is used -+ for all groups farther left. */ -+ char *grouping; -+ -+ /* Monetary information. */ -+ -+ /* First three chars are a currency symbol from ISO 4217. -+ Fourth char is the separator. Fifth char is '\0'. */ -+ char *int_curr_symbol; -+ char *currency_symbol; /* Local currency symbol. */ -+ char *mon_decimal_point; /* Decimal point character. */ -+ char *mon_thousands_sep; /* Thousands separator. */ -+ char *mon_grouping; /* Like `grouping' element (above). */ -+ char *positive_sign; /* Sign for positive values. */ -+ char *negative_sign; /* Sign for negative values. */ -+ char int_frac_digits; /* Int'l fractional digits. */ -+ char frac_digits; /* Local fractional digits. */ -+ /* 1 if currency_symbol precedes a positive value, 0 if succeeds. */ -+ char p_cs_precedes; -+ /* 1 iff a space separates currency_symbol from a positive value. */ -+ char p_sep_by_space; -+ /* 1 if currency_symbol precedes a negative value, 0 if succeeds. */ -+ char n_cs_precedes; -+ /* 1 iff a space separates currency_symbol from a negative value. */ -+ char n_sep_by_space; -+ /* Positive and negative sign positions: -+ 0 Parentheses surround the quantity and currency_symbol. -+ 1 The sign string precedes the quantity and currency_symbol. -+ 2 The sign string follows the quantity and currency_symbol. -+ 3 The sign string immediately precedes the currency_symbol. -+ 4 The sign string immediately follows the currency_symbol. */ -+ char p_sign_posn; -+ char n_sign_posn; -+#ifdef __USE_ISOC99 -+ /* 1 if int_curr_symbol precedes a positive value, 0 if succeeds. */ -+ char int_p_cs_precedes; -+ /* 1 iff a space separates int_curr_symbol from a positive value. */ -+ char int_p_sep_by_space; -+ /* 1 if int_curr_symbol precedes a negative value, 0 if succeeds. */ -+ char int_n_cs_precedes; -+ /* 1 iff a space separates int_curr_symbol from a negative value. */ -+ char int_n_sep_by_space; -+ /* Positive and negative sign positions: -+ 0 Parentheses surround the quantity and int_curr_symbol. -+ 1 The sign string precedes the quantity and int_curr_symbol. -+ 2 The sign string follows the quantity and int_curr_symbol. -+ 3 The sign string immediately precedes the int_curr_symbol. -+ 4 The sign string immediately follows the int_curr_symbol. */ -+ char int_p_sign_posn; -+ char int_n_sign_posn; -+#else -+ char __int_p_cs_precedes; -+ char __int_p_sep_by_space; -+ char __int_n_cs_precedes; -+ char __int_n_sep_by_space; -+ char __int_p_sign_posn; -+ char __int_n_sign_posn; -+#endif -+}; -+ -+__END_NAMESPACE_STD; -+ -+struct lconv *localeconv(void); -+#endif /* MISSING */ -+ -+__END_DECLS -+ -+#endif /* _LOCALE_H_ */ From 26d5155bb7156032dcc55f97d12c6ce523f9adae Mon Sep 17 00:00:00 2001 From: Mirko Galimberti Date: Mon, 12 Dec 2022 18:41:59 +0100 Subject: [PATCH 24/26] InputType.TYPE_TEXT_FLAG_MULTI_LINE forces InputType.TYPE_TEXT even if SDLActivity.keyboardInputType is NULL (#2716) --- .../bootstraps/sdl2/build/src/patches/SDLActivity.java.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/bootstraps/sdl2/build/src/patches/SDLActivity.java.patch b/pythonforandroid/bootstraps/sdl2/build/src/patches/SDLActivity.java.patch index d061be8463..71d2537e7c 100644 --- a/pythonforandroid/bootstraps/sdl2/build/src/patches/SDLActivity.java.patch +++ b/pythonforandroid/bootstraps/sdl2/build/src/patches/SDLActivity.java.patch @@ -69,7 +69,7 @@ - outAttrs.inputType = InputType.TYPE_CLASS_TEXT | - InputType.TYPE_TEXT_FLAG_MULTI_LINE; -+ outAttrs.inputType = SDLActivity.keyboardInputType | InputType.TYPE_TEXT_FLAG_MULTI_LINE; ++ outAttrs.inputType = SDLActivity.keyboardInputType; outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */; From 5b4e893adf1dc1c5a3115010ba84c74b013ee78e Mon Sep 17 00:00:00 2001 From: Mirko Galimberti Date: Tue, 20 Dec 2022 21:00:49 +0100 Subject: [PATCH 25/26] Update CHANGELOG.md --- CHANGELOG.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2393dc204e..dda944b49b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,59 @@ # Changelog +## [v2022.12.20](https://github.com/kivy/python-for-android/tree/v2022.12.20) (2022-12-20) + +[Full Changelog](https://github.com/kivy/python-for-android/compare/v2022.09.04...v2022.12.20) + +**Fixed bugs:** + +- `liblzma` fails to build on macOS \(sh.ErrorReturnCode\_2. when running buildozer android debug\) [\#2343](https://github.com/kivy/python-for-android/issues/2343) + +**Closed issues:** + +- SDL\_ttf 2.0.15 download missing \(deprecated\) related to \#2698 [\#2710](https://github.com/kivy/python-for-android/issues/2710) +- Update kivy app that's already published on google play store. [\#2709](https://github.com/kivy/python-for-android/issues/2709) +- Installing deepspeech [\#2702](https://github.com/kivy/python-for-android/issues/2702) +- ImportError: dlopen failed: library "libc++\_shared.so" not found [\#2699](https://github.com/kivy/python-for-android/issues/2699) +- ffpyplayer recipe broken after SDL2 upgrade [\#2698](https://github.com/kivy/python-for-android/issues/2698) +- ModuleNotFoundError: No module named 'android' [\#2697](https://github.com/kivy/python-for-android/issues/2697) +- Error When I set android.api = 31 [\#2696](https://github.com/kivy/python-for-android/issues/2696) +- Is threre any way to protect .py code [\#2695](https://github.com/kivy/python-for-android/issues/2695) +- args.service\_class\_name results in link error [\#2679](https://github.com/kivy/python-for-android/issues/2679) +- Remove `x86_64` suffix from ndk download link [\#2676](https://github.com/kivy/python-for-android/issues/2676) +- Pillow 9.2.0 recipe? [\#2671](https://github.com/kivy/python-for-android/issues/2671) +- `ffmpeg`: unable to find library -lvpx [\#2665](https://github.com/kivy/python-for-android/issues/2665) +- Buildozer fails while build numpy recipie 'UnixCCompiler' object has no attribute 'cxx\_compiler' [\#2664](https://github.com/kivy/python-for-android/issues/2664) +- \[PEP 517\] Relax installation-time "pep517\<0.7.0" requirement [\#2573](https://github.com/kivy/python-for-android/issues/2573) +- Add support for custom resources [\#2298](https://github.com/kivy/python-for-android/issues/2298) +- Auto-correct / word suggestion does not work with Swiftkey/Samsung keyboard [\#2010](https://github.com/kivy/python-for-android/issues/2010) + +**Merged pull requests:** + +- `InputType.TYPE_TEXT_FLAG_MULTI_LINE` forces `InputType.TYPE_TEXT` even if `SDLActivity.keyboardInputType` is `NULL` [\#2716](https://github.com/kivy/python-for-android/pull/2716) ([misl6](https://github.com/misl6)) +- secp256k1 Update "--host=" [\#2714](https://github.com/kivy/python-for-android/pull/2714) ([RobertFlatt](https://github.com/RobertFlatt)) +- Delete pythonforandroid/recipes/cdecimal directory [\#2713](https://github.com/kivy/python-for-android/pull/2713) ([RobertFlatt](https://github.com/RobertFlatt)) +- Bump `sdl2` version to `2.26.1` [\#2712](https://github.com/kivy/python-for-android/pull/2712) ([misl6](https://github.com/misl6)) +- Flake8 does not support inline comments for any of the keys. [\#2708](https://github.com/kivy/python-for-android/pull/2708) ([misl6](https://github.com/misl6)) +- Gradle: Run the clean task before anything else to make sure nothing is cached. [\#2705](https://github.com/kivy/python-for-android/pull/2705) ([misl6](https://github.com/misl6)) +- Custom Service notification [\#2703](https://github.com/kivy/python-for-android/pull/2703) ([RobertFlatt](https://github.com/RobertFlatt)) +- Include paths for sdl2\_mixer have changed. Added a method to return the right one. [\#2700](https://github.com/kivy/python-for-android/pull/2700) ([misl6](https://github.com/misl6)) +- WRITE\_EXTERNAL\_STORAGE maxSdk [\#2694](https://github.com/kivy/python-for-android/pull/2694) ([RobertFlatt](https://github.com/RobertFlatt)) +- Fixes an issue regarding blacklist and bytecode compile + some cleanup [\#2693](https://github.com/kivy/python-for-android/pull/2693) ([misl6](https://github.com/misl6)) +- Bump to a version of `SDL` with patches for the TextInput / TextEditing \(SDL `2.26.0`\) [\#2692](https://github.com/kivy/python-for-android/pull/2692) ([misl6](https://github.com/misl6)) +- Make CI compile aiohttp again. [\#2690](https://github.com/kivy/python-for-android/pull/2690) ([xavierfiechter](https://github.com/xavierfiechter)) +- Add resources [\#2684](https://github.com/kivy/python-for-android/pull/2684) ([RobertFlatt](https://github.com/RobertFlatt)) +- Update `MIN_TARGET_API` to `30` and `RECOMMENDED_TARGET_API` to `33` [\#2683](https://github.com/kivy/python-for-android/pull/2683) ([misl6](https://github.com/misl6)) +- recipe.download\_file: implement shallow git cloning [\#2682](https://github.com/kivy/python-for-android/pull/2682) ([SomberNight](https://github.com/SomberNight)) +- requirements: relax version bound on "pep517" [\#2680](https://github.com/kivy/python-for-android/pull/2680) ([SomberNight](https://github.com/SomberNight)) +- Add new Android permissions [\#2677](https://github.com/kivy/python-for-android/pull/2677) ([RobertFlatt](https://github.com/RobertFlatt)) +- Resize webview when keyboard is shown [\#2674](https://github.com/kivy/python-for-android/pull/2674) ([dbnicholson](https://github.com/dbnicholson)) +- Update `SDL2`, `SDL2_ttf`, `SDL2_mixer`, `SDL2_image` to latest releases [\#2673](https://github.com/kivy/python-for-android/pull/2673) ([misl6](https://github.com/misl6)) +- Fixes libvpx build [\#2672](https://github.com/kivy/python-for-android/pull/2672) ([misl6](https://github.com/misl6)) +- `toml` may not be available on systemwide python [\#2670](https://github.com/kivy/python-for-android/pull/2670) ([misl6](https://github.com/misl6)) +- android/activity: Add Application.ActivityLifecycleCallbacks helpers [\#2669](https://github.com/kivy/python-for-android/pull/2669) ([dbnicholson](https://github.com/dbnicholson)) +- Bump minimal and recommended Android NDK version to 25b [\#2668](https://github.com/kivy/python-for-android/pull/2668) ([misl6](https://github.com/misl6)) +- Include HOME in build environment [\#2582](https://github.com/kivy/python-for-android/pull/2582) ([dbnicholson](https://github.com/dbnicholson)) + ## [v2022.09.04](https://github.com/kivy/python-for-android/tree/v2022.09.04) (2022-09-04) [Full Changelog](https://github.com/kivy/python-for-android/compare/v2022.07.20...v2022.09.04) From 697929b93da2899426c33a4a4b61bc3300a997f4 Mon Sep 17 00:00:00 2001 From: Mirko Galimberti Date: Tue, 20 Dec 2022 21:01:45 +0100 Subject: [PATCH 26/26] Bump version to 2022.12.20 --- pythonforandroid/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythonforandroid/__init__.py b/pythonforandroid/__init__.py index f39a847cb5..8364e6416d 100644 --- a/pythonforandroid/__init__.py +++ b/pythonforandroid/__init__.py @@ -1 +1 @@ -__version__ = '2022.09.04' +__version__ = '2022.12.20'