|
| 1 | +frommpos.appsimportActivity,Intent |
| 2 | +importmpos.config |
| 3 | +importmpos.ui |
| 4 | + |
| 5 | +classHello(Activity): |
| 6 | + |
| 7 | +defonCreate(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 | +classSettingsActivity(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 | +defonCreate(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 | +defonResume(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 | +forsettinginself.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 | +lambdae,s=setting:self.startSettingActivity(s),lv.EVENT.CLICKED,None |
| 69 | + ) |
| 70 | + |
| 71 | +defstartSettingActivity(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 | +classSettingActivity(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 | +defonCreate(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 | +ifsetting["key"]=="light_dark_theme"orsetting["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 | +ifsetting["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 | +ifcurrent_setting==options[0][1]: |
| 124 | +self.active_radio_index=0 |
| 125 | +elifcurrent_setting==options[1][1]: |
| 126 | +self.active_radio_index=1 |
| 127 | + |
| 128 | +fori, (text,_)inenumerate(options): |
| 129 | +cb=self.create_radio_button(self.radio_container,text,i) |
| 130 | +ifi==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 | +ifcurrent: |
| 140 | +self.textarea.set_text(current) |
| 141 | +placeholder=setting.get("placeholder") |
| 142 | +ifplaceholder: |
| 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(lambdae,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(lambdae:self.finish(),lv.EVENT.CLICKED,None) |
| 176 | + |
| 177 | +ifFalse:# 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 | +defonStop(self,screen): |
| 194 | +ifself.keyboard: |
| 195 | +mpos.ui.anim.smooth_hide(self.keyboard) |
| 196 | + |
| 197 | +defradio_event_handler(self,event): |
| 198 | +print("radio_event_handler called") |
| 199 | +ifself.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 | +forchildnrinrange(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 | +ifstate&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 | +defcreate_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 | +returncb |
| 227 | + |
| 228 | +defgotqr_result_callback_unused(self,result): |
| 229 | +print(f"QR capture finished, result:{result}") |
| 230 | +ifresult.get("result_code"): |
| 231 | +data=result.get("data") |
| 232 | +print(f"Setting textarea data:{data}") |
| 233 | +self.textarea.set_text(data) |
| 234 | + |
| 235 | +defcambutton_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 | +defsave_setting(self,setting): |
| 240 | +if (setting["key"]=="light_dark_theme"orsetting["key"]=="boot_mode" )andself.radio_container: |
| 241 | +ifsetting["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 | +ifselected_idx==0: |
| 247 | +new_value=options[0][1] |
| 248 | +elifselected_idx==1: |
| 249 | +new_value=options[1][1] |
| 250 | +else: |
| 251 | +return# nothing to save |
| 252 | +elifself.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_valueifnew_valueelse"(not set)") |
| 260 | +self.finish() |