You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/apps/app-lifecycle.md
+32-1Lines changed: 32 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -140,6 +140,32 @@ def onPause(self, screen):
140
140
141
141
**Important:** Call `super().onPause(screen)` to properly track foreground state.
142
142
143
+
### onBackPressed(screen)
144
+
145
+
Called when the user performs the back/close gesture (e.g. swiping from the left edge) **before** the activity is paused. This is the right place to ask the user for confirmation when there is unsaved work.
146
+
147
+
Return `True` to consume the event and keep the activity in the foreground. In that case the activity is responsible for calling `finish()` later when it is ready to close. Return `False` (the default) to let the framework finish the activity normally.
148
+
149
+
```python
150
+
defonBackPressed(self, screen):
151
+
ifself._has_unsaved_changes():
152
+
# Show a dialog; return True so the activity stays alive
153
+
self._show_exit_confirm()
154
+
returnTrue
155
+
returnFalse
156
+
```
157
+
158
+
In the dialog callback, call `self.finish()` to actually close the activity:
159
+
160
+
```python
161
+
def_on_exit_confirmed(self, dialog):
162
+
dialog.close()
163
+
self._save_changes()
164
+
self.finish()
165
+
```
166
+
167
+
**Important:**`finish()` does **not** call `onBackPressed()` again, so your dialog callbacks can safely call it.
168
+
143
169
### onStop(screen)
144
170
145
171
Called when the activity is no longer visible (fully covered by another activity).
@@ -189,7 +215,7 @@ Activity Stack:
189
215
190
216
1.**Starting a new activity:** Current activity receives `onPause()` → `onStop()`, new activity receives `onCreate()` → `onStart()` → `onResume()`
2.**Going back:**Framework first calls `onBackPressed()`. If it returns `True`, the activity stays foreground and must call `finish()` itself when ready. If it returns `False`, the current activity receives `onPause()` → `onStop()` → `onDestroy()`, and the previous activity receives `onResume()`
193
219
194
220
## Starting Activities
195
221
@@ -404,6 +430,8 @@ class NetworkAwareActivity(Activity):
404
430
✅ Check `has_foreground()` before updating UI from async operations
405
431
✅ Use `setContentView()` at the end of `onCreate()`
406
432
✅ Clean up resources in `onDestroy()`
433
+
✅ Use `onBackPressed()` to ask before discarding unsaved changes
434
+
407
435
408
436
### Don'ts
409
437
@@ -412,6 +440,8 @@ class NetworkAwareActivity(Activity):
412
440
❌ Don't update UI from background threads without `update_ui_threadsafe_if_foreground()`
413
441
❌ Don't assume the activity is still visible after async operations
414
442
❌ Don't store references to LVGL objects after `onDestroy()`
443
+
❌ Don't show back-navigation confirmation dialogs in `onPause()` - use `onBackPressed()` instead
444
+
415
445
416
446
## Services — Background Components
417
447
@@ -460,6 +490,7 @@ Services are declared either programmatically (system services) or via `"service
Copy file name to clipboardExpand all lines: docs/apps/appstore.md
+4Lines changed: 4 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,6 +11,10 @@ App discovery is currently done by downloading the app list from [apps.micropyth
11
11
-**Image Viewer**: Displays images stored in `/data/images/`.
12
12
-**IMU**: Visualize data from the Intertial Measurement Unit, also known as the accellerometer.
13
13
14
+
## Image Viewer
15
+
16
+
The **Image Viewer** app displays images stored in `/data/images/`. See [Supported File Formats](../other/supported-file-formats.md) for the list of image formats the OS can decode.
Copy file name to clipboardExpand all lines: docs/frameworks/font-manager.md
+40-13Lines changed: 40 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,13 +1,13 @@
1
1
# FontManager
2
2
3
-
FontManager is a singleton framework for loading, caching, and composing LVGL fonts — including built-in bitmap fonts, TrueType fonts, and emoji image fonts. It uses LVGL's imgfont fallback mechanism to render 20×20 emoji PNGs inline with text, with nearest-neighbour scaling to match any font size.
3
+
FontManager is a singleton framework for loading, caching, and composing LVGL fonts — including built-in bitmap fonts, TrueType fonts, and emoji image fonts. It uses LVGL's imgfont fallback mechanism to render 32×32 emoji PNGs inline with text, with nearest-neighbour scaling to match any font size.
4
4
5
5
## Overview
6
6
7
7
FontManager centralizes all font concerns in a single class:
8
8
9
9
-**Unified API** - One call (`getFont`) to get any font, with or without emoji support
10
-
-**Emoji Compositing** - Transparently layers 20×20 emoji PNGs via LVGL's imgfont fallback, with nearest-neighbour scaling to match any font size
10
+
-**Emoji Compositing** - Transparently layers 32×32 emoji PNGs via LVGL's imgfont fallback, with nearest-neighbour scaling to match any font size
11
11
-**Lazy Caching** - Fonts and scaled image descriptors are cached on first use; no redundant work on subsequent calls
12
12
-**Android-Inspired** - Follows the same singleton/class-method pattern as other MicroPythonOS frameworks
# Get all available emoji codepoints (base codepoints only)
33
33
for cp in FontManager.getEmojiCodepoints():
34
34
print(hex(cp))
35
+
36
+
# Get all available emoji strings (full sequences, including flag pairs)
37
+
for s in FontManager.getEmojiStrings():
38
+
print(s)
35
39
```
36
40
37
41
## Architecture
@@ -56,17 +60,18 @@ Emoji PNGs are stored in `builtin/res/emojis/`:
56
60
57
61
| Directory | Used for |
58
62
|-----------|----------|
59
-
|`20x20/`| All fonts — emoji are rendered at 20×20 px and nearest-neighbour scaled up or down by LVGL as needed |
63
+
|`32x32/`| All fonts — emoji are rendered at 32×32 px and nearest-neighbour scaled up or down by LVGL as needed |
60
64
61
-
`_imgfont_path_cb` receives the rendering font's pixel height and returns the 20×20 emoji source. LVGL's software renderer performs nearest-neighbour scaling to fit the target font size.
65
+
`_imgfont_path_cb` receives the rendering font's pixel height and returns the 32×32 emoji source. LVGL's software renderer performs nearest-neighbour scaling to fit the target font size.
@@ -128,7 +133,9 @@ for info in FontManager.listFonts(emojis=True):
128
133
129
134
### `getEmojiCodepoints()`
130
135
131
-
Return a sorted list of all available emoji codepoints.
136
+
Return a sorted list of the base emoji codepoints available in the emoji map.
137
+
138
+
For multi-codepoint emoji such as flag sequences (e.g. `"🇸🇻"`), only the first codepoint is returned. Use `getEmojiStrings()` when you need complete, renderable emoji sequences.
132
139
133
140
**Returns:** list of int
134
141
@@ -143,6 +150,25 @@ for cp in FontManager.getEmojiCodepoints():
143
150
144
151
---
145
152
153
+
### `getEmojiStrings()`
154
+
155
+
Return a sorted list of all available, complete emoji strings.
156
+
157
+
Unlike `getEmojiCodepoints()`, this returns full sequences: flag emoji include both regional indicators, and emoji with variation selectors keep their trailing selector. This is the preferred API for building a visual list of every supported emoji.
158
+
159
+
**Returns:** list of str
160
+
161
+
**Example:**
162
+
163
+
```python
164
+
from mpos import FontManager
165
+
166
+
for s in FontManager.getEmojiStrings():
167
+
print(s)
168
+
```
169
+
170
+
---
171
+
146
172
### `normalizeEmojiText(text)`
147
173
148
174
Strip Unicode variation selectors (U+FE0E text selector, U+FE0F emoji selector) from a string. Useful before storing or comparing text that may have been pasted from a source that appends these codepoints.
@@ -167,20 +193,21 @@ To keep firmware image size small, the OS bundles ~50 of the most frequently-use
167
193
168
194
```
169
195
builtin/res/emojis/
170
-
└── 20x20/ # Pre-rendered at 20×20 px
196
+
└── 32x32/ # Pre-rendered at 32×32 px
171
197
├── 1F600.png
172
-
├── 263A.png
198
+
├── 1F3CE-FE0F.png
199
+
├── 1F1F8-1F1FB.png
173
200
└── ...
174
201
```
175
202
176
-
Files are named by their Unicode codepoint in uppercase hex(e.g. `1F600.png` for 😀). FontManager scans the directory at runtime and builds a `{codepoint: path}` map.
203
+
Files are named by their Unicode codepoint(s) in uppercase hex, with multiple codepoints joined by `-`(e.g. `1F600.png` for 😀, `1F3CE-FE0F.png` for 🏎️, `1F1F8-1F1FB.png` for 🇸🇻). FontManager scans the directory at runtime and builds a `{codepoint: path}` map.
177
204
178
205
### Adding new emoji
179
206
180
-
1. Add a PNG named `<CODEPOINT_HEX>.png` to `20x20/`:
207
+
1. Add a PNG named `<CODEPOINT_HEX>.png` to `32x32/`:
181
208
182
209
```bash
183
-
cp original.png 20x20/CODEPOINT.png
210
+
cp original.png 32x32/CODEPOINT.png
184
211
```
185
212
186
213
2. The new emoji will be picked up automatically on next boot — no code changes needed.
MicroPythonOS uses LVGL's built-in image decoders and its own audio stack. The formats below are supported out of the box for apps such as the Image Viewer and Music Player.
4
+
5
+
## Image formats
6
+
7
+
| Format | Extensions | Notes |
8
+
|--------|------------|-------|
9
+
| PNG |`.png`| Fully supported via LodePNG. |
10
+
| Baseline JPEG |`.jpg`, `.jpeg`| Fully supported via TJpgD. |
11
+
| Progressive JPEG |`.jpg`, `.jpeg`|**Not supported.** Files will decode as `0x0` and appear blank. |
12
+
| RAW |`.raw`| Supported by the Image Viewer app if named as `<name>_<w>x<h>_RGB565.raw` or `<name>_<w>x<h>_GRAY.raw`. |
13
+
14
+
## Audio formats
15
+
16
+
| Format | Extensions | Notes |
17
+
|--------|------------|-------|
18
+
| Plain PCM WAV |`.wav`| Standard RIFF/WAVE with `WAVE_FORMAT_PCM` (0x0001) or `WAVE_FORMAT_EXTENSIBLE` (0xFFFE) and 16-bit samples. |
19
+
| IMA ADPCM WAV |`.wav`| RIFF/WAVE with `WAVE_FORMAT_ADPCM` (0x0011), decoded by the built-in `adpcm_ima` module. Must have 4 or 16 bits per sample. |
20
+
21
+
### JPEG conversion
22
+
23
+
The built-in JPEG decoder only supports baseline JPEGs. The file extension is also treated case-sensitively, so `.JPG` or `.JPEG` will **not** be recognised.
24
+
25
+
To convert an image to a supported baseline JPEG:
26
+
27
+
```bash
28
+
convert input.jpg -interlace none output.jpg
29
+
```
30
+
31
+
`input.jpg` can be any image format that ImageMagick supports (including PNG), and `-interlace none` forces a non-progressive JPEG.
0 commit comments