Skip to content

Commit 5342105

Browse files
Add Setting(s)Activity to the docs
1 parent b295b3b commit 5342105

File tree

4 files changed

+719
-1
lines changed

4 files changed

+719
-1
lines changed
Lines changed: 342 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
# SettingActivity
2+
3+
`SettingActivity` is used to edit a single setting with various UI options. It provides a flexible framework for collecting user input with support for text input, radio buttons, dropdowns, and custom Activity implementations.
4+
5+
## Overview
6+
7+
`SettingActivity` displays a single setting that the user can modify. The setting is passed via Intent extras and can be configured with different UI types. When the user saves the setting, it's automatically persisted to SharedPreferences (unless `dont_persist` is set to true).
8+
9+
## Basic Usage
10+
11+
```python
12+
from mpos import Activity, Intent, SettingActivity, SharedPreferences
13+
14+
class MyApp(Activity):
15+
def onCreate(self):
16+
self.prefs = SharedPreferences("com.example.myapp")
17+
# ... create UI ...
18+
19+
def open_setting(self):
20+
intent = Intent(activity_class=SettingActivity)
21+
intent.putExtra("prefs", self.prefs)
22+
intent.putExtra("setting", {
23+
"title": "Your Name",
24+
"key": "user_name",
25+
"placeholder": "Enter your name"
26+
})
27+
self.startActivity(intent)
28+
```
29+
30+
## Setting Dictionary Structure
31+
32+
Each setting is defined as a dictionary with the following properties:
33+
34+
### Required Properties
35+
- **`title`** (string): Display name of the setting shown at the top
36+
- **`key`** (string): Unique identifier used as the SharedPreferences key
37+
38+
### Optional Properties
39+
- **`ui`** (string): UI type to use for editing. Options: `"textarea"` (default), `"radiobuttons"`, `"dropdown"`, `"activity"`
40+
- **`ui_options`** (list): Options for `radiobuttons` and `dropdown` UI types
41+
- **`placeholder`** (string): Placeholder text for textarea input
42+
- **`changed_callback`** (function): Callback function called when the setting value changes
43+
- **`should_show`** (function): Function to determine if this setting should be displayed
44+
- **`dont_persist`** (bool): If `True`, the setting won't be saved to SharedPreferences
45+
- **`activity_class`** (class): Custom Activity class for `"activity"` UI type
46+
- **`value_label`** (widget): Internal reference to the value label (set by SettingsActivity)
47+
- **`cont`** (widget): Internal reference to the container (set by SettingsActivity)
48+
49+
## UI Types
50+
51+
### 1. Textarea (Default)
52+
53+
Text input with an on-screen keyboard and optional QR code scanner.
54+
55+
**When to use:** For text input like URLs, API keys, names, etc.
56+
57+
**Example:**
58+
```python
59+
{
60+
"title": "API Key",
61+
"key": "api_key",
62+
"placeholder": "Enter your API key",
63+
"ui": "textarea" # or omit this, textarea is default
64+
}
65+
```
66+
67+
**Features:**
68+
- On-screen keyboard for text input
69+
- QR code scanner button (for scanning data from QR codes)
70+
- Placeholder text support
71+
- Single-line input
72+
73+
### 2. Radio Buttons
74+
75+
Mutually exclusive selection from a list of options.
76+
77+
**When to use:** For choosing one option from a small set of choices.
78+
79+
**Format for `ui_options`:**
80+
```python
81+
ui_options = [
82+
("Display Label 1", "value1"),
83+
("Display Label 2", "value2"),
84+
("Display Label 3", "value3")
85+
]
86+
```
87+
88+
**Example:**
89+
```python
90+
{
91+
"title": "Theme",
92+
"key": "theme",
93+
"ui": "radiobuttons",
94+
"ui_options": [
95+
("Light", "light"),
96+
("Dark", "dark"),
97+
("Auto", "auto")
98+
]
99+
}
100+
```
101+
102+
**Features:**
103+
- Circular radio button indicators
104+
- Only one option can be selected at a time
105+
- Displays both label and value (if different)
106+
107+
### 3. Dropdown
108+
109+
Dropdown selection from a list of options.
110+
111+
**When to use:** For choosing one option from a larger set of choices.
112+
113+
**Format for `ui_options`:**
114+
```python
115+
ui_options = [
116+
("Display Label 1", "value1"),
117+
("Display Label 2", "value2"),
118+
("Display Label 3", "value3")
119+
]
120+
```
121+
122+
**Example:**
123+
```python
124+
{
125+
"title": "Language",
126+
"key": "language",
127+
"ui": "dropdown",
128+
"ui_options": [
129+
("English", "en"),
130+
("German", "de"),
131+
("French", "fr"),
132+
("Spanish", "es")
133+
]
134+
}
135+
```
136+
137+
**Features:**
138+
- Compact dropdown menu
139+
- Shows label and value if different
140+
- Automatically selects the current value
141+
142+
### 4. Custom Activity
143+
144+
Use a custom Activity class for advanced UI implementations.
145+
146+
**When to use:** For complex settings that need custom UI beyond the built-in options.
147+
148+
**Example:**
149+
```python
150+
class ColorPickerActivity(Activity):
151+
def onCreate(self):
152+
# Custom color picker UI
153+
pass
154+
155+
setting = {
156+
"title": "Pick a Color",
157+
"key": "color",
158+
"ui": "activity",
159+
"activity_class": ColorPickerActivity
160+
}
161+
```
162+
163+
**Requirements:**
164+
- Must provide `activity_class` parameter
165+
- The custom Activity receives the setting and prefs via Intent extras
166+
- Must call `self.finish()` to return to the previous screen
167+
168+
## Callbacks and Advanced Features
169+
170+
### changed_callback
171+
172+
Called when the setting value changes (after saving). Useful for triggering UI updates or reloading data.
173+
174+
**Example:**
175+
```python
176+
def on_theme_changed(new_value):
177+
print(f"Theme changed to: {new_value}")
178+
# Reload UI with new theme
179+
self.apply_theme(new_value)
180+
181+
setting = {
182+
"title": "Theme",
183+
"key": "theme",
184+
"ui": "radiobuttons",
185+
"ui_options": [("Light", "light"), ("Dark", "dark")],
186+
"changed_callback": on_theme_changed
187+
}
188+
```
189+
190+
**Important:** The callback is only called if the value actually changed (old value != new value).
191+
192+
### should_show
193+
194+
Function to conditionally show/hide a setting. Used primarily in SettingsActivity.
195+
196+
**Example:**
197+
```python
198+
def should_show_advanced_options(setting):
199+
prefs = SharedPreferences("com.example.app")
200+
return prefs.get_string("mode") == "advanced"
201+
202+
setting = {
203+
"title": "Advanced Option",
204+
"key": "advanced_setting",
205+
"should_show": should_show_advanced_options
206+
}
207+
```
208+
209+
### dont_persist
210+
211+
Prevent the setting from being saved to SharedPreferences.
212+
213+
**Example:**
214+
```python
215+
setting = {
216+
"title": "Temporary Value",
217+
"key": "temp_value",
218+
"dont_persist": True
219+
}
220+
```
221+
222+
## Complete Example: Simple Settings App
223+
224+
Here's a minimal app that uses SettingActivity to edit a single setting:
225+
226+
```python
227+
import lvgl as lv
228+
from mpos import Activity, Intent, SettingActivity, SharedPreferences
229+
230+
class SimpleSettingsApp(Activity):
231+
def onCreate(self):
232+
self.prefs = SharedPreferences("com.example.simplesettings")
233+
234+
# Create main screen
235+
self.main_screen = lv.obj()
236+
self.main_screen.set_flex_flow(lv.FLEX_FLOW.COLUMN)
237+
238+
# Display current value
239+
self.value_label = lv.label(self.main_screen)
240+
self.update_value_label()
241+
242+
# Settings button
243+
settings_btn = lv.button(self.main_screen)
244+
settings_btn.set_size(lv.pct(80), lv.SIZE_CONTENT)
245+
settings_label = lv.label(settings_btn)
246+
settings_label.set_text("Edit Setting")
247+
settings_label.center()
248+
settings_btn.add_event_cb(self.open_settings, lv.EVENT.CLICKED, None)
249+
250+
self.setContentView(self.main_screen)
251+
252+
def onResume(self, screen):
253+
super().onResume(screen)
254+
self.update_value_label()
255+
256+
def update_value_label(self):
257+
value = self.prefs.get_string("my_setting", "(not set)")
258+
self.value_label.set_text(f"Current value: {value}")
259+
260+
def open_settings(self, event):
261+
intent = Intent(activity_class=SettingActivity)
262+
intent.putExtra("prefs", self.prefs)
263+
intent.putExtra("setting", {
264+
"title": "My Setting",
265+
"key": "my_setting",
266+
"placeholder": "Enter a value",
267+
"changed_callback": self.on_setting_changed
268+
})
269+
self.startActivity(intent)
270+
271+
def on_setting_changed(self, new_value):
272+
print(f"Setting changed to: {new_value}")
273+
self.update_value_label()
274+
```
275+
276+
## Real-World Example: AppStore Backend Selection
277+
278+
This example is inspired by the AppStore app, showing how to use SettingActivity with radio buttons and a callback:
279+
280+
```python
281+
import lvgl as lv
282+
from mpos import Activity, Intent, SettingActivity, SharedPreferences
283+
284+
class AppStore(Activity):
285+
BACKENDS = [
286+
("MPOS GitHub", "github,https://apps.micropythonos.com/app_index.json"),
287+
("BadgeHub Test", "badgehub,https://badgehub.p1m.nl/api/v3"),
288+
("BadgeHub Prod", "badgehub,https://badge.why2025.org/api/v3")
289+
]
290+
291+
def onCreate(self):
292+
self.prefs = SharedPreferences("com.micropythonos.appstore")
293+
294+
self.main_screen = lv.obj()
295+
self.main_screen.set_flex_flow(lv.FLEX_FLOW.COLUMN)
296+
297+
# Settings button
298+
settings_btn = lv.button(self.main_screen)
299+
settings_btn.set_size(lv.pct(20), lv.pct(25))
300+
settings_btn.align(lv.ALIGN.TOP_RIGHT, 0, 0)
301+
settings_label = lv.label(settings_btn)
302+
settings_label.set_text(lv.SYMBOL.SETTINGS)
303+
settings_label.center()
304+
settings_btn.add_event_cb(self.open_backend_settings, lv.EVENT.CLICKED, None)
305+
306+
self.setContentView(self.main_screen)
307+
308+
def open_backend_settings(self, event):
309+
intent = Intent(activity_class=SettingActivity)
310+
intent.putExtra("prefs", self.prefs)
311+
intent.putExtra("setting", {
312+
"title": "AppStore Backend",
313+
"key": "backend",
314+
"ui": "radiobuttons",
315+
"ui_options": self.BACKENDS,
316+
"changed_callback": self.backend_changed
317+
})
318+
self.startActivity(intent)
319+
320+
def backend_changed(self, new_value):
321+
print(f"Backend changed to {new_value}, refreshing app list...")
322+
# Trigger app list refresh with new backend
323+
self.refresh_app_list()
324+
325+
def refresh_app_list(self):
326+
# Implementation to refresh the app list
327+
pass
328+
```
329+
330+
## Tips and Best Practices
331+
332+
1. **Load prefs once in onCreate()**: Load SharedPreferences in your main Activity's `onCreate()` and pass it to SettingActivity. This is faster than loading it multiple times.
333+
334+
2. **Use changed_callback for side effects**: If changing a setting requires reloading data or updating the UI, use `changed_callback` instead of checking the value on resume.
335+
336+
3. **Validate input**: For textarea inputs, consider validating the input in your `changed_callback` before using it.
337+
338+
4. **Use radio buttons for small sets**: Use radio buttons for 2-5 options, dropdown for larger lists.
339+
340+
5. **Provide meaningful placeholders**: Help users understand what format is expected with clear placeholder text.
341+
342+
6. **Consider conditional visibility**: Use `should_show` in SettingsActivity to hide settings that don't apply based on other settings.

0 commit comments

Comments
 (0)