Skip to content

Commit 3f4ab9c

Browse files
Add settings app
1 parent c443325 commit 3f4ab9c

File tree

3 files changed

+288
-4
lines changed

3 files changed

+288
-4
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "Settings",
3+
"publisher": "MicroPythonOS",
4+
"short_description": "View and change MicroPythonOS settings.",
5+
"long_description": "This is the official settings app for MicroPythonOS. It allows you to configure all aspects of MicroPythonOS.",
6+
"icon_url": "https://apps.micropythonos.com/apps/com.micropythonos.settings/icons/com.micropythonos.settings_0.0.1_64x64.png",
7+
"download_url": "https://apps.micropythonos.com/apps/com.micropythonos.settings/mpks/com.micropythonos.settings_0.0.1.mpk",
8+
"fullname": "com.micropythonos.settings",
9+
"version": "0.0.1",
10+
"category": "development",
11+
"activities": [
12+
{
13+
"entrypoint": "assets/settings.py",
14+
"classname": "SettingsActivity",
15+
"intent_filters": [
16+
{
17+
"action": "main",
18+
"category": "launcher"
19+
}
20+
]
21+
}
22+
]
23+
}
24+
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
from mpos.apps import Activity, Intent
2+
import mpos.config
3+
import mpos.ui
4+
5+
class Hello(Activity):
6+
7+
def onCreate(self):
8+
screen = lv.obj()
9+
label = lv.label(screen)
10+
label.set_text('Hello World!')
11+
label.center()
12+
self.setContentView(screen)
13+
14+
15+
# Used to list and edit all settings:
16+
class SettingsActivity(Activity):
17+
def __init__(self):
18+
super().__init__()
19+
self.prefs = None
20+
self.settings = [
21+
{"title": "Light/Dark Theme", "key": "light_dark_theme", "value_label": None, "cont": None},
22+
{"title": "Theme Color", "key": "theme_color", "value_label": None, "cont": None, "placeholder": "HTML hex color, like: EC048C"},
23+
{"title": "Reboot into Bootloader", "key": "boot_mode", "value_label": None, "cont": None},
24+
{"title": "Display Brightness", "key": "display_brightness", "value_label": None, "cont": None, "placeholder": "A value from 0 to 100."},
25+
{"title": "Timezone", "key": "timezone", "value_label": None, "cont": None, "placeholder": "Example: Europe/Prague"},
26+
]
27+
28+
def onCreate(self):
29+
screen = lv.obj()
30+
print("creating SettingsActivity ui...")
31+
screen.set_style_pad_all(mpos.ui.pct_of_display_width(2), 0)
32+
screen.set_flex_flow(lv.FLEX_FLOW.COLUMN)
33+
screen.set_style_border_width(0, 0)
34+
self.setContentView(screen)
35+
36+
def onResume(self, screen):
37+
# reload settings because the SettingsActivity might have changed them - could be optimized to only load if it did:
38+
self.prefs = mpos.config.SharedPreferences("com.micropythonos.settings")
39+
#wallet_type = self.prefs.get_string("wallet_type") # unused
40+
41+
# Create settings entries
42+
screen.clean()
43+
for setting in self.settings:
44+
# Container for each setting
45+
setting_cont = lv.obj(screen)
46+
setting_cont.set_width(lv.pct(100))
47+
setting_cont.set_height(lv.SIZE_CONTENT)
48+
setting_cont.set_style_border_width(1, 0)
49+
setting_cont.set_style_border_side(lv.BORDER_SIDE.BOTTOM, 0)
50+
setting_cont.set_style_pad_all(mpos.ui.pct_of_display_width(2), 0)
51+
setting_cont.add_flag(lv.obj.FLAG.CLICKABLE)
52+
setting["cont"] = setting_cont # Store container reference for visibility control
53+
54+
# Title label (bold, larger)
55+
title = lv.label(setting_cont)
56+
title.set_text(setting["title"])
57+
title.set_style_text_font(lv.font_montserrat_16, 0)
58+
title.set_pos(0, 0)
59+
60+
# Value label (smaller, below title)
61+
value = lv.label(setting_cont)
62+
value.set_text(self.prefs.get_string(setting["key"], "(not set)"))
63+
value.set_style_text_font(lv.font_montserrat_12, 0)
64+
value.set_style_text_color(lv.color_hex(0x666666), 0)
65+
value.set_pos(0, 20)
66+
setting["value_label"] = value # Store reference for updating
67+
setting_cont.add_event_cb(
68+
lambda e, s=setting: self.startSettingActivity(s), lv.EVENT.CLICKED, None
69+
)
70+
71+
def startSettingActivity(self, setting):
72+
intent = Intent(activity_class=SettingActivity)
73+
intent.putExtra("setting", setting)
74+
self.startActivity(intent)
75+
76+
# Used to edit one setting:
77+
class SettingActivity(Activity):
78+
79+
keyboard = None
80+
textarea = None
81+
radio_container = None
82+
active_radio_index = 0 # Track active radio button index
83+
84+
def __init__(self):
85+
super().__init__()
86+
self.prefs = mpos.config.SharedPreferences("com.micropythonos.settings")
87+
self.setting = None
88+
89+
def onCreate(self):
90+
setting = self.getIntent().extras.get("setting")
91+
settings_screen_detail = lv.obj()
92+
settings_screen_detail.set_style_pad_all(mpos.ui.pct_of_display_width(2), 0)
93+
settings_screen_detail.set_flex_flow(lv.FLEX_FLOW.COLUMN)
94+
95+
top_cont = lv.obj(settings_screen_detail)
96+
top_cont.set_width(lv.pct(100))
97+
top_cont.set_style_border_width(0, 0)
98+
top_cont.set_height(lv.SIZE_CONTENT)
99+
top_cont.set_style_pad_all(mpos.ui.pct_of_display_width(1), 0)
100+
top_cont.set_flex_flow(lv.FLEX_FLOW.ROW)
101+
top_cont.set_style_flex_main_place(lv.FLEX_ALIGN.SPACE_BETWEEN, 0)
102+
103+
setting_label = lv.label(top_cont)
104+
setting_label.set_text(setting["title"])
105+
setting_label.align(lv.ALIGN.TOP_LEFT,0,0)
106+
setting_label.set_style_text_font(lv.font_montserrat_26, 0)
107+
108+
if setting["key"] == "light_dark_theme" or setting["key"] == "boot_mode":
109+
# Create container for radio buttons
110+
self.radio_container = lv.obj(settings_screen_detail)
111+
self.radio_container.set_width(lv.pct(100))
112+
self.radio_container.set_height(lv.SIZE_CONTENT)
113+
self.radio_container.set_flex_flow(lv.FLEX_FLOW.COLUMN)
114+
self.radio_container.add_event_cb(self.radio_event_handler, lv.EVENT.CLICKED, None)
115+
116+
# Create radio buttons
117+
if setting["key"] == "boot_mode":
118+
options = [("Normal", "normal"), ("Bootloader", "bootloader")]
119+
else:
120+
options = [("Light", "light"), ("Dark", "dark")]
121+
current_setting = self.prefs.get_string(setting["key"])
122+
self.active_radio_index = -1 # none
123+
if current_setting == options[0][1]:
124+
self.active_radio_index = 0
125+
elif current_setting == options[1][1]:
126+
self.active_radio_index = 1
127+
128+
for i, (text, _) in enumerate(options):
129+
cb = self.create_radio_button(self.radio_container, text, i)
130+
if i == self.active_radio_index:
131+
cb.add_state(lv.STATE.CHECKED)
132+
else:
133+
# Textarea for other settings
134+
self.textarea = lv.textarea(settings_screen_detail)
135+
self.textarea.set_width(lv.pct(100))
136+
self.textarea.set_height(lv.SIZE_CONTENT)
137+
self.textarea.align_to(top_cont, lv.ALIGN.OUT_BOTTOM_MID, 0, 0)
138+
current = self.prefs.get_string(setting["key"])
139+
if current:
140+
self.textarea.set_text(current)
141+
placeholder = setting.get("placeholder")
142+
if placeholder:
143+
self.textarea.set_placeholder_text(placeholder)
144+
self.textarea.add_event_cb(lambda *args: mpos.ui.anim.smooth_show(self.keyboard), lv.EVENT.CLICKED, None) # it might be focused, but keyboard hidden (because ready/cancel clicked)
145+
self.textarea.add_event_cb(lambda *args: mpos.ui.anim.smooth_hide(self.keyboard), lv.EVENT.DEFOCUSED, None)
146+
# Initialize keyboard (hidden initially)
147+
self.keyboard = lv.keyboard(lv.layer_sys())
148+
self.keyboard.align(lv.ALIGN.BOTTOM_MID, 0, 0)
149+
self.keyboard.set_style_min_height(150, 0)
150+
self.keyboard.add_flag(lv.obj.FLAG.HIDDEN)
151+
self.keyboard.add_event_cb(lambda *args: mpos.ui.anim.smooth_hide(self.keyboard), lv.EVENT.READY, None)
152+
self.keyboard.add_event_cb(lambda *args: mpos.ui.anim.smooth_hide(self.keyboard), lv.EVENT.CANCEL, None)
153+
self.keyboard.set_textarea(self.textarea)
154+
155+
# Button container
156+
btn_cont = lv.obj(settings_screen_detail)
157+
btn_cont.set_width(lv.pct(100))
158+
btn_cont.set_style_border_width(0, 0)
159+
btn_cont.set_height(lv.SIZE_CONTENT)
160+
btn_cont.set_flex_flow(lv.FLEX_FLOW.ROW)
161+
btn_cont.set_style_flex_main_place(lv.FLEX_ALIGN.SPACE_BETWEEN, 0)
162+
# Save button
163+
save_btn = lv.button(btn_cont)
164+
save_btn.set_size(lv.pct(45), lv.SIZE_CONTENT)
165+
save_label = lv.label(save_btn)
166+
save_label.set_text("Save")
167+
save_label.center()
168+
save_btn.add_event_cb(lambda e, s=setting: self.save_setting(s), lv.EVENT.CLICKED, None)
169+
# Cancel button
170+
cancel_btn = lv.button(btn_cont)
171+
cancel_btn.set_size(lv.pct(45), lv.SIZE_CONTENT)
172+
cancel_label = lv.label(cancel_btn)
173+
cancel_label.set_text("Cancel")
174+
cancel_label.center()
175+
cancel_btn.add_event_cb(lambda e: self.finish(), lv.EVENT.CLICKED, None)
176+
177+
if False: # No scan QR button for text settings because they're all short right now
178+
cambutton = lv.button(settings_screen_detail)
179+
cambutton.align(lv.ALIGN.BOTTOM_MID,0,0)
180+
cambutton.set_size(lv.pct(100), lv.pct(30))
181+
cambuttonlabel = lv.label(cambutton)
182+
cambuttonlabel.set_text("Scan data from QR code")
183+
cambuttonlabel.set_style_text_font(lv.font_montserrat_18, 0)
184+
cambuttonlabel.align(lv.ALIGN.TOP_MID, 0, 0)
185+
cambuttonlabel2 = lv.label(cambutton)
186+
cambuttonlabel2.set_text("Tip: Create your own QR code,\nusing https://genqrcode.com or another tool.")
187+
cambuttonlabel2.set_style_text_font(lv.font_montserrat_10, 0)
188+
cambuttonlabel2.align(lv.ALIGN.BOTTOM_MID, 0, 0)
189+
cambutton.add_event_cb(self.cambutton_cb, lv.EVENT.CLICKED, None)
190+
191+
self.setContentView(settings_screen_detail)
192+
193+
def onStop(self, screen):
194+
if self.keyboard:
195+
mpos.ui.anim.smooth_hide(self.keyboard)
196+
197+
def radio_event_handler(self, event):
198+
print("radio_event_handler called")
199+
if self.active_radio_index >= 0:
200+
print(f"removing old CHECKED state from child {self.active_radio_index}")
201+
old_cb = self.radio_container.get_child(self.active_radio_index)
202+
old_cb.remove_state(lv.STATE.CHECKED)
203+
self.active_radio_index = -1
204+
for childnr in range(self.radio_container.get_child_count()):
205+
child = self.radio_container.get_child(childnr)
206+
state = child.get_state()
207+
print(f"radio_container child's state: {state}")
208+
if state & lv.STATE.CHECKED: # State can be something like 19 = lv.STATE.HOVERED (16) & lv.STATE.FOCUSED (2) & lv.STATE.CHECKED (1)
209+
self.active_radio_index = childnr
210+
break
211+
print(f"active_radio_index is now {self.active_radio_index}")
212+
213+
def create_radio_button(self, parent, text, index):
214+
cb = lv.checkbox(parent)
215+
cb.set_text(text)
216+
cb.add_flag(lv.obj.FLAG.EVENT_BUBBLE)
217+
# Add circular style to indicator for radio button appearance
218+
style_radio = lv.style_t()
219+
style_radio.init()
220+
style_radio.set_radius(lv.RADIUS_CIRCLE)
221+
cb.add_style(style_radio, lv.PART.INDICATOR)
222+
style_radio_chk = lv.style_t()
223+
style_radio_chk.init()
224+
style_radio_chk.set_bg_image_src(None)
225+
cb.add_style(style_radio_chk, lv.PART.INDICATOR | lv.STATE.CHECKED)
226+
return cb
227+
228+
def gotqr_result_callback_unused(self, result):
229+
print(f"QR capture finished, result: {result}")
230+
if result.get("result_code"):
231+
data = result.get("data")
232+
print(f"Setting textarea data: {data}")
233+
self.textarea.set_text(data)
234+
235+
def cambutton_cb_unused(self, event):
236+
print("cambutton clicked!")
237+
self.startActivityForResult(Intent(activity_class=CameraApp).putExtra("scanqr_mode", True), self.gotqr_result_callback)
238+
239+
def save_setting(self, setting):
240+
if ( setting["key"] =="light_dark_theme" or setting["key"] == "boot_mode" ) and self.radio_container:
241+
if setting["key"] == "boot_mode":
242+
options = [("Normal", "normal"), ("Bootloader", "bootloader")]
243+
else:
244+
options = [("Light", "light"), ("Dark", "dark")]
245+
selected_idx = self.active_radio_index
246+
if selected_idx == 0:
247+
new_value = options[0][1]
248+
elif selected_idx == 1:
249+
new_value = options[1][1]
250+
else:
251+
return # nothing to save
252+
elif self.textarea:
253+
new_value = self.textarea.get_text()
254+
else:
255+
new_value = ""
256+
editor = self.prefs.edit()
257+
editor.put_string(setting["key"], new_value)
258+
editor.commit()
259+
setting["value_label"].set_text(new_value if new_value else "(not set)")
260+
self.finish()

internal_filesystem/boot_unix.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
import mpos.clipboard
1313

1414
# Same as Waveshare ESP32-S3-Touch-LCD-2
15-
#TFT_HOR_RES=320
16-
#TFT_VER_RES=240
15+
TFT_HOR_RES=320
16+
TFT_VER_RES=240
1717

1818
#TFT_HOR_RES=640
1919
#TFT_VER_RES=480
@@ -27,8 +27,8 @@
2727
#TFT_VER_RES=576
2828

2929
# 16:9 good resolution but fairly small icons:
30-
TFT_HOR_RES=1280
31-
TFT_VER_RES=720
30+
#TFT_HOR_RES=1280
31+
#TFT_VER_RES=720
3232

3333
# Even HD works:
3434
#TFT_HOR_RES=1920

0 commit comments

Comments
 (0)