Skip to content

Commit 0a5c630

Browse files
Update docs
1 parent be5a86f commit 0a5c630

8 files changed

Lines changed: 557 additions & 42 deletions

File tree

docs/apps/built-in-apps.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ MicroPythonOS includes essential apps to bootstrap the system, located in [`/bui
66
- **WiFi**: Configures WiFi connections.
77
- **AppStore**: Downloads and installs new apps.
88
- **OSUpdate**: Manages Over-The-Air (OTA) system updates.
9-
- **Settings**: Configuration for MicroPythonOS
9+
- **Settings**: Configuration for MicroPythonOS.
10+
- **File Manager**: Browses the filesystem and opens files with the appropriate viewer.
1011

1112
## Screenshots
1213

docs/apps/creating-apps.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,61 @@ Each service entry has:
9595

9696
Services that subscribe to `"boot_completed"` are started automatically during system boot, after the launcher is displayed. See the [Service documentation](../frameworks/service.md) for details on writing and using services.
9797

98+
## Handling the view action
99+
100+
Apps can register themselves as file viewers by declaring an `intent_filter` with `action: "view"` and a `pathPattern` list in `MANIFEST.JSON`. This lets other apps (and the built-in `FileExplorerActivity`) open files with your app.
101+
102+
### Manifest example
103+
104+
```json
105+
{
106+
"fullname": "com.example.imageviewer",
107+
"name": "Image Viewer",
108+
"version": "1.0.0",
109+
"activities": [
110+
{
111+
"entrypoint": "imageview.py",
112+
"classname": "ImageView",
113+
"intent_filters": [
114+
{ "action": "main", "category": "launcher" },
115+
{ "action": "view", "mimeType": "image/*", "pathPattern": [".png", ".jpg", ".jpeg"] }
116+
]
117+
}
118+
]
119+
}
120+
```
121+
122+
`pathPattern` entries are matched case-insensitively against the file extension. A leading `*` is optional. `mimeType` is recorded for documentation but is not used for matching.
123+
124+
### Receiving the file
125+
126+
When the system opens your activity via the `view` action, the file path is available in `intent.data` and also in the `filename` extra:
127+
128+
```python
129+
from mpos import Activity
130+
131+
class ImageView(Activity):
132+
def onResume(self, screen):
133+
super().onResume(screen)
134+
path = self.getIntent().extras.get("filename") or self.getIntent().data
135+
if path:
136+
self.open_image(path)
137+
```
138+
139+
### Opening a file from your app
140+
141+
To open a file in whatever app is registered for it, send a `view` intent:
142+
143+
```python
144+
from mpos import Intent, Activity
145+
146+
class MyActivity(Activity):
147+
def on_file_click(self, path):
148+
self.startActivity(Intent(action="view", data=path))
149+
```
150+
151+
If multiple apps can handle the file type, MicroPythonOS automatically shows an "Open with" chooser.
152+
98153
## Icon
99154

100155
The icon is a simple 64x64 pixel PNG image named `icon_64x64.png` in the app root, which you can create with any tool, such as GIMP.

docs/architecture/frameworks.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,33 @@ if ConnectivityManager.is_online():
198198
print("Connected to network")
199199
```
200200

201+
### FileExplorerActivity
202+
Reusable file browser and picker activity. Can browse the filesystem or return selected files to the caller via `startActivityForResult`.
203+
204+
```python
205+
from mpos import Intent, FileExplorerActivity, Activity
206+
207+
class MyActivity(Activity):
208+
def pick_file(self):
209+
intent = Intent(action="pick_file")
210+
intent.putExtra("mode", "pick")
211+
intent.putExtra("path_pattern", [".wav"])
212+
self.startActivityForResult(intent, self.on_file_picked)
213+
```
214+
215+
See [FileExplorerActivity](../frameworks/file-explorer-activity.md) for details.
216+
217+
### Focus Borders
218+
Provides a single helper, `add_focus_border`, for drawing a focus border around any widget. It replaces the duplicated `FOCUSED`/`DEFOCUSED` handlers that used to exist in many apps.
219+
220+
```python
221+
from mpos import add_focus_border
222+
223+
add_focus_border(button, width=2)
224+
```
225+
226+
See [Focus Borders](../frameworks/focus.md) for details.
227+
201228
### CameraManager
202229
Provides access to camera hardware.
203230

docs/architecture/intents.md

Lines changed: 163 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,61 @@ class HomeActivity(Activity):
8080
# Or launches directly if only one handler
8181
```
8282

83+
### File-type intents and the view action
84+
85+
A common implicit intent is the `view` action, which asks the system to open a file with the most appropriate app. Apps declare which file types they can open by adding an `intent_filter` with `action: "view"` and a `pathPattern` list to their manifest. `pathPattern` entries are matched case-insensitively against the file extension; a leading `*` is optional.
86+
87+
**Example manifest declaring an image viewer:**
88+
89+
```json
90+
{
91+
"fullname": "com.example.imageviewer",
92+
"name": "Image Viewer",
93+
"version": "1.0.0",
94+
"activities": [
95+
{
96+
"entrypoint": "imageview.py",
97+
"classname": "ImageView",
98+
"intent_filters": [
99+
{ "action": "main", "category": "launcher" },
100+
{ "action": "view", "mimeType": "image/*", "pathPattern": [".png", ".jpg", ".jpeg", ".raw"] }
101+
]
102+
}
103+
]
104+
}
105+
```
106+
107+
**Opening a file from another app:**
108+
109+
```python
110+
from mpos import Intent, Activity
111+
112+
class GalleryActivity(Activity):
113+
def on_photo_selected(self, path):
114+
self.startActivity(Intent(action="view", data=path))
115+
```
116+
117+
**Receiving a file in the target app:**
118+
119+
```python
120+
from mpos import Activity
121+
122+
class ImageView(Activity):
123+
def onResume(self, screen):
124+
path = self.getIntent().extras.get("filename") or self.getIntent().data
125+
if path:
126+
self.load_image(path)
127+
```
128+
129+
When a `view` intent is fired:
130+
131+
1. `AppManager.resolve_activity()` looks for installed apps whose manifest `pathPattern` matches the file path.
132+
2. If one or more specific handlers match, those handlers are returned.
133+
3. If multiple handlers match, `ChooserActivity` shows an "Open with" dialog.
134+
4. If no specific handler matches, the system falls back to generic handlers registered for `view`, such as the framework's built-in `ViewActivity`.
135+
136+
The framework `ViewActivity` provides a last-resort preview for unknown files by reading and displaying the first 512 bytes as text.
137+
83138
## Intent Class Reference
84139

85140
### Constructor
@@ -342,13 +397,24 @@ class HomeActivity(Activity):
342397

343398
## AppManager Intent Resolution
344399

345-
The `AppManager` maintains a registry of activities and their associated actions, enabling implicit intent resolution.
400+
The `AppManager` maintains a registry of activities and their associated actions, enabling implicit intent resolution. Resolution works for both programmatically registered handlers and handlers declared in app manifests.
346401

347402
**Location:** [`MicroPythonOS/internal_filesystem/lib/mpos/content/app_manager.py`](https://github.com/MicroPythonOS/MicroPythonOS/blob/main/internal_filesystem/lib/mpos/content/app_manager.py)
348403

404+
### HandlerInfo
405+
406+
`resolve_activity()` returns a list of `HandlerInfo` objects rather than raw activity classes:
407+
408+
| Attribute | Type | Purpose |
409+
|-----------|------|---------|
410+
| `activity_class` | `type` | The `Activity` subclass that can handle the intent. |
411+
| `app_fullname` | `str` or `None` | The owning app's fullname for manifest-declared handlers, or `None` for framework handlers. |
412+
413+
Use `handler.activity_class` when inspecting a resolved handler and `handler.app_fullname` when you need to know which installed app provided it.
414+
349415
### `register_activity(action, activity_cls)`
350416

351-
Register an activity to handle a specific action.
417+
Register an activity programmatically to handle a specific action. This is useful for framework activities and dynamic handlers.
352418

353419
**Parameters:**
354420

@@ -363,35 +429,58 @@ from mpos import AppManager
363429
# Register handlers for SEND action
364430
AppManager.register_activity("android.intent.action.SEND", ShareActivity)
365431
AppManager.register_activity("android.intent.action.SEND", EmailActivity)
432+
```
366433

367-
# Register handler for VIEW action
368-
AppManager.register_activity("android.intent.action.VIEW", BrowserActivity)
434+
### Manifest-based file handlers
435+
436+
Apps declare file-type handlers in `MANIFEST.JSON`. When an implicit intent carries a string `data` payload (usually a file path), MicroPythonOS preferentially returns handlers whose `pathPattern` matches the path.
437+
438+
```json
439+
{
440+
"activities": [
441+
{
442+
"entrypoint": "player.py",
443+
"classname": "Player",
444+
"intent_filters": [
445+
{ "action": "view", "mimeType": "audio/wav", "pathPattern": [".wav"] }
446+
]
447+
}
448+
]
449+
}
369450
```
370451

452+
- `pathPattern` may be a single string or a list of strings.
453+
- Matching is case-insensitive and suffix-based; `"*.wav"` and `".wav"` are equivalent.
454+
- `mimeType` is recorded but is not currently used for matching.
455+
371456
### `resolve_activity(intent)`
372457

373-
Find all activities that handle an intent's action.
458+
Find all handlers that can handle an intent's action, with file-type preference when `intent.data` is a path.
374459

375460
**Parameters:**
376461

377-
- `intent` (Intent): Intent with action attribute
462+
- `intent` (Intent): Intent with `action` attribute
378463

379-
**Returns:** List of Activity classes (empty if no handlers)
464+
**Returns:** List of `HandlerInfo` objects (empty if no handlers)
465+
466+
**Resolution order:**
467+
468+
1. If `intent.data` is a string and one or more manifest file handlers match its extension, return only those handlers.
469+
2. If no file handler matches, return all generic handlers registered for the action.
470+
3. If the list contains multiple handlers, the framework shows `ChooserActivity`.
380471

381472
**Example:**
382473

383474
```python
384475
from mpos import Intent, AppManager
385476

386-
intent = Intent(action="android.intent.action.SEND")
477+
intent = Intent(action="view", data="/data/audio/song.wav")
387478
handlers = AppManager.resolve_activity(intent)
388479

389-
if len(handlers) == 0:
390-
print("No handler for SEND action")
391-
elif len(handlers) == 1:
392-
print(f"Single handler: {handlers[0]}")
393-
else:
394-
print(f"Multiple handlers: {handlers}")
480+
for handler in handlers:
481+
print(f"Handler: {handler.activity_class.__name__}")
482+
if handler.app_fullname:
483+
print(f" from app: {handler.app_fullname}")
395484
```
396485

397486
### `query_intent_activities(intent)`
@@ -400,16 +489,16 @@ Android-compatible alias for `resolve_activity()`. Identical behavior.
400489

401490
**Parameters:**
402491

403-
- `intent` (Intent): Intent with action attribute
492+
- `intent` (Intent): Intent object with `action` attribute
404493

405-
**Returns:** List of Activity classes
494+
**Returns:** List of `HandlerInfo` objects
406495

407496
**Example:**
408497

409498
```python
410499
from mpos import Intent, AppManager
411500

412-
intent = Intent(action="android.intent.action.SEND")
501+
intent = Intent(action="view", data="/data/notes.txt")
413502
handlers = AppManager.query_intent_activities(intent)
414503
```
415504

@@ -548,7 +637,37 @@ class HomeActivity(Activity):
548637

549638
---
550639

551-
### Pattern 5: Method Chaining
640+
### Pattern 5: Open a File with the View Action
641+
642+
Use the `view` action to open a file in the app that is registered for its type. If more than one app can handle the type, the system shows an "Open with" chooser.
643+
644+
```python
645+
from mpos import Intent, Activity
646+
647+
class FileListActivity(Activity):
648+
def on_file_click(self, path):
649+
self.startActivity(Intent(action="view", data=path))
650+
```
651+
652+
In the receiving app, read the file path from `intent.data` or from the `filename` extra:
653+
654+
```python
655+
class ImageView(Activity):
656+
def onResume(self, screen):
657+
path = self.getIntent().extras.get("filename") or self.getIntent().data
658+
if path:
659+
self.load_image(path)
660+
```
661+
662+
**Key Points:**
663+
- The file path is passed in `intent.data`.
664+
- Apps declare supported extensions in `MANIFEST.JSON` with `action: "view"` and `pathPattern`.
665+
- `ChooserActivity` is shown automatically when multiple handlers exist.
666+
- The framework provides a fallback `ViewActivity` for unhandled file types.
667+
668+
---
669+
670+
### Pattern 6: Method Chaining
552671

553672
Use fluent API for readable intent construction.
554673

@@ -573,7 +692,7 @@ class HomeActivity(Activity):
573692

574693
---
575694

576-
### Pattern 6: Complex Data Passing
695+
### Pattern 7: Complex Data Passing
577696

578697
Pass complex objects and references between activities.
579698

@@ -797,12 +916,29 @@ item_id = intent.extras["item_id"] # KeyError if missing
797916

798917
Register implicit intent handlers during app initialization.
799918

919+
**Programmatic registration** is useful for framework activities and dynamic handlers:
920+
800921
```python
801922
# In app initialization
802923
from mpos import AppManager
803924

804925
AppManager.register_activity("android.intent.action.SEND", ShareActivity)
805-
AppManager.register_activity("android.intent.action.VIEW", BrowserActivity)
926+
```
927+
928+
**Manifest registration** is preferred for file-type handlers. Add the `intent_filters` to `MANIFEST.JSON` so `AppManager.refresh_apps()` discovers the handler automatically:
929+
930+
```json
931+
{
932+
"activities": [
933+
{
934+
"entrypoint": "player.py",
935+
"classname": "Player",
936+
"intent_filters": [
937+
{ "action": "view", "pathPattern": [".wav"] }
938+
]
939+
}
940+
]
941+
}
806942
```
807943

808944
---
@@ -852,9 +988,9 @@ MicroPythonOS Intents are inspired by Android's Intent system but simplified for
852988
| **Implicit Intents** | ✅ Supported | ✅ Supported | Action-based routing |
853989
| **Intent Extras** | ✅ Dict-based | ✅ Bundle-based | MicroPythonOS simpler |
854990
| **Intent Flags** | ⚠️ Partial | ✅ Full | Limited flag support |
855-
| **Intent Filters** | ❌ Programmatic only | ✅ In manifest | No manifest-based registration |
991+
| **Intent Filters** | ✅ Manifest + programmatic | ✅ In manifest | File-type filters in `MANIFEST.JSON`, generic handlers via code |
856992
| **Categories** | ❌ Not supported | ✅ Supported | Simplified routing |
857-
| **Data Types** | ❌ Not matched | ✅ MIME types | No type filtering |
993+
| **Data Types** | ⚠️ Path patterns | ✅ MIME types | `pathPattern` suffix matching; `mimeType` is stored but not used for matching |
858994
| **URI Schemes** | ❌ Not matched | ✅ Supported | No scheme filtering |
859995
| **Chooser UI** | ✅ ChooserActivity | ✅ Intent chooser | Custom implementation |
860996
| **Result Callbacks** | ✅ Callback-based | ✅ onActivityResult() | Different mechanism |
@@ -864,9 +1000,9 @@ MicroPythonOS Intents are inspired by Android's Intent system but simplified for
8641000

8651001
**Simplified Intent Filters:**
8661002

867-
- MicroPythonOS uses programmatic registration via `AppManager.register_activity()`
868-
- Android uses manifest-based intent filters
869-
- MicroPythonOS approach is more flexible for dynamic app loading
1003+
- MicroPythonOS supports both programmatic registration via `AppManager.register_activity()` and manifest-based file-type filters in `MANIFEST.JSON`
1004+
- Android uses manifest-based intent filters exclusively
1005+
- MicroPythonOS approach is more flexible for dynamic app loading while still allowing apps to declare file handlers declaratively
8701006

8711007
**Callback-Based Results:**
8721008

@@ -893,4 +1029,6 @@ The Intent system is implemented across these core files:
8931029

8941030
- [App Lifecycle](../apps/app-lifecycle.md) - Activity lifecycle and Intent basics
8951031
- [AppManager](../frameworks/app-manager.md) - Intent resolution and activity registration
1032+
- [FileExplorerActivity](../frameworks/file-explorer-activity.md) - Browsing files and sending `view` intents
1033+
- [Focus Borders](../frameworks/focus.md) - Reusable focus highlighting for intent-driven UIs
8961034
- [SettingActivity](../frameworks/setting-activity.md) - Intent extras for settings configuration

0 commit comments

Comments
 (0)