pythonhere 0.2.0__py3-none-any.whl → 0.2.2__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.
- pythonhere/__init__.py +8 -1
- pythonhere/android_here.py +13 -6
- pythonhere/magic_here/prompts/README.md +49 -0
- pythonhere/magic_here/prompts/able.md +554 -0
- pythonhere/magic_here/prompts/android-media.md +130 -0
- pythonhere/magic_here/prompts/android-packages.md +69 -0
- pythonhere/magic_here/prompts/android-permissions.md +195 -0
- pythonhere/magic_here/prompts/android-runtime.md +34 -0
- pythonhere/magic_here/prompts/jnius.md +432 -0
- pythonhere/magic_here/prompts/kivy-kv.md +239 -0
- pythonhere/magic_here/prompts/kivy-runtime.md +306 -0
- pythonhere/magic_here/prompts/midi.md +243 -0
- pythonhere/magic_here/prompts/plyer.md +202 -0
- pythonhere/magic_here/prompts.py +37 -0
- pythonhere/main.py +9 -0
- pythonhere/server_here.py +1 -1
- pythonhere/version_here.py +1 -1
- {pythonhere-0.2.0.dist-info → pythonhere-0.2.2.dist-info}/METADATA +8 -4
- {pythonhere-0.2.0.dist-info → pythonhere-0.2.2.dist-info}/RECORD +22 -10
- {pythonhere-0.2.0.dist-info → pythonhere-0.2.2.dist-info}/WHEEL +0 -0
- {pythonhere-0.2.0.dist-info → pythonhere-0.2.2.dist-info}/licenses/LICENSE +0 -0
- {pythonhere-0.2.0.dist-info → pythonhere-0.2.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
## Pyjnius
|
|
2
|
+
|
|
3
|
+
`jnius` is installed.
|
|
4
|
+
|
|
5
|
+
Rules:
|
|
6
|
+
|
|
7
|
+
- Import Java classes with `from jnius import autoclass`.
|
|
8
|
+
- For Java callbacks/listeners implemented in Python, import and use
|
|
9
|
+
`PythonJavaClass` and `java_method`. A plain Python class with a matching
|
|
10
|
+
method name is not a Java interface implementation.
|
|
11
|
+
- Import `cast` only when the generated code actually uses it.
|
|
12
|
+
- Do not import Android app classes directly from `jnius`. For example, do not
|
|
13
|
+
write `from jnius import PythonService`; use
|
|
14
|
+
`autoclass("org.kivy.android.PythonService")` inside the fallback path.
|
|
15
|
+
- Import optional Android classes, such as `org.kivy.android.PythonService`,
|
|
16
|
+
only inside the fallback path where they are needed.
|
|
17
|
+
- Use `cast` only when a Java API requires a specific declared type.
|
|
18
|
+
- Keep Java object references local unless the user needs to inspect them later.
|
|
19
|
+
- Convert Java strings to Python strings with `str(...)` before storing results.
|
|
20
|
+
- Treat Java arrays and lists defensively: they may be `None`; iterate only after
|
|
21
|
+
checking.
|
|
22
|
+
- Java arrays are Python-indexable in Pyjnius. Use `len(java_array)` and
|
|
23
|
+
`java_array[index]`. Do not call `.size()` or `.get()` unless the object is a
|
|
24
|
+
Java `List`.
|
|
25
|
+
- For Java long bitmasks, Python `int` values are acceptable.
|
|
26
|
+
- Catch narrow exceptions only around optional Android fields or deprecated APIs.
|
|
27
|
+
Do not hide collection-wide failures.
|
|
28
|
+
- If catching an exception for one package/item, store a readable error string in
|
|
29
|
+
that item and continue.
|
|
30
|
+
- Do not use Pyjnius to launch external processes.
|
|
31
|
+
- Do not reference Android class constants from a variable that has not been
|
|
32
|
+
assigned with `autoclass`.
|
|
33
|
+
|
|
34
|
+
Pyjnius arrays:
|
|
35
|
+
- Use Python lists, bytes, or bytearray for Java array arguments.
|
|
36
|
+
- Do not invent imports like `jarray`.
|
|
37
|
+
- If a Java API needs a writable output buffer, use a mutable Python list or bytearray and check the method’s return value.
|
|
38
|
+
|
|
39
|
+
Pyjnius object conversion rules:
|
|
40
|
+
|
|
41
|
+
- Do not assume Python string conversion of Java objects produces valid Android
|
|
42
|
+
values.
|
|
43
|
+
- Convert Java `String` objects to Python strings with `str(...)` before storing
|
|
44
|
+
ordinary text results, but do not use Python `str(...)` for Android object
|
|
45
|
+
identifiers that have their own Java string form, such as `Uri`.
|
|
46
|
+
- For Java objects with Android-specific string forms, call the Java method
|
|
47
|
+
`toString()`.
|
|
48
|
+
- For `android.net.Uri`, prefer keeping the Java `Uri` object and passing it
|
|
49
|
+
directly to Android APIs such as `ContentResolver.openInputStream(uri)`.
|
|
50
|
+
- Do not convert Java `android.net.Uri` objects with Python `str(uri)`.
|
|
51
|
+
In Pyjnius, `str(uri)` may produce a Python object representation like
|
|
52
|
+
`<android.net.Uri at 0x... jclass=android/net/Uri ...>` instead of a valid
|
|
53
|
+
`content://...` URI string.
|
|
54
|
+
- If a Java `Uri` must be stored as text, use `uri.toString()`, not `str(uri)`.
|
|
55
|
+
- Never parse a Pyjnius object representation as a URI.
|
|
56
|
+
- Any generated code that logs or displays a URI should log both the media ID
|
|
57
|
+
and `uri.toString()`, not the Python object representation.
|
|
58
|
+
|
|
59
|
+
Wrong:
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
content_uri = ContentUris.withAppendedId(ImagesMedia.EXTERNAL_CONTENT_URI, image_id)
|
|
63
|
+
photos.append({"id": image_id, "uri": str(content_uri)})
|
|
64
|
+
|
|
65
|
+
uri = Uri.parse(photo["uri"])
|
|
66
|
+
stream = resolver.openInputStream(uri)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Correct, preferred:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
content_uri = ContentUris.withAppendedId(ImagesMedia.EXTERNAL_CONTENT_URI, image_id)
|
|
73
|
+
photos.append({"id": image_id, "uri": content_uri})
|
|
74
|
+
|
|
75
|
+
stream = resolver.openInputStream(photo["uri"])
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Correct, if serialization is needed:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
content_uri = ContentUris.withAppendedId(ImagesMedia.EXTERNAL_CONTENT_URI, image_id)
|
|
82
|
+
photos.append({"id": image_id, "uri": content_uri.toString()})
|
|
83
|
+
|
|
84
|
+
Uri = autoclass("android.net.Uri")
|
|
85
|
+
uri = Uri.parse(photo["uri"])
|
|
86
|
+
stream = resolver.openInputStream(uri)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Pyjnius Java class access rules:
|
|
90
|
+
|
|
91
|
+
- Do not call Java classes through undefined Python package names.
|
|
92
|
+
- Every Java class used must be bound with `autoclass(...)`.
|
|
93
|
+
- Do not assume Java package names are available as Python modules.
|
|
94
|
+
|
|
95
|
+
Wrong:
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
android.graphics.Bitmap.createScaledBitmap(bitmap, new_w, new_h, True)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Correct:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
Bitmap = autoclass("android.graphics.Bitmap")
|
|
105
|
+
scaled_bitmap = Bitmap.createScaledBitmap(bitmap, new_w, new_h, True)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Nested Java classes:
|
|
109
|
+
|
|
110
|
+
- In Pyjnius, nested Java classes must be imported with `$` using `autoclass`.
|
|
111
|
+
Do not access nested Java classes as Python attributes of the parent class.
|
|
112
|
+
|
|
113
|
+
Correct:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
VERSION = autoclass("android.os.Build$VERSION")
|
|
117
|
+
BitmapFactoryOptions = autoclass("android.graphics.BitmapFactory$Options")
|
|
118
|
+
CompressFormat = autoclass("android.graphics.Bitmap$CompressFormat")
|
|
119
|
+
PackageInfoFlags = autoclass("android.content.pm.PackageManager$PackageInfoFlags")
|
|
120
|
+
ImagesMedia = autoclass("android.provider.MediaStore$Images$Media")
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Incorrect:
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
Build.VERSION.SDK_INT
|
|
127
|
+
BitmapFactory.Options()
|
|
128
|
+
Bitmap.CompressFormat
|
|
129
|
+
PackageManager.PackageInfoFlags
|
|
130
|
+
MediaStore.Images.Media
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Android version checks:
|
|
134
|
+
|
|
135
|
+
- Use:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
VERSION = autoclass("android.os.Build$VERSION")
|
|
139
|
+
SDK_INT = VERSION.SDK_INT
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
- Do not use:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
Build.VERSION.SDK_INT
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
because Pyjnius does not reliably expose nested classes as Python attributes.
|
|
149
|
+
|
|
150
|
+
Nullable Java constants:
|
|
151
|
+
|
|
152
|
+
- Treat Android Java class constants returned through Pyjnius as nullable.
|
|
153
|
+
Some constants may be `None` even when the Android API normally defines them.
|
|
154
|
+
- Never pass unchecked Pyjnius constants into Android APIs that expect strings,
|
|
155
|
+
arrays of strings, column names, permissions, selection clauses, sort orders,
|
|
156
|
+
file modes, or intent actions.
|
|
157
|
+
- Before using a Java string constant in `projection`, `selection`, `sortOrder`,
|
|
158
|
+
`getColumnIndex(...)`, `Intent(...)`, or permission checks, resolve it to a
|
|
159
|
+
non-null Python string.
|
|
160
|
+
|
|
161
|
+
Correct:
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
ImagesMedia = autoclass("android.provider.MediaStore$Images$Media")
|
|
165
|
+
|
|
166
|
+
COL_ID = ImagesMedia._ID or "_id"
|
|
167
|
+
COL_DATE_ADDED = ImagesMedia.DATE_ADDED or "date_added"
|
|
168
|
+
COL_DATE_TAKEN = ImagesMedia.DATE_TAKEN or "datetaken"
|
|
169
|
+
COL_DISPLAY_NAME = ImagesMedia.DISPLAY_NAME or "_display_name"
|
|
170
|
+
COL_BUCKET = ImagesMedia.BUCKET_DISPLAY_NAME or "bucket_display_name"
|
|
171
|
+
|
|
172
|
+
projection = [COL_ID, COL_DATE_ADDED]
|
|
173
|
+
sort_order = COL_DATE_ADDED + " DESC"
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Incorrect:
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
projection = [ImagesMedia._ID, ImagesMedia.DATE_TAKEN]
|
|
180
|
+
sort_order = f"{ImagesMedia.DATE_TAKEN} DESC"
|
|
181
|
+
selection = ImagesMedia.BUCKET_DISPLAY_NAME + " = ?"
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
- Never allow `None` inside Java `String[]` arguments or string parameters.
|
|
185
|
+
This includes MediaStore projections, selection args, sort order strings, and
|
|
186
|
+
cursor column names.
|
|
187
|
+
- If an Android API throws a Java `NullPointerException` mentioning
|
|
188
|
+
`String.toLowerCase()` during a query, suspect a `None` column name or string
|
|
189
|
+
argument passed from Pyjnius.
|
|
190
|
+
- Prefer explicit fallback strings for well-known Android column names rather
|
|
191
|
+
than raw constants when generating resilient code.
|
|
192
|
+
|
|
193
|
+
Bitmap-related Pyjnius rules:
|
|
194
|
+
|
|
195
|
+
- For bitmap decode options, use:
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
BitmapFactoryOptions = autoclass("android.graphics.BitmapFactory$Options")
|
|
199
|
+
opts = BitmapFactoryOptions()
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
- Never use:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
opts = BitmapFactory.Options()
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
- For bitmap compression format, use:
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
CompressFormat = autoclass("android.graphics.Bitmap$CompressFormat")
|
|
212
|
+
bitmap.compress(CompressFormat.JPEG, 85, output_stream)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
- Never use:
|
|
216
|
+
|
|
217
|
+
```python
|
|
218
|
+
Bitmap.CompressFormat.JPEG
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
Android MediaStore and shared-media Pyjnius rules:
|
|
223
|
+
|
|
224
|
+
- Prefer MediaStore content URIs over raw filesystem paths.
|
|
225
|
+
- Do not depend on the `_data` column for gallery/media access.
|
|
226
|
+
- Query `_id`, build a content URI, then decode through `ContentResolver`.
|
|
227
|
+
- Keep the Java `Uri` object directly or store URI text with `uri.toString()`.
|
|
228
|
+
Do not store `str(uri)`.
|
|
229
|
+
- Do not filter only by `bucket_display_name = "Camera"` unless the user
|
|
230
|
+
specifically asked for the Camera folder only. For a general gallery, query
|
|
231
|
+
all images first, then add filters only after the broad query is confirmed
|
|
232
|
+
working.
|
|
233
|
+
- Always handle `cursor is None` and `cursor.getCount() == 0`.
|
|
234
|
+
- Always close the cursor in `finally`.
|
|
235
|
+
- If a thumbnail decode fails for one item, store/log a readable item-level
|
|
236
|
+
error and continue with other items.
|
|
237
|
+
|
|
238
|
+
Preferred MediaStore URI pattern:
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
ContentUris = autoclass("android.content.ContentUris")
|
|
242
|
+
ImagesMedia = autoclass("android.provider.MediaStore$Images$Media")
|
|
243
|
+
BitmapFactory = autoclass("android.graphics.BitmapFactory")
|
|
244
|
+
BitmapFactoryOptions = autoclass("android.graphics.BitmapFactory$Options")
|
|
245
|
+
|
|
246
|
+
content_uri = ContentUris.withAppendedId(
|
|
247
|
+
ImagesMedia.EXTERNAL_CONTENT_URI,
|
|
248
|
+
image_id,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
opts = BitmapFactoryOptions()
|
|
252
|
+
stream = resolver.openInputStream(content_uri)
|
|
253
|
+
try:
|
|
254
|
+
bitmap = BitmapFactory.decodeStream(stream, None, opts)
|
|
255
|
+
finally:
|
|
256
|
+
if stream is not None:
|
|
257
|
+
stream.close()
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Avoid as the default:
|
|
261
|
+
|
|
262
|
+
```python
|
|
263
|
+
path = cursor.getString(cursor.getColumnIndex("_data"))
|
|
264
|
+
bitmap = BitmapFactory.decodeFile(path, opts)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Android media permission and special-access rules:
|
|
268
|
+
|
|
269
|
+
- Declaring a permission in `buildozer.spec` or `AndroidManifest.xml` does not
|
|
270
|
+
prove it is granted at runtime.
|
|
271
|
+
- For Android 13+ image access, check/request
|
|
272
|
+
`android.permission.READ_MEDIA_IMAGES`.
|
|
273
|
+
- For Android 13+ video access, check/request
|
|
274
|
+
`android.permission.READ_MEDIA_VIDEO`.
|
|
275
|
+
- For Android 13+ audio access, check/request
|
|
276
|
+
`android.permission.READ_MEDIA_AUDIO`.
|
|
277
|
+
- For Android 12 and below, check/request
|
|
278
|
+
`android.permission.READ_EXTERNAL_STORAGE`.
|
|
279
|
+
- Do not rely on `WRITE_EXTERNAL_STORAGE` for reading shared photos on modern
|
|
280
|
+
Android.
|
|
281
|
+
- Do not check `MANAGE_EXTERNAL_STORAGE` with `check_permission(...)`.
|
|
282
|
+
It is special Android settings access, not a normal runtime permission.
|
|
283
|
+
- For arbitrary `/sdcard`, `/sdcard/DCIM`, `/sdcard/Download`, or full
|
|
284
|
+
shared-storage browsing on Android 11+, check
|
|
285
|
+
`Environment.isExternalStorageManager()`.
|
|
286
|
+
- If all-files access is needed and not enabled, open
|
|
287
|
+
`Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION` with
|
|
288
|
+
`Uri.parse("package:" + activity.getPackageName())`. If that fails, fall back
|
|
289
|
+
to `Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION`.
|
|
290
|
+
|
|
291
|
+
Thumbnail and cache rules:
|
|
292
|
+
|
|
293
|
+
- Write generated thumbnails into the app cache directory from
|
|
294
|
+
`context.getCacheDir().getAbsolutePath()`.
|
|
295
|
+
- Do not use `Path.cwd() / "gallery_cache"` for Android cache files.
|
|
296
|
+
- Kivy `Image.source` should point to a real cache file path.
|
|
297
|
+
- Do not assume Kivy supports `data:image/...;base64,...` sources.
|
|
298
|
+
- Use separate constants for UI size and bitmap decode size, for example
|
|
299
|
+
`THUMB_UI_DP = dp(120)` and `THUMB_PX = 240`.
|
|
300
|
+
- Do not pass `dp(...)` floats into Android bitmap APIs.
|
|
301
|
+
- Android bitmap dimensions and sample sizes must be plain Python `int` values.
|
|
302
|
+
- Recycle Android `Bitmap` objects after thumbnail compression when possible.
|
|
303
|
+
|
|
304
|
+
Media/gallery error-reporting rules:
|
|
305
|
+
|
|
306
|
+
- Do not hide media/gallery failures behind generic messages like
|
|
307
|
+
`Error loading photos. Check logs.`
|
|
308
|
+
- Generated code should show the real failure stage and exception message in the
|
|
309
|
+
UI during development.
|
|
310
|
+
- Background workers should return structured results such as
|
|
311
|
+
`{ "ok": False, "stage": "decode_thumbnail", "error": "AttributeError: ..." }`.
|
|
312
|
+
- Logs may include full stack traces, but logs must not be the only place where
|
|
313
|
+
the real problem appears.
|
|
314
|
+
|
|
315
|
+
General forbidden Pyjnius patterns:
|
|
316
|
+
|
|
317
|
+
Never generate:
|
|
318
|
+
|
|
319
|
+
```python
|
|
320
|
+
BitmapFactory.Options()
|
|
321
|
+
Bitmap.CompressFormat
|
|
322
|
+
Build.VERSION
|
|
323
|
+
MediaStore.Images.Media
|
|
324
|
+
PackageManager.PackageInfoFlags
|
|
325
|
+
projection = [SomeJavaClass.SOME_COLUMN]
|
|
326
|
+
sort_order = f"{SomeJavaClass.SOME_COLUMN} DESC"
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Generate:
|
|
330
|
+
|
|
331
|
+
```python
|
|
332
|
+
BitmapFactoryOptions = autoclass("android.graphics.BitmapFactory$Options")
|
|
333
|
+
CompressFormat = autoclass("android.graphics.Bitmap$CompressFormat")
|
|
334
|
+
VERSION = autoclass("android.os.Build$VERSION")
|
|
335
|
+
ImagesMedia = autoclass("android.provider.MediaStore$Images$Media")
|
|
336
|
+
PackageInfoFlags = autoclass("android.content.pm.PackageManager$PackageInfoFlags")
|
|
337
|
+
|
|
338
|
+
COL = SomeJavaClass.SOME_COLUMN or "known_fallback_name"
|
|
339
|
+
projection = [COL]
|
|
340
|
+
sort_order = COL + " DESC"
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
For Android package APIs:
|
|
344
|
+
|
|
345
|
+
- `PackageManager.GET_PERMISSIONS` requests permission metadata.
|
|
346
|
+
- Android API 33 and newer require
|
|
347
|
+
`android.content.pm.PackageManager$PackageInfoFlags.of(flags)`.
|
|
348
|
+
- In Pyjnius, define:
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
PackageInfoFlags = autoclass("android.content.pm.PackageManager$PackageInfoFlags")
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
and call:
|
|
355
|
+
|
|
356
|
+
```python
|
|
357
|
+
PackageInfoFlags.of(flags)
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
- Do not access it as:
|
|
361
|
+
|
|
362
|
+
```python
|
|
363
|
+
PackageManager.PackageInfoFlags
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
- Older APIs accept integer flags.
|
|
367
|
+
- System-app flags come from `android.content.pm.ApplicationInfo`.
|
|
368
|
+
Use `ApplicationInfo.FLAG_SYSTEM` and `ApplicationInfo.FLAG_UPDATED_SYSTEM_APP`.
|
|
369
|
+
Do not use `android.content.pm.ActivityInfo` for this.
|
|
370
|
+
- Permission grant status comes from
|
|
371
|
+
`android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED`, not from
|
|
372
|
+
`PackageManager.PERMISSION_GRANTED` and not from `ActivityInfo`.
|
|
373
|
+
- If checking requested permission grant status, define:
|
|
374
|
+
|
|
375
|
+
```python
|
|
376
|
+
PackageInfo = autoclass("android.content.pm.PackageInfo")
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
before using that constant.
|
|
380
|
+
- Version code should use `longVersionCode` when present and fall back to
|
|
381
|
+
`versionCode`.
|
|
382
|
+
|
|
383
|
+
Additional forbidden Pyjnius media patterns:
|
|
384
|
+
|
|
385
|
+
Never generate:
|
|
386
|
+
|
|
387
|
+
```python
|
|
388
|
+
str(content_uri)
|
|
389
|
+
Uri.parse(str(content_uri))
|
|
390
|
+
photos.append({"uri": str(content_uri)})
|
|
391
|
+
android.graphics.Bitmap.createScaledBitmap(bitmap, new_w, new_h, True)
|
|
392
|
+
Path.cwd() / "gallery_cache"
|
|
393
|
+
show_error("Error loading photos. Check logs.")
|
|
394
|
+
return None # after catching a media/gallery exception
|
|
395
|
+
cursor.getString(cursor.getColumnIndex("_data")) # as the primary media access path
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
Generate:
|
|
399
|
+
|
|
400
|
+
```python
|
|
401
|
+
photos.append({"uri": content_uri})
|
|
402
|
+
# or, only if text serialization is required:
|
|
403
|
+
photos.append({"uri": content_uri.toString()})
|
|
404
|
+
|
|
405
|
+
Bitmap = autoclass("android.graphics.Bitmap")
|
|
406
|
+
scaled_bitmap = Bitmap.createScaledBitmap(bitmap, new_w, new_h, True)
|
|
407
|
+
|
|
408
|
+
cache_dir = context.getCacheDir().getAbsolutePath()
|
|
409
|
+
|
|
410
|
+
return {
|
|
411
|
+
"ok": False,
|
|
412
|
+
"stage": stage,
|
|
413
|
+
"error": f"{type(e).__name__}: {e}",
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Pyjnius overload and Android intent safety:
|
|
418
|
+
- When Java APIs have overloaded constructors or methods, prefer the least
|
|
419
|
+
ambiguous call pattern through Pyjnius. For Android `Intent`, create
|
|
420
|
+
`intent = Intent(action)` and then call setters such as `setData(...)`,
|
|
421
|
+
`setType(...)`, or `putExtra(...)` rather than relying on overloaded
|
|
422
|
+
constructors.
|
|
423
|
+
- Do not pass Python `None` where Android expects a Java `String`, `String[]`,
|
|
424
|
+
`Uri`, `Intent`, `Context`, or callback/listener.
|
|
425
|
+
- When an API expects a Java primitive array or Java collection, prefer normal
|
|
426
|
+
Python lists only when Pyjnius is known to convert them for that method.
|
|
427
|
+
Otherwise use the Android/Python-for-Android helper API if one exists.
|
|
428
|
+
- For nullable Android constants, resolve to non-null Python strings before
|
|
429
|
+
passing them into Android APIs.
|
|
430
|
+
- Keep Settings/Intent launch results in a named global and report whether
|
|
431
|
+
`startActivity(...)` was attempted; do not claim the requested setting changed
|
|
432
|
+
just because the Settings screen opened.
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
## Kv design
|
|
2
|
+
|
|
3
|
+
Use KV for layout only:
|
|
4
|
+
- widget tree
|
|
5
|
+
- ids
|
|
6
|
+
- simple widget properties
|
|
7
|
+
- simple canvas instructions
|
|
8
|
+
|
|
9
|
+
Critical generated-KV constraints:
|
|
10
|
+
- If KV uses `dp(...)` or `sp(...)`, the KV string must include the matching
|
|
11
|
+
`#:import` line once at the top, before widget rules.
|
|
12
|
+
- If KV uses `sin(...)`, `cos(...)`, `abs(...)`, `min(...)`, or any other helper
|
|
13
|
+
function in an expression, the name must be defined in KV parser scope with
|
|
14
|
+
`#:import` or the calculation must be moved into Python.
|
|
15
|
+
- Do not use KV dynamic class/template syntax such as `<Name@BaseWidget>:` in
|
|
16
|
+
generated PythonHere snippets.
|
|
17
|
+
- Do not call `Builder.template(...)`; use `ui = Builder.load_string(KV)`.
|
|
18
|
+
- Do not put callbacks in KV. Bind callbacks in Python after
|
|
19
|
+
`Builder.load_string(KV)`.
|
|
20
|
+
- Do not use `app.some_method()` or `root.some_method()` in KV callbacks.
|
|
21
|
+
- Do not use `#:set` to inject Python globals, callback functions, generated
|
|
22
|
+
text, or state into KV. Set widget properties and bind callbacks from Python
|
|
23
|
+
after loading.
|
|
24
|
+
|
|
25
|
+
For generated PythonHere cells, do not put Python callbacks in KV.
|
|
26
|
+
Do not put generated dynamic Python logic in KV.
|
|
27
|
+
|
|
28
|
+
Avoid:
|
|
29
|
+
`on_release: something()`
|
|
30
|
+
`on_press: something()`
|
|
31
|
+
`on_text: something(self.text)`
|
|
32
|
+
`on_value: something(self.value)`
|
|
33
|
+
`values: [f"{num}: {name}" for num, name in sorted(items.items())]`
|
|
34
|
+
`text: some_python_variable`
|
|
35
|
+
`source: compute_path()`
|
|
36
|
+
`angle: app.feature_angle`
|
|
37
|
+
|
|
38
|
+
Also avoid generated proxy/global calls in KV:
|
|
39
|
+
`on_release: actions.handle(...)`
|
|
40
|
+
`on_release: app_actions["handle"]()`
|
|
41
|
+
`on_release: app.stop_everything()`
|
|
42
|
+
`on_release: root.stop_everything()`
|
|
43
|
+
`on_release: start_cb()`
|
|
44
|
+
`text: app_poem_text`
|
|
45
|
+
`#:set start_cb some_python_function`
|
|
46
|
+
`#:set app_poem_text some_python_text`
|
|
47
|
+
|
|
48
|
+
Avoid referencing notebook/global variables from KV. Kivy Builder may not have
|
|
49
|
+
the expected globals in parser scope, and KV parser errors are hard to recover
|
|
50
|
+
from in a live app. Put dynamic values into widgets from Python after the widget
|
|
51
|
+
tree exists.
|
|
52
|
+
Do not use `app.some_feature_state` in generated KV. In PythonHere, `app` is the
|
|
53
|
+
real PythonHere Kivy App instance, not a generated feature controller. Store
|
|
54
|
+
feature state on the generated root/widget class with Kivy properties, or update
|
|
55
|
+
widget/canvas instructions from Python.
|
|
56
|
+
|
|
57
|
+
Preferred pattern:
|
|
58
|
+
- Put ids on interactive widgets in KV.
|
|
59
|
+
- Load the UI.
|
|
60
|
+
- Set dynamic properties from Python after `Builder.load_string(KV)`.
|
|
61
|
+
- Bind callbacks in Python after `Builder.load_string(KV)`.
|
|
62
|
+
|
|
63
|
+
Example:
|
|
64
|
+
|
|
65
|
+
`ui = Builder.load_string(KV)`
|
|
66
|
+
`ui.ids.primary_button.bind(on_release=handle_primary_action)`
|
|
67
|
+
`ui.ids.value_slider.bind(value=handle_value_change)`
|
|
68
|
+
|
|
69
|
+
Dynamic values example:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
KV = """
|
|
73
|
+
BoxLayout:
|
|
74
|
+
Spinner:
|
|
75
|
+
id: instrument_spinner
|
|
76
|
+
text: "Choose instrument"
|
|
77
|
+
values: []
|
|
78
|
+
Slider:
|
|
79
|
+
id: volume_slider
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
ui = Builder.load_string(KV)
|
|
83
|
+
ui.ids.instrument_spinner.values = [
|
|
84
|
+
f"{num}: {name}" for num, name in sorted(midi_instruments.items())
|
|
85
|
+
]
|
|
86
|
+
ui.ids.instrument_spinner.bind(text=handle_instrument)
|
|
87
|
+
ui.ids.volume_slider.bind(value=handle_volume)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Widget-owned property example:
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
from kivy.properties import NumericProperty
|
|
94
|
+
from kivy.uix.floatlayout import FloatLayout
|
|
95
|
+
|
|
96
|
+
class DynamicRoot(FloatLayout):
|
|
97
|
+
feature_angle = NumericProperty(0)
|
|
98
|
+
|
|
99
|
+
KV = """
|
|
100
|
+
#:import dp kivy.metrics.dp
|
|
101
|
+
|
|
102
|
+
<DynamicRoot>:
|
|
103
|
+
canvas.before:
|
|
104
|
+
PushMatrix:
|
|
105
|
+
Rotate:
|
|
106
|
+
angle: root.feature_angle
|
|
107
|
+
origin: self.center
|
|
108
|
+
Ellipse:
|
|
109
|
+
size: dp(120), dp(120)
|
|
110
|
+
pos: self.center_x - dp(60), self.center_y - dp(60)
|
|
111
|
+
PopMatrix:
|
|
112
|
+
|
|
113
|
+
DynamicRoot:
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
ui = Builder.load_string(KV)
|
|
117
|
+
Clock.schedule_interval(lambda dt: setattr(ui, "feature_angle", ui.feature_angle + 3), 1 / 30)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
KV root rule:
|
|
121
|
+
- In PythonHere generated cells, `KV` must end with a concrete root widget instance.
|
|
122
|
+
- Prefer a direct root widget such as `BoxLayout:`, `FloatLayout:`, `GridLayout:`, or a custom class instance such as `DesktopUI:`.
|
|
123
|
+
- Do not make `KV` contain only class/rule definitions.
|
|
124
|
+
- Do not use KV dynamic class/template syntax such as `<RootWidget@BoxLayout>:`
|
|
125
|
+
for generated PythonHere snippets.
|
|
126
|
+
- Do not call `Builder.template(...)`. Use `ui = Builder.load_string(KV)` with a
|
|
127
|
+
KV string that ends in a concrete root widget instance.
|
|
128
|
+
- Do not load rule-only KV and then instantiate a Python class manually, such as
|
|
129
|
+
`Builder.load_string(KV); ui = MyWidget()`. The generated KV should return the
|
|
130
|
+
actual root widget from `Builder.load_string(KV)`.
|
|
131
|
+
- Do not call `Builder.unload_file(...)` or pass a fake `filename=...` for
|
|
132
|
+
generated inline KV snippets.
|
|
133
|
+
|
|
134
|
+
Good:
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
KV = """
|
|
138
|
+
BoxLayout:
|
|
139
|
+
Label:
|
|
140
|
+
text: "Hello"
|
|
141
|
+
"""
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Good when using custom classes:
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
KV = """
|
|
148
|
+
<DesktopUI>:
|
|
149
|
+
...
|
|
150
|
+
|
|
151
|
+
DesktopUI:
|
|
152
|
+
"""
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Bad:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
KV = """
|
|
159
|
+
<DesktopUI>:
|
|
160
|
+
...
|
|
161
|
+
"""
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
because `Builder.load_string(KV)` returns `None` for rule-only KV.
|
|
165
|
+
|
|
166
|
+
Required self-check:
|
|
167
|
+
- If the code does `ui = Builder.load_string(KV)`, then `ui` must be a widget.
|
|
168
|
+
- If `KV` contains `<SomeClass>:` rules, it must also contain a final concrete instance like `SomeClass:`.
|
|
169
|
+
- Never call `root.add_widget(ui)` unless `ui is not None`.
|
|
170
|
+
- Never call `Builder.template(...)` for generated PythonHere UI snippets.
|
|
171
|
+
- Never define `<SomeName@BaseWidget>:` dynamic classes in generated KV.
|
|
172
|
+
- Never use `#:set` to expose Python functions or generated text to KV.
|
|
173
|
+
- Never call `Builder.unload_file(...)` for generated inline KV snippets.
|
|
174
|
+
- If code defines a `KV = """..."""` string for the UI, it must actually load
|
|
175
|
+
that string with `Builder.load_string(KV)` and add the loaded widget, or omit
|
|
176
|
+
the KV string entirely. Do not define KV and then instantiate a bare Python
|
|
177
|
+
widget class such as `ui = MyWidget()`; that ignores the KV tree and usually
|
|
178
|
+
shows an empty UI.
|
|
179
|
+
|
|
180
|
+
Kivy property compatibility:
|
|
181
|
+
- Generated Kivy code must use only valid property option values in both KV and Python-created widgets. For `Label.shorten_from`, use only `"left"`, `"center"`, or `"right"`.
|
|
182
|
+
|
|
183
|
+
KV imports:
|
|
184
|
+
- Any Python name used inside KV expressions must either be a KV local such as
|
|
185
|
+
`self`, `root`, or an explicitly imported name declared with `#:import` at
|
|
186
|
+
the top of the KV string.
|
|
187
|
+
- Do not assume Python imports outside the KV string are visible to the KV
|
|
188
|
+
parser. `from math import sin` in Python does not make `sin(...)` valid inside
|
|
189
|
+
KV; use `#:import sin math.sin` in the KV string or move the calculation into
|
|
190
|
+
Python.
|
|
191
|
+
- If KV uses `dp(...)` or `sp(...)`, include exactly one matching import at the
|
|
192
|
+
top of the KV string:
|
|
193
|
+
`#:import dp kivy.metrics.dp`
|
|
194
|
+
`#:import sp kivy.metrics.sp`
|
|
195
|
+
- If KV expressions use math functions, include explicit imports such as:
|
|
196
|
+
`#:import sin math.sin`
|
|
197
|
+
`#:import cos math.cos`
|
|
198
|
+
- Do not generate duplicate `#:import` lines for the same name.
|
|
199
|
+
- Prefer moving nontrivial calculations into Python properties or Python-side
|
|
200
|
+
canvas updates instead of putting complex formulas in KV. This is especially
|
|
201
|
+
important for animated positions, trigonometry, paths, query results, and
|
|
202
|
+
generated lists.
|
|
203
|
+
- If a KV canvas expression still uses trigonometry, the KV string must include
|
|
204
|
+
the math imports once, before any widget rules:
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
KV = """
|
|
208
|
+
#:import sin math.sin
|
|
209
|
+
#:import cos math.cos
|
|
210
|
+
#:import dp kivy.metrics.dp
|
|
211
|
+
|
|
212
|
+
FloatLayout:
|
|
213
|
+
canvas:
|
|
214
|
+
Ellipse:
|
|
215
|
+
size: dp(24), dp(24)
|
|
216
|
+
pos: self.x + self.width * sin(root.phase), self.y
|
|
217
|
+
"""
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
- Before returning code, scan the KV string for function calls such as `sin(`,
|
|
221
|
+
`cos(`, `dp(`, `sp(`, `rgba(`, or helper names and ensure each helper is
|
|
222
|
+
defined in KV parser scope or removed.
|
|
223
|
+
|
|
224
|
+
KV safety additions:
|
|
225
|
+
- `ids` exist on the loaded root widget, not as global variables. Access them as
|
|
226
|
+
`ui.ids.some_id` after `Builder.load_string(KV)` returns a concrete root
|
|
227
|
+
widget.
|
|
228
|
+
- Avoid assigning duplicate ids in generated KV.
|
|
229
|
+
- Do not create dynamic styling aliases such as `<PoemLabel@Label>:`. For
|
|
230
|
+
generated snippets, repeat simple properties, use a real Python class, or set
|
|
231
|
+
properties from Python after loading the widget tree.
|
|
232
|
+
- Keep KV expressions literal and simple. Use Python to compute all lists,
|
|
233
|
+
paths, formatted labels, colors derived from runtime state, and callback
|
|
234
|
+
decisions after the widget tree is loaded.
|
|
235
|
+
- Do not use `root` in KV to refer to the PythonHere global `root`. In KV,
|
|
236
|
+
`root` means the current KV rule/root widget.
|
|
237
|
+
- When a custom root class owns Kivy properties, define the class in Python
|
|
238
|
+
before `Builder.load_string(KV)` and end KV with a concrete instance of that
|
|
239
|
+
class.
|