kivyforge 3.0.0.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. kivyforge/__init__.py +9 -0
  2. kivyforge/artifacts/__init__.py +41 -0
  3. kivyforge/artifacts/cache.py +81 -0
  4. kivyforge/artifacts/collect.py +354 -0
  5. kivyforge/artifacts/download.py +125 -0
  6. kivyforge/artifacts/frameworks.py +240 -0
  7. kivyforge/artifacts/native_stage_util.py +346 -0
  8. kivyforge/artifacts/runtime.py +65 -0
  9. kivyforge/artifacts/verify.py +45 -0
  10. kivyforge/artifacts/wheels.py +89 -0
  11. kivyforge/bundle/__init__.py +1 -0
  12. kivyforge/bundle/pycompile.py +161 -0
  13. kivyforge/cli/__init__.py +52 -0
  14. kivyforge/cli/_common.py +62 -0
  15. kivyforge/cli/_platform.py +84 -0
  16. kivyforge/cli/build.py +111 -0
  17. kivyforge/cli/clean.py +135 -0
  18. kivyforge/cli/doctor.py +52 -0
  19. kivyforge/cli/init.py +612 -0
  20. kivyforge/cli/init_writer.py +716 -0
  21. kivyforge/cli/lock.py +261 -0
  22. kivyforge/cli/open_cmd.py +15 -0
  23. kivyforge/cli/package.py +134 -0
  24. kivyforge/cli/run.py +162 -0
  25. kivyforge/cli/status.py +20 -0
  26. kivyforge/cli/upgrade.py +314 -0
  27. kivyforge/config/__init__.py +46 -0
  28. kivyforge/config/errors.py +43 -0
  29. kivyforge/config/icons.py +60 -0
  30. kivyforge/config/loader.py +3079 -0
  31. kivyforge/config/model.py +817 -0
  32. kivyforge/doctor/__init__.py +19 -0
  33. kivyforge/doctor/checks_common.py +95 -0
  34. kivyforge/doctor/probe.py +357 -0
  35. kivyforge/doctor/result.py +41 -0
  36. kivyforge/lock/__init__.py +42 -0
  37. kivyforge/lock/_pip_shim.py +63 -0
  38. kivyforge/lock/find_links.py +182 -0
  39. kivyforge/lock/model.py +69 -0
  40. kivyforge/lock/pep751.py +111 -0
  41. kivyforge/lock/reader.py +30 -0
  42. kivyforge/lock/resolver.py +124 -0
  43. kivyforge/lock/wheelruntime/__init__.py +70 -0
  44. kivyforge/lock/wheelruntime/builder.py +280 -0
  45. kivyforge/lock/wheelruntime/model.py +94 -0
  46. kivyforge/lock/wheelruntime/native_binaries.py +127 -0
  47. kivyforge/lock/wheelruntime/pbs_github.py +133 -0
  48. kivyforge/lock/wheelruntime/profile.py +104 -0
  49. kivyforge/lock/wheelruntime/resolver.py +248 -0
  50. kivyforge/lock/wheelruntime/runtime.py +161 -0
  51. kivyforge/lock/wheelruntime/serialize.py +245 -0
  52. kivyforge/platforms/__init__.py +114 -0
  53. kivyforge/platforms/android/__init__.py +140 -0
  54. kivyforge/platforms/android/adb.py +215 -0
  55. kivyforge/platforms/android/bootstrap/__init__.py +7 -0
  56. kivyforge/platforms/android/bootstrap/contract.py +166 -0
  57. kivyforge/platforms/android/bootstrap/render.py +244 -0
  58. kivyforge/platforms/android/bootstrap/templates/androidtest/KivyforgeContractTest.java +76 -0
  59. kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivy_bootstrap.py +50 -0
  60. kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivyforge_bootstrap.py +54 -0
  61. kivyforge/platforms/android/bootstrap/templates/bootstrap_py/_kivyforge_selftest.py +97 -0
  62. kivyforge/platforms/android/bootstrap/templates/cpp/CMakeLists.txt +20 -0
  63. kivyforge/platforms/android/bootstrap/templates/cpp/main.c +150 -0
  64. kivyforge/platforms/android/bootstrap/templates/java/org/jnius/NativeInvocationHandler.java +48 -0
  65. kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonActivity.java +75 -0
  66. kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonBundle.java +132 -0
  67. kivyforge/platforms/android/bootstrap/templates/java/org/kivy/android/PythonService.java +189 -0
  68. kivyforge/platforms/android/bootstrap/templates/java/org/renpy/android/Hardware.java +276 -0
  69. kivyforge/platforms/android/bootstrap/templates/sdl2/LICENSE-SDL.txt +18 -0
  70. kivyforge/platforms/android/bootstrap/templates/sdl2/SDL_REVISION.txt +3 -0
  71. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDevice.java +22 -0
  72. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceBLESteamController.java +650 -0
  73. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceManager.java +698 -0
  74. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/HIDDeviceUSB.java +309 -0
  75. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDL.java +90 -0
  76. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLActivity.java +2120 -0
  77. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLAudioManager.java +514 -0
  78. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLControllerManager.java +856 -0
  79. kivyforge/platforms/android/bootstrap/templates/sdl2/org/libsdl/app/SDLSurface.java +405 -0
  80. kivyforge/platforms/android/bootstrap/templates/sdl3/LICENSE-SDL.txt +18 -0
  81. kivyforge/platforms/android/bootstrap/templates/sdl3/SDL_REVISION.txt +3 -0
  82. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDevice.java +21 -0
  83. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceBLESteamController.java +829 -0
  84. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceManager.java +698 -0
  85. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/HIDDeviceUSB.java +354 -0
  86. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDL.java +90 -0
  87. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLActivity.java +2240 -0
  88. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLAudioManager.java +126 -0
  89. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLControllerManager.java +1010 -0
  90. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLDummyEdit.java +66 -0
  91. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLInputConnection.java +136 -0
  92. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLSensorManager.java +73 -0
  93. kivyforge/platforms/android/bootstrap/templates/sdl3/org/libsdl/app/SDLSurface.java +464 -0
  94. kivyforge/platforms/android/cli.py +1023 -0
  95. kivyforge/platforms/android/doctor.py +899 -0
  96. kivyforge/platforms/android/elf.py +148 -0
  97. kivyforge/platforms/android/generate/__init__.py +1 -0
  98. kivyforge/platforms/android/generate/manifest.py +261 -0
  99. kivyforge/platforms/android/generate/project.py +462 -0
  100. kivyforge/platforms/android/generate/services.py +187 -0
  101. kivyforge/platforms/android/gradle_wrapper/NOTICE +3 -0
  102. kivyforge/platforms/android/gradle_wrapper/SHA256SUMS +1 -0
  103. kivyforge/platforms/android/gradle_wrapper/gradle-wrapper.jar +0 -0
  104. kivyforge/platforms/android/gradle_wrapper/gradlew +164 -0
  105. kivyforge/platforms/android/gradle_wrapper/gradlew.bat +90 -0
  106. kivyforge/platforms/android/gradlew.py +60 -0
  107. kivyforge/platforms/android/icons.py +237 -0
  108. kivyforge/platforms/android/lock/__init__.py +1 -0
  109. kivyforge/platforms/android/lock/archives.py +109 -0
  110. kivyforge/platforms/android/lock/builder.py +387 -0
  111. kivyforge/platforms/android/lock/markers.py +71 -0
  112. kivyforge/platforms/android/lock/maven.py +252 -0
  113. kivyforge/platforms/android/lock/model.py +113 -0
  114. kivyforge/platforms/android/lock/python_meta.py +199 -0
  115. kivyforge/platforms/android/lock/reader.py +206 -0
  116. kivyforge/platforms/android/lock/resolver.py +276 -0
  117. kivyforge/platforms/android/lock/writer.py +121 -0
  118. kivyforge/platforms/android/policy.py +214 -0
  119. kivyforge/platforms/android/signing.py +145 -0
  120. kivyforge/platforms/android/smoke.py +43 -0
  121. kivyforge/platforms/android/splash.py +293 -0
  122. kivyforge/platforms/android/stage/__init__.py +1 -0
  123. kivyforge/platforms/android/stage/bundle.py +295 -0
  124. kivyforge/platforms/android/stage/jnilibs.py +186 -0
  125. kivyforge/platforms/android/stage/runtime.py +61 -0
  126. kivyforge/platforms/android/stage/wheels.py +106 -0
  127. kivyforge/platforms/android/toolchain.py +29 -0
  128. kivyforge/platforms/base.py +157 -0
  129. kivyforge/platforms/ios/__init__.py +129 -0
  130. kivyforge/platforms/ios/assets.py +193 -0
  131. kivyforge/platforms/ios/buildsettings.py +128 -0
  132. kivyforge/platforms/ios/cli.py +543 -0
  133. kivyforge/platforms/ios/doctor.py +410 -0
  134. kivyforge/platforms/ios/entitlements.py +34 -0
  135. kivyforge/platforms/ios/generator.py +318 -0
  136. kivyforge/platforms/ios/lock/__init__.py +71 -0
  137. kivyforge/platforms/ios/lock/builder.py +393 -0
  138. kivyforge/platforms/ios/lock/markers.py +69 -0
  139. kivyforge/platforms/ios/lock/model.py +95 -0
  140. kivyforge/platforms/ios/lock/python_meta.py +187 -0
  141. kivyforge/platforms/ios/lock/reader.py +191 -0
  142. kivyforge/platforms/ios/lock/resolver.py +304 -0
  143. kivyforge/platforms/ios/lock/spm.py +217 -0
  144. kivyforge/platforms/ios/lock/writer.py +114 -0
  145. kivyforge/platforms/ios/lock/xcframework.py +188 -0
  146. kivyforge/platforms/ios/materialize.py +59 -0
  147. kivyforge/platforms/ios/plist.py +91 -0
  148. kivyforge/platforms/ios/privacy.py +39 -0
  149. kivyforge/platforms/ios/skeleton.py +165 -0
  150. kivyforge/platforms/ios/sources.py +63 -0
  151. kivyforge/platforms/ios/staging.py +225 -0
  152. kivyforge/platforms/ios/swift_packages.py +324 -0
  153. kivyforge/platforms/ios/templates/kivyforge_bootstrap.h +34 -0
  154. kivyforge/platforms/ios/templates/kivyforge_bootstrap.m +146 -0
  155. kivyforge/platforms/ios/templates/main.m +15 -0
  156. kivyforge/platforms/ios/templates/main_config.h.in +12 -0
  157. kivyforge/platforms/ios/xcframeworks.py +194 -0
  158. kivyforge/platforms/ios/xcode/__init__.py +76 -0
  159. kivyforge/platforms/ios/xcode/commands.py +296 -0
  160. kivyforge/platforms/ios/xcode/runner.py +316 -0
  161. kivyforge/platforms/linux/__init__.py +120 -0
  162. kivyforge/platforms/linux/appimage.py +169 -0
  163. kivyforge/platforms/linux/bundle.py +235 -0
  164. kivyforge/platforms/linux/cli.py +232 -0
  165. kivyforge/platforms/linux/desktop.py +71 -0
  166. kivyforge/platforms/linux/doctor.py +438 -0
  167. kivyforge/platforms/linux/elftools.py +95 -0
  168. kivyforge/platforms/linux/icons.py +129 -0
  169. kivyforge/platforms/linux/launcher.py +107 -0
  170. kivyforge/platforms/linux/lock/__init__.py +126 -0
  171. kivyforge/platforms/linux/lock/profile.py +254 -0
  172. kivyforge/platforms/linux/lock/runtime.py +75 -0
  173. kivyforge/platforms/linux/native_stage.py +47 -0
  174. kivyforge/platforms/linux/runtime_stage.py +107 -0
  175. kivyforge/platforms/linux/wheels_stage.py +103 -0
  176. kivyforge/platforms/macos/__init__.py +121 -0
  177. kivyforge/platforms/macos/bundle.py +255 -0
  178. kivyforge/platforms/macos/cli.py +269 -0
  179. kivyforge/platforms/macos/doctor.py +480 -0
  180. kivyforge/platforms/macos/icns.py +75 -0
  181. kivyforge/platforms/macos/launcher.py +118 -0
  182. kivyforge/platforms/macos/lock/__init__.py +112 -0
  183. kivyforge/platforms/macos/lock/profile.py +119 -0
  184. kivyforge/platforms/macos/lock/runtime.py +58 -0
  185. kivyforge/platforms/macos/machotools.py +138 -0
  186. kivyforge/platforms/macos/native_stage.py +47 -0
  187. kivyforge/platforms/macos/notarize.py +143 -0
  188. kivyforge/platforms/macos/plist.py +37 -0
  189. kivyforge/platforms/macos/runtime_stage.py +110 -0
  190. kivyforge/platforms/macos/signing.py +78 -0
  191. kivyforge/platforms/macos/wheels_stage.py +108 -0
  192. kivyforge/platforms/windows/__init__.py +133 -0
  193. kivyforge/platforms/windows/assets.py +76 -0
  194. kivyforge/platforms/windows/bundle.py +298 -0
  195. kivyforge/platforms/windows/cli.py +314 -0
  196. kivyforge/platforms/windows/doctor.py +563 -0
  197. kivyforge/platforms/windows/fsswap.py +87 -0
  198. kivyforge/platforms/windows/icons.py +64 -0
  199. kivyforge/platforms/windows/launcher/__init__.py +193 -0
  200. kivyforge/platforms/windows/launcher/build_launcher.py +507 -0
  201. kivyforge/platforms/windows/launcher/launcher.c +379 -0
  202. kivyforge/platforms/windows/lock/__init__.py +112 -0
  203. kivyforge/platforms/windows/lock/profile.py +101 -0
  204. kivyforge/platforms/windows/lock/runtime.py +64 -0
  205. kivyforge/platforms/windows/naming.py +52 -0
  206. kivyforge/platforms/windows/native_stage.py +66 -0
  207. kivyforge/platforms/windows/petools.py +98 -0
  208. kivyforge/platforms/windows/rcedit.py +116 -0
  209. kivyforge/platforms/windows/runtime_stage.py +221 -0
  210. kivyforge/platforms/windows/signing.py +168 -0
  211. kivyforge/platforms/windows/vendor/LICENSE.rcedit +29 -0
  212. kivyforge/platforms/windows/vendor/NOTICE +17 -0
  213. kivyforge/platforms/windows/vendor/SHA256SUMS +4 -0
  214. kivyforge/platforms/windows/vendor/TOOLSET.txt +4 -0
  215. kivyforge/platforms/windows/vendor/__init__.py +12 -0
  216. kivyforge/platforms/windows/vendor/fetch_rcedit.py +97 -0
  217. kivyforge/platforms/windows/vendor/launcher-amd64.exe +0 -0
  218. kivyforge/platforms/windows/vendor/rcedit-x64.exe +0 -0
  219. kivyforge/platforms/windows/wheels_stage.py +139 -0
  220. kivyforge-3.0.0.dev0.dist-info/METADATA +609 -0
  221. kivyforge-3.0.0.dev0.dist-info/RECORD +225 -0
  222. kivyforge-3.0.0.dev0.dist-info/WHEEL +5 -0
  223. kivyforge-3.0.0.dev0.dist-info/entry_points.txt +3 -0
  224. kivyforge-3.0.0.dev0.dist-info/licenses/LICENSE +21 -0
  225. kivyforge-3.0.0.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,132 @@
1
+ package org.kivy.android;
2
+
3
+ import android.content.Context;
4
+ import android.system.Os;
5
+ import android.util.Log;
6
+
7
+ import java.io.File;
8
+ import java.io.FileNotFoundException;
9
+ import java.io.FileOutputStream;
10
+ import java.io.InputStream;
11
+ import java.io.OutputStream;
12
+ import java.io.RandomAccessFile;
13
+ import java.nio.channels.FileChannel;
14
+ import java.nio.channels.FileLock;
15
+ import java.nio.charset.StandardCharsets;
16
+ import java.nio.file.Files;
17
+
18
+ /**
19
+ * The asset-bundle unpack + environment contract, shared by every process that
20
+ * starts a kivyforge interpreter (docs/design/platforms/android/05).
21
+ *
22
+ * PythonActivity and PythonService both need exactly this sequence, and a
23
+ * service runs in its own process (android:process=":service_*"), so it unpacks
24
+ * and configures independently rather than inheriting the activity's state.
25
+ * Keeping one implementation is deliberate: the environment contract is the
26
+ * most delicate part of the load model, and two copies would drift.
27
+ */
28
+ public class PythonBundle {
29
+ private static final String TAG = "kivyforge";
30
+
31
+ /** Unpack assets/_python_bundle to filesDir, skipped when VERSION matches.
32
+ *
33
+ * Serialized across processes: a service starts in its own process and may
34
+ * race the activity here, and two unpacks into the same directory would
35
+ * leave a half-written bundle behind whichever finished first.
36
+ */
37
+ public static File unpack(Context ctx) throws Exception {
38
+ File lock = new File(ctx.getFilesDir(), "_python_bundle.lock");
39
+ try (RandomAccessFile raf = new RandomAccessFile(lock, "rw");
40
+ FileChannel channel = raf.getChannel();
41
+ FileLock held = channel.lock()) {
42
+ return unpackLocked(ctx);
43
+ }
44
+ }
45
+
46
+ private static File unpackLocked(Context ctx) throws Exception {
47
+ File target = new File(ctx.getFilesDir(), "_python_bundle");
48
+ File stamp = new File(target, "VERSION");
49
+ String wanted = readAsset(ctx, "_python_bundle/VERSION");
50
+ if (stamp.exists()) {
51
+ String have = new String(
52
+ Files.readAllBytes(stamp.toPath()), StandardCharsets.UTF_8);
53
+ if (have.equals(wanted)) {
54
+ Log.i(TAG, "bundle up to date (VERSION " + have + "), skipping unpack");
55
+ Os.setenv("KF_UNPACK_SKIPPED", "1", true);
56
+ return target;
57
+ }
58
+ }
59
+ Log.i(TAG, "unpacking bundle...");
60
+ long t0 = System.currentTimeMillis();
61
+ deleteRecursive(target);
62
+ extractAssetDir(ctx, "_python_bundle", ctx.getFilesDir());
63
+ Log.i(TAG, "bundle unpacked in " + (System.currentTimeMillis() - t0) + " ms");
64
+ Os.setenv("KF_UNPACK_SKIPPED", "0", true);
65
+ return target;
66
+ }
67
+
68
+ /**
69
+ * Set the environment contract the native launcher and Kivy/pyjnius read.
70
+ * Must run before any native code in the process.
71
+ */
72
+ public static void setEnvironment(Context ctx, File bundleDir, String entryPoint)
73
+ throws Exception {
74
+ File appDir = new File(bundleDir, "app");
75
+ Os.setenv("KF_BUNDLE", bundleDir.getAbsolutePath(), true);
76
+ Os.setenv("KF_NATIVE_DIR", ctx.getApplicationInfo().nativeLibraryDir, true);
77
+ Os.setenv("KF_ENTRY_POINT", entryPoint, true);
78
+ // pyjnius/Kivy environment contract:
79
+ Os.setenv("ANDROID_ARGUMENT", appDir.getAbsolutePath(), true);
80
+ Os.setenv("ANDROID_APP_PATH", appDir.getAbsolutePath(), true);
81
+ Os.setenv("ANDROID_PRIVATE", ctx.getFilesDir().getAbsolutePath(), true);
82
+ Os.setenv("ANDROID_UNPACK", bundleDir.getAbsolutePath(), true);
83
+ Os.setenv("TMPDIR", ctx.getCacheDir().getAbsolutePath(), false);
84
+ }
85
+
86
+ private static String readAsset(Context ctx, String path) throws Exception {
87
+ try (InputStream in = ctx.getAssets().open(path)) {
88
+ return new String(readAll(in), StandardCharsets.UTF_8);
89
+ }
90
+ }
91
+
92
+ private static byte[] readAll(InputStream in) throws Exception {
93
+ java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
94
+ byte[] buf = new byte[65536];
95
+ int n;
96
+ while ((n = in.read(buf)) > 0) bos.write(buf, 0, n);
97
+ return bos.toByteArray();
98
+ }
99
+
100
+ private static void extractAssetDir(Context ctx, String path, File targetDir)
101
+ throws Exception {
102
+ String[] names = ctx.getAssets().list(path);
103
+ if (names == null) throw new RuntimeException("cannot list asset " + path);
104
+ File subdir = new File(targetDir, path);
105
+ if (!subdir.exists() && !subdir.mkdirs())
106
+ throw new RuntimeException("mkdirs failed: " + subdir);
107
+ for (String name : names) {
108
+ String subPath = path + "/" + name;
109
+ InputStream in;
110
+ try {
111
+ in = ctx.getAssets().open(subPath);
112
+ } catch (FileNotFoundException e) {
113
+ extractAssetDir(ctx, subPath, targetDir);
114
+ continue;
115
+ }
116
+ try (InputStream input = in;
117
+ OutputStream out = new FileOutputStream(new File(subdir, name))) {
118
+ byte[] buf = new byte[65536];
119
+ int n;
120
+ while ((n = input.read(buf)) > 0) out.write(buf, 0, n);
121
+ }
122
+ }
123
+ }
124
+
125
+ private static void deleteRecursive(File f) {
126
+ if (f.isDirectory()) {
127
+ File[] children = f.listFiles();
128
+ if (children != null) for (File c : children) deleteRecursive(c);
129
+ }
130
+ f.delete();
131
+ }
132
+ }
@@ -0,0 +1,189 @@
1
+ package org.kivy.android;
2
+
3
+ import android.app.Notification;
4
+ import android.app.NotificationChannel;
5
+ import android.app.NotificationManager;
6
+ import android.app.Service;
7
+ import android.content.Intent;
8
+ import android.os.Build;
9
+ import android.os.IBinder;
10
+ import android.system.Os;
11
+ import android.util.Log;
12
+
13
+ import java.io.File;
14
+ import java.io.FileOutputStream;
15
+ import java.nio.charset.StandardCharsets;
16
+
17
+ /**
18
+ * Base class for the services generated from [[tool.kivy.android.services]]
19
+ * (docs/design/platforms/android/05 §services).
20
+ *
21
+ * Each declared service becomes a subclass that names its Python entry point
22
+ * and, when it is a foreground service, its notification. The manifest gives
23
+ * every service its own android:process, so this runs in a fresh process: it
24
+ * unpacks the bundle, sets the same environment contract the activity does, and
25
+ * starts one interpreter — on a background thread, because the interpreter does
26
+ * not return until the entry point does and blocking a service's main thread is
27
+ * an ANR.
28
+ *
29
+ * There is no SDL here. The service's Python does background work, so only
30
+ * libpython and libmain are loaded; nothing creates a window or a JNIEnv-owning
31
+ * SDL thread.
32
+ */
33
+ public class PythonService extends Service implements Runnable {
34
+ private static final String TAG = "kivyforge";
35
+ // The libpython soname stem, substituted at render time.
36
+ private static final String PYTHON_LIB = "python3.14";
37
+
38
+ private static final int NOTIFICATION_ID = 1;
39
+
40
+ private boolean mStarted = false;
41
+ private Thread mThread = null;
42
+
43
+ // --- overridden by the generated subclass ------------------------------ #
44
+
45
+ /** The service's [[services]] name, used for logging + the ready marker. */
46
+ protected String getServiceName() {
47
+ return getClass().getSimpleName();
48
+ }
49
+
50
+ /** The Python module this service imports (its own entry_point). */
51
+ protected String getEntryPoint() {
52
+ return "main";
53
+ }
54
+
55
+ protected boolean isForeground() {
56
+ return false;
57
+ }
58
+
59
+ protected String getChannelId() {
60
+ return "kivyforge_service";
61
+ }
62
+
63
+ protected String getChannelName() {
64
+ return "Background service";
65
+ }
66
+
67
+ protected String getNotificationTitle() {
68
+ return getServiceName();
69
+ }
70
+
71
+ protected String getNotificationText() {
72
+ return "";
73
+ }
74
+
75
+ /** A drawable resource name for the notification icon, or null for none. */
76
+ protected String getNotificationIcon() {
77
+ return null;
78
+ }
79
+
80
+ // --- service lifecycle ------------------------------------------------- #
81
+
82
+ @Override
83
+ public int onStartCommand(Intent intent, int flags, int startId) {
84
+ if (isForeground()) {
85
+ // Mandatory on Android 14+: a foreground service that does not
86
+ // call startForeground() promptly is killed by the OS.
87
+ startForegroundNotification();
88
+ }
89
+ if (!mStarted) {
90
+ mStarted = true;
91
+ String argument = intent == null
92
+ ? null : intent.getStringExtra("kivyforge_service_argument");
93
+ try {
94
+ prepare(argument);
95
+ } catch (Exception e) {
96
+ Log.e(TAG, "service " + getServiceName() + " failed to prepare", e);
97
+ stopSelf();
98
+ return START_NOT_STICKY;
99
+ }
100
+ mThread = new Thread(this, "python-service-" + getServiceName());
101
+ mThread.start();
102
+ }
103
+ // Deliberately not sticky: a crash-looping service that the OS keeps
104
+ // restarting is worse than one that stays down and is visible as down.
105
+ return START_NOT_STICKY;
106
+ }
107
+
108
+ @Override
109
+ public IBinder onBind(Intent intent) {
110
+ return null;
111
+ }
112
+
113
+ /** Unpack + environment, then the ready-marker path the smoke test polls. */
114
+ private void prepare(String argument) throws Exception {
115
+ File bundleDir = PythonBundle.unpack(this);
116
+ PythonBundle.setEnvironment(this, bundleDir, getEntryPoint());
117
+ // p4a convention, kept so existing service code keeps working.
118
+ Os.setenv("PYTHON_SERVICE_ARGUMENT", argument == null ? "" : argument, true);
119
+ File marker = markerFile(this, getServiceName());
120
+ marker.delete();
121
+ Os.setenv("KF_SERVICE_MARKER", marker.getAbsolutePath(), true);
122
+ }
123
+
124
+ /** Where the interpreter records that it came up (android/06 --smoke). */
125
+ public static File markerFile(Service service, String name) {
126
+ return new File(service.getFilesDir(), "kivyforge_service_" + name + ".txt");
127
+ }
128
+
129
+ @Override
130
+ public void run() {
131
+ Log.i(TAG, "service " + getServiceName() + ": starting " + getEntryPoint());
132
+ try {
133
+ System.loadLibrary(PYTHON_LIB);
134
+ System.loadLibrary("main");
135
+ int rc = nativeStart();
136
+ Log.i(TAG, "service " + getServiceName() + " returned " + rc);
137
+ } catch (UnsatisfiedLinkError e) {
138
+ Log.e(TAG, "service " + getServiceName() + ": native start failed", e);
139
+ recordFailure(e.toString());
140
+ } finally {
141
+ stopSelf();
142
+ }
143
+ }
144
+
145
+ private void recordFailure(String detail) {
146
+ File marker = markerFile(this, getServiceName());
147
+ try (FileOutputStream out = new FileOutputStream(marker, true)) {
148
+ out.write(("SERVICE_START_FAILED " + detail + "\n")
149
+ .getBytes(StandardCharsets.UTF_8));
150
+ } catch (Exception ignored) {
151
+ // The marker is diagnostics; losing it must not mask the real error.
152
+ }
153
+ }
154
+
155
+ private void startForegroundNotification() {
156
+ NotificationManager manager = getSystemService(NotificationManager.class);
157
+ Notification.Builder builder;
158
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
159
+ NotificationChannel channel = new NotificationChannel(
160
+ getChannelId(), getChannelName(), NotificationManager.IMPORTANCE_LOW);
161
+ manager.createNotificationChannel(channel);
162
+ builder = new Notification.Builder(this, getChannelId());
163
+ } else {
164
+ builder = new Notification.Builder(this);
165
+ }
166
+ builder.setContentTitle(getNotificationTitle());
167
+ builder.setContentText(getNotificationText());
168
+ builder.setSmallIcon(notificationIconResource());
169
+ startForeground(NOTIFICATION_ID, builder.build());
170
+ }
171
+
172
+ private int notificationIconResource() {
173
+ String icon = getNotificationIcon();
174
+ if (icon != null) {
175
+ int id = getResources().getIdentifier(icon, "drawable", getPackageName());
176
+ if (id == 0) {
177
+ id = getResources().getIdentifier(icon, "mipmap", getPackageName());
178
+ }
179
+ if (id != 0) {
180
+ return id;
181
+ }
182
+ Log.w(TAG, "notification icon " + icon + " not found; using the launcher");
183
+ }
184
+ return getApplicationInfo().icon;
185
+ }
186
+
187
+ /** Implemented in libmain (cpp/main.c): brings up CPython in this process. */
188
+ private native int nativeStart();
189
+ }
@@ -0,0 +1,276 @@
1
+ /*
2
+ * org.renpy.android.Hardware — Kivy-compatibility shim.
3
+ *
4
+ * Kivy's own kivy/metrics.py calls autoclass('org.renpy.android.Hardware')
5
+ * for DPI/fontscale, so the kivyforge bootstrap must supply this class for
6
+ * unmodified Kivy-Android apps to start (android/05 §namespace preservation).
7
+ * Vendored verbatim from python-for-android (MIT License,
8
+ * https://github.com/kivy/python-for-android) — the surface Kivy was written
9
+ * against. Kivy's startup path (getDPI/get_fontscale) uses only the
10
+ * PythonActivity.mActivity reference; `context` is initialized lazily below so
11
+ * the sensor/network helpers work without a cross-package assignment.
12
+ *
13
+ * vibrate/getDPI/checkNetwork call APIs Android has since deprecated
14
+ * (Vibrator#vibrate(int), Display#getMetrics, ConnectivityManager
15
+ * #getActiveNetworkInfo) but not removed; their replacements need API
16
+ * 26/30/29 respectively, above this project's min_sdk floor of 24, so the
17
+ * deprecated call is kept rather than forking this surface into two paths.
18
+ * @SuppressWarnings("deprecation") on each is a deliberate, understood
19
+ * choice, not an oversight — see android/05 for the min_sdk floor.
20
+ */
21
+ package org.renpy.android;
22
+
23
+ import android.content.BroadcastReceiver;
24
+ import android.content.Context;
25
+ import android.content.Intent;
26
+ import android.content.IntentFilter;
27
+ import android.hardware.Sensor;
28
+ import android.hardware.SensorEvent;
29
+ import android.hardware.SensorEventListener;
30
+ import android.hardware.SensorManager;
31
+ import android.net.ConnectivityManager;
32
+ import android.net.NetworkInfo;
33
+ import android.net.wifi.ScanResult;
34
+ import android.net.wifi.WifiManager;
35
+ import android.os.Vibrator;
36
+ import android.util.DisplayMetrics;
37
+ import android.view.View;
38
+ import android.view.inputmethod.InputMethodManager;
39
+ import java.util.List;
40
+ import org.kivy.android.PythonActivity;
41
+
42
+ /**
43
+ * Methods that are expected to be called via JNI, to access the device's non-screen hardware. (For
44
+ * example, the vibration and accelerometer.)
45
+ */
46
+ public class Hardware {
47
+
48
+ // The context.
49
+ static Context context;
50
+ static View view;
51
+ public static final float defaultRv[] = {0f, 0f, 0f};
52
+
53
+ /** Vibrate for s seconds. */
54
+ @SuppressWarnings("deprecation")
55
+ public static void vibrate(double s) {
56
+ Vibrator v = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
57
+ if (v != null) {
58
+ v.vibrate((int) (1000 * s));
59
+ }
60
+ }
61
+
62
+ /** Get an Overview of all Hardware Sensors of an Android Device */
63
+ public static String getHardwareSensors() {
64
+ SensorManager sm = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
65
+ List<Sensor> allSensors = sm.getSensorList(Sensor.TYPE_ALL);
66
+
67
+ if (allSensors != null) {
68
+ String resultString = "";
69
+ for (Sensor s : allSensors) {
70
+ resultString += String.format("Name=" + s.getName());
71
+ resultString += String.format(",Vendor=" + s.getVendor());
72
+ resultString += String.format(",Version=" + s.getVersion());
73
+ resultString += String.format(",MaximumRange=" + s.getMaximumRange());
74
+ // XXX MinDelay is not in the 2.2
75
+ // resultString += String.format(",MinDelay=" + s.getMinDelay());
76
+ resultString += String.format(",Power=" + s.getPower());
77
+ resultString += String.format(",Type=" + s.getType() + "\n");
78
+ }
79
+ return resultString;
80
+ }
81
+ return "";
82
+ }
83
+
84
+ /**
85
+ * Get Access to 3 Axis Hardware Sensors Accelerometer, Orientation and Magnetic Field Sensors
86
+ */
87
+ public static class generic3AxisSensor implements SensorEventListener {
88
+ private final SensorManager sSensorManager;
89
+ private final Sensor sSensor;
90
+ private final int sSensorType;
91
+ SensorEvent sSensorEvent;
92
+
93
+ public generic3AxisSensor(int sensorType) {
94
+ sSensorType = sensorType;
95
+ sSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
96
+ sSensor = sSensorManager.getDefaultSensor(sSensorType);
97
+ }
98
+
99
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {}
100
+
101
+ public void onSensorChanged(SensorEvent event) {
102
+ sSensorEvent = event;
103
+ }
104
+
105
+ /** Enable or disable the Sensor by registering/unregistering */
106
+ public void changeStatus(boolean enable) {
107
+ if (enable) {
108
+ sSensorManager.registerListener(this, sSensor, SensorManager.SENSOR_DELAY_NORMAL);
109
+ } else {
110
+ sSensorManager.unregisterListener(this, sSensor);
111
+ }
112
+ }
113
+
114
+ /** Read the Sensor */
115
+ public float[] readSensor() {
116
+ if (sSensorEvent != null) {
117
+ return sSensorEvent.values;
118
+ } else {
119
+ return defaultRv;
120
+ }
121
+ }
122
+ }
123
+
124
+ public static generic3AxisSensor accelerometerSensor = null;
125
+ public static generic3AxisSensor orientationSensor = null;
126
+ public static generic3AxisSensor magneticFieldSensor = null;
127
+
128
+ /** functions for backward compatibility reasons */
129
+ public static void accelerometerEnable(boolean enable) {
130
+ if (accelerometerSensor == null)
131
+ accelerometerSensor = new generic3AxisSensor(Sensor.TYPE_ACCELEROMETER);
132
+ accelerometerSensor.changeStatus(enable);
133
+ }
134
+
135
+ public static float[] accelerometerReading() {
136
+ if (accelerometerSensor == null) return defaultRv;
137
+ return (float[]) accelerometerSensor.readSensor();
138
+ }
139
+
140
+ public static void orientationSensorEnable(boolean enable) {
141
+ if (orientationSensor == null)
142
+ orientationSensor = new generic3AxisSensor(Sensor.TYPE_ORIENTATION);
143
+ orientationSensor.changeStatus(enable);
144
+ }
145
+
146
+ public static float[] orientationSensorReading() {
147
+ if (orientationSensor == null) return defaultRv;
148
+ return (float[]) orientationSensor.readSensor();
149
+ }
150
+
151
+ public static void magneticFieldSensorEnable(boolean enable) {
152
+ if (magneticFieldSensor == null)
153
+ magneticFieldSensor = new generic3AxisSensor(Sensor.TYPE_MAGNETIC_FIELD);
154
+ magneticFieldSensor.changeStatus(enable);
155
+ }
156
+
157
+ public static float[] magneticFieldSensorReading() {
158
+ if (magneticFieldSensor == null) return defaultRv;
159
+ return (float[]) magneticFieldSensor.readSensor();
160
+ }
161
+
162
+ public static DisplayMetrics metrics = new DisplayMetrics();
163
+
164
+ /** Get display DPI. */
165
+ @SuppressWarnings("deprecation")
166
+ public static int getDPI() {
167
+ // AND: Shouldn't have to get the metrics like this every time...
168
+ PythonActivity.mActivity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
169
+ return metrics.densityDpi;
170
+ }
171
+
172
+ // /**
173
+ // * Show the soft keyboard.
174
+ // */
175
+ // public static void showKeyboard(int input_type) {
176
+ // //Log.i("python", "hardware.Java show_keyword " input_type);
177
+
178
+ // InputMethodManager imm = (InputMethodManager)
179
+ // context.getSystemService(Context.INPUT_METHOD_SERVICE);
180
+
181
+ // SDLSurfaceView vw = (SDLSurfaceView) view;
182
+
183
+ // int inputType = input_type;
184
+
185
+ // if (vw.inputType != inputType){
186
+ // vw.inputType = inputType;
187
+ // imm.restartInput(view);
188
+ // }
189
+
190
+ // imm.showSoftInput(view, InputMethodManager.SHOW_FORCED);
191
+ // }
192
+
193
+ /** Hide the soft keyboard. */
194
+ public static void hideKeyboard() {
195
+ InputMethodManager imm =
196
+ (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
197
+ imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
198
+ }
199
+
200
+ /** Scan WiFi networks */
201
+ static List<ScanResult> latestResult;
202
+
203
+ public static void enableWifiScanner() {
204
+ IntentFilter i = new IntentFilter();
205
+ i.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
206
+
207
+ context.registerReceiver(
208
+ new BroadcastReceiver() {
209
+
210
+ @Override
211
+ public void onReceive(Context c, Intent i) {
212
+ // Code to execute when SCAN_RESULTS_AVAILABLE_ACTION event occurs
213
+ WifiManager w = (WifiManager) c.getSystemService(Context.WIFI_SERVICE);
214
+ latestResult = w.getScanResults(); // Returns a <list> of scanResults
215
+ }
216
+ },
217
+ i);
218
+ }
219
+
220
+ public static String scanWifi() {
221
+
222
+ // Now you can call this and it should execute the broadcastReceiver's
223
+ // onReceive()
224
+ if (latestResult != null) {
225
+
226
+ String latestResultString = "";
227
+ for (ScanResult result : latestResult) {
228
+ latestResultString +=
229
+ String.format("%s\t%s\t%d\n", result.SSID, result.BSSID, result.level);
230
+ }
231
+
232
+ return latestResultString;
233
+ }
234
+
235
+ return "";
236
+ }
237
+
238
+ /** network state */
239
+ public static boolean network_state = false;
240
+
241
+ /**
242
+ * Check network state directly
243
+ *
244
+ * <p>(only one connection can be active at a given moment, detects all network type)
245
+ */
246
+ @SuppressWarnings("deprecation")
247
+ public static boolean checkNetwork() {
248
+ boolean state = false;
249
+ final ConnectivityManager conMgr =
250
+ (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
251
+
252
+ final NetworkInfo activeNetwork = conMgr.getActiveNetworkInfo();
253
+ if (activeNetwork != null && activeNetwork.isConnected()) {
254
+ state = true;
255
+ } else {
256
+ state = false;
257
+ }
258
+
259
+ return state;
260
+ }
261
+
262
+ /** To receive network state changes */
263
+ public static void registerNetworkCheck() {
264
+ IntentFilter i = new IntentFilter();
265
+ i.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
266
+ context.registerReceiver(
267
+ new BroadcastReceiver() {
268
+
269
+ @Override
270
+ public void onReceive(Context c, Intent i) {
271
+ network_state = checkNetwork();
272
+ }
273
+ },
274
+ i);
275
+ }
276
+ }
@@ -0,0 +1,18 @@
1
+ Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
2
+
3
+ This software is provided 'as-is', without any express or implied
4
+ warranty. In no event will the authors be held liable for any damages
5
+ arising from the use of this software.
6
+
7
+ Permission is granted to anyone to use this software for any purpose,
8
+ including commercial applications, and to alter it and redistribute it
9
+ freely, subject to the following restrictions:
10
+
11
+ 1. The origin of this software must not be misrepresented; you must not
12
+ claim that you wrote the original software. If you use this software
13
+ in a product, an acknowledgment in the product documentation would be
14
+ appreciated but is not required.
15
+ 2. Altered source versions must be plainly marked as such, and must not be
16
+ misrepresented as being the original software.
17
+ 3. This notice may not be removed or altered from any source distribution.
18
+
@@ -0,0 +1,3 @@
1
+ #define SDL_MAJOR_VERSION 2
2
+ #define SDL_MINOR_VERSION 32
3
+ #define SDL_PATCHLEVEL 10
@@ -0,0 +1,22 @@
1
+ package org.libsdl.app;
2
+
3
+ import android.hardware.usb.UsbDevice;
4
+
5
+ interface HIDDevice
6
+ {
7
+ public int getId();
8
+ public int getVendorId();
9
+ public int getProductId();
10
+ public String getSerialNumber();
11
+ public int getVersion();
12
+ public String getManufacturerName();
13
+ public String getProductName();
14
+ public UsbDevice getDevice();
15
+ public boolean open();
16
+ public int sendFeatureReport(byte[] report);
17
+ public int sendOutputReport(byte[] report);
18
+ public boolean getFeatureReport(byte[] report);
19
+ public void setFrozen(boolean frozen);
20
+ public void close();
21
+ public void shutdown();
22
+ }