|
| 1 | +importlvglaslv |
| 2 | + |
| 3 | +importmpos.ui |
| 4 | +frommpos.ui.animimportWidgetAnimator |
| 5 | + |
| 6 | +NOTIFICATION_BAR_HEIGHT=24 |
| 7 | + |
| 8 | +CLOCK_UPDATE_INTERVAL=1000# 10 or even 1 ms doesn't seem to change the framerate but 100ms is enough |
| 9 | +WIFI_ICON_UPDATE_INTERVAL=1500 |
| 10 | +BATTERY_ICON_UPDATE_INTERVAL=5000 |
| 11 | +TEMPERATURE_UPDATE_INTERVAL=2000 |
| 12 | +MEMFREE_UPDATE_INTERVAL=5000# not too frequent because there's a forced gc.collect() to give it a reliable value |
| 13 | + |
| 14 | +DRAWER_ANIM_DURATION=300 |
| 15 | + |
| 16 | + |
| 17 | +hide_bar_animation=None |
| 18 | +show_bar_animation=None |
| 19 | +show_bar_animation_start_value=-NOTIFICATION_BAR_HEIGHT |
| 20 | +show_bar_animation_end_value=0 |
| 21 | +hide_bar_animation_start_value=show_bar_animation_end_value |
| 22 | +hide_bar_animation_end_value=show_bar_animation_start_value |
| 23 | + |
| 24 | +drawer=None |
| 25 | +drawer_open=False |
| 26 | +bar_open=False |
| 27 | + |
| 28 | +scroll_start_y=None |
| 29 | + |
| 30 | + |
| 31 | +# Widgets: |
| 32 | +notification_bar=None |
| 33 | + |
| 34 | +defopen_drawer(): |
| 35 | +globaldrawer_open,drawer |
| 36 | +ifnotdrawer_open: |
| 37 | +open_bar() |
| 38 | +drawer_open=True |
| 39 | +WidgetAnimator.show_widget(drawer,anim_type="slide_down",duration=1000,delay=0) |
| 40 | +drawer.scroll_to(0,0,lv.ANIM.OFF)# make sure it's at the top, not scrolled down |
| 41 | + |
| 42 | +defclose_drawer(to_launcher=False): |
| 43 | +globaldrawer_open,drawer,foreground_app_name |
| 44 | +ifdrawer_open: |
| 45 | +drawer_open=False |
| 46 | +ifnotto_launcherandnotmpos.apps.is_launcher(mpos.ui.foreground_app_name): |
| 47 | +print(f"close_drawer: also closing bar because to_launcher is{to_launcher} and foreground_app_name is{foreground_app_name}") |
| 48 | +close_bar() |
| 49 | +WidgetAnimator.hide_widget(drawer,anim_type="slide_up",duration=1000,delay=0) |
| 50 | + |
| 51 | +defopen_bar(): |
| 52 | +print("opening bar...") |
| 53 | +globalbar_open,show_bar_animation,hide_bar_animation,notification_bar |
| 54 | +ifnotbar_open: |
| 55 | +#print("not open so opening...") |
| 56 | +bar_open=True |
| 57 | +hide_bar_animation.current_value=hide_bar_animation_end_value |
| 58 | +show_bar_animation.start() |
| 59 | +else: |
| 60 | +print("bar already open") |
| 61 | + |
| 62 | +defclose_bar(): |
| 63 | +globalbar_open,show_bar_animation,hide_bar_animation |
| 64 | +ifbar_open: |
| 65 | +bar_open=False |
| 66 | +show_bar_animation.current_value=show_bar_animation_end_value |
| 67 | +hide_bar_animation.start() |
| 68 | + |
| 69 | + |
| 70 | + |
| 71 | + |
| 72 | +defcreate_notification_bar(): |
| 73 | +globalnotification_bar |
| 74 | +# Create notification bar |
| 75 | +notification_bar=lv.obj(lv.layer_top()) |
| 76 | +notification_bar.set_size(lv.pct(100),NOTIFICATION_BAR_HEIGHT) |
| 77 | +notification_bar.set_pos(0,show_bar_animation_start_value) |
| 78 | +notification_bar.set_scrollbar_mode(lv.SCROLLBAR_MODE.OFF) |
| 79 | +notification_bar.set_scroll_dir(lv.DIR.NONE) |
| 80 | +notification_bar.set_style_border_width(0,0) |
| 81 | +notification_bar.set_style_radius(0,0) |
| 82 | +# Time label |
| 83 | +time_label=lv.label(notification_bar) |
| 84 | +time_label.set_text("00:00:00") |
| 85 | +time_label.align(lv.ALIGN.LEFT_MID,0,0) |
| 86 | +temp_label=lv.label(notification_bar) |
| 87 | +temp_label.set_text("00°C") |
| 88 | +temp_label.align_to(time_label,lv.ALIGN.OUT_RIGHT_MID,mpos.ui.pct_of_display_width(7),0) |
| 89 | +memfree_label=lv.label(notification_bar) |
| 90 | +memfree_label.set_text("") |
| 91 | +memfree_label.align_to(temp_label,lv.ALIGN.OUT_RIGHT_MID,mpos.ui.pct_of_display_width(7),0) |
| 92 | +#style = lv.style_t() |
| 93 | +#style.init() |
| 94 | +#style.set_text_font(lv.font_montserrat_8) # tiny font |
| 95 | +#memfree_label.add_style(style, 0) |
| 96 | +# Notification icon (bell) |
| 97 | +#notif_icon = lv.label(notification_bar) |
| 98 | +#notif_icon.set_text(lv.SYMBOL.BELL) |
| 99 | +#notif_icon.align_to(time_label, lv.ALIGN.OUT_RIGHT_MID, PADDING_TINY, 0) |
| 100 | +# Battery percentage |
| 101 | +#battery_label = lv.label(notification_bar) |
| 102 | +#battery_label.set_text("100%") |
| 103 | +#battery_label.align(lv.ALIGN.RIGHT_MID, 0, 0) |
| 104 | +#battery_label.add_flag(lv.obj.FLAG.HIDDEN) |
| 105 | +# Battery icon |
| 106 | +battery_icon=lv.label(notification_bar) |
| 107 | +battery_icon.set_text(lv.SYMBOL.BATTERY_FULL) |
| 108 | +#battery_icon.align_to(battery_label, lv.ALIGN.OUT_LEFT_MID, 0, 0) |
| 109 | +battery_icon.align(lv.ALIGN.RIGHT_MID,0,0) |
| 110 | +battery_icon.add_flag(lv.obj.FLAG.HIDDEN)# keep it hidden until it has a correct value |
| 111 | +# WiFi icon |
| 112 | +wifi_icon=lv.label(notification_bar) |
| 113 | +wifi_icon.set_text(lv.SYMBOL.WIFI) |
| 114 | +wifi_icon.align_to(battery_icon,lv.ALIGN.OUT_LEFT_MID,-mpos.ui.pct_of_display_width(1),0) |
| 115 | +wifi_icon.add_flag(lv.obj.FLAG.HIDDEN) |
| 116 | +# Update time |
| 117 | +defupdate_time(timer): |
| 118 | +hours=mpos.time.localtime()[3] |
| 119 | +minutes=mpos.time.localtime()[4] |
| 120 | +seconds=mpos.time.localtime()[5] |
| 121 | +time_label.set_text(f"{hours:02d}:{minutes:02d}:{seconds:02d}") |
| 122 | + |
| 123 | +can_check_network=False |
| 124 | +try: |
| 125 | +importnetwork |
| 126 | +can_check_network=True |
| 127 | +exceptExceptionase: |
| 128 | +print("Warning: could not check WLAN status:",str(e)) |
| 129 | + |
| 130 | +defupdate_battery_icon(timer=None): |
| 131 | +percent=mpos.battery_voltage.get_battery_percentage() |
| 132 | +ifpercent>80:# 4.1V |
| 133 | +battery_icon.set_text(lv.SYMBOL.BATTERY_FULL) |
| 134 | +elifpercent>60:# 4.0V |
| 135 | +battery_icon.set_text(lv.SYMBOL.BATTERY_3) |
| 136 | +elifpercent>40:# 3.9V |
| 137 | +battery_icon.set_text(lv.SYMBOL.BATTERY_2) |
| 138 | +elifpercent>20:# 3.8V |
| 139 | +battery_icon.set_text(lv.SYMBOL.BATTERY_1) |
| 140 | +else:# > 3.7V |
| 141 | +battery_icon.set_text(lv.SYMBOL.BATTERY_EMPTY) |
| 142 | +battery_icon.remove_flag(lv.obj.FLAG.HIDDEN) |
| 143 | +# Percentage is not shown for now: |
| 144 | +#battery_label.set_text(f"{round(percent)}%") |
| 145 | +#battery_label.remove_flag(lv.obj.FLAG.HIDDEN) |
| 146 | +update_battery_icon()# run it immediately instead of waiting for the timer |
| 147 | + |
| 148 | +defupdate_wifi_icon(timer): |
| 149 | +ifmpos.wifi.WifiService.is_connected(): |
| 150 | +wifi_icon.remove_flag(lv.obj.FLAG.HIDDEN) |
| 151 | +else: |
| 152 | +wifi_icon.add_flag(lv.obj.FLAG.HIDDEN) |
| 153 | + |
| 154 | +can_check_temperature=False |
| 155 | +try: |
| 156 | +importesp32 |
| 157 | +can_check_temperature=True |
| 158 | +exceptExceptionase: |
| 159 | +print("Warning: can't check temperature sensor:",str(e)) |
| 160 | + |
| 161 | +defupdate_temperature(timer): |
| 162 | +ifcan_check_temperature: |
| 163 | +temp_label.set_text(f"{esp32.mcu_temperature()}°C") |
| 164 | +else: |
| 165 | +temp_label.set_text("42°C") |
| 166 | + |
| 167 | +defupdate_memfree(timer): |
| 168 | +importgc |
| 169 | +gc.collect()# otherwise it goes down to 10% before shooting back up to 70% |
| 170 | +free=gc.mem_free() |
| 171 | +used=gc.mem_alloc() |
| 172 | +total_memory=gc.mem_free()+gc.mem_alloc() |
| 173 | +percentage=round(free*100/ (free+used)) |
| 174 | +memfree_label.set_text(f"{percentage}%") |
| 175 | + |
| 176 | +lv.timer_create(update_time,CLOCK_UPDATE_INTERVAL,None) |
| 177 | +lv.timer_create(update_temperature,TEMPERATURE_UPDATE_INTERVAL,None) |
| 178 | +lv.timer_create(update_memfree,MEMFREE_UPDATE_INTERVAL,None) |
| 179 | +lv.timer_create(update_wifi_icon,WIFI_ICON_UPDATE_INTERVAL,None) |
| 180 | +lv.timer_create(update_battery_icon,BATTERY_ICON_UPDATE_INTERVAL,None) |
| 181 | + |
| 182 | +# hide bar animation |
| 183 | +globalhide_bar_animation |
| 184 | +hide_bar_animation=lv.anim_t() |
| 185 | +hide_bar_animation.init() |
| 186 | +hide_bar_animation.set_var(notification_bar) |
| 187 | +hide_bar_animation.set_values(0,-NOTIFICATION_BAR_HEIGHT) |
| 188 | +hide_bar_animation.set_time(2000) |
| 189 | +hide_bar_animation.set_custom_exec_cb(lambdanot_used,value :notification_bar.set_y(value)) |
| 190 | + |
| 191 | +# show bar animation |
| 192 | +globalshow_bar_animation |
| 193 | +show_bar_animation=lv.anim_t() |
| 194 | +show_bar_animation.init() |
| 195 | +show_bar_animation.set_var(notification_bar) |
| 196 | +show_bar_animation.set_values(show_bar_animation_start_value,show_bar_animation_end_value) |
| 197 | +show_bar_animation.set_time(1000) |
| 198 | +show_bar_animation.set_custom_exec_cb(lambdanot_used,value :notification_bar.set_y(value)) |
| 199 | + |
| 200 | + |
| 201 | + |
| 202 | +defcreate_drawer(display=None): |
| 203 | +globaldrawer |
| 204 | +drawer=lv.obj(lv.layer_top()) |
| 205 | +drawer.set_size(lv.pct(100),lv.pct(90)) |
| 206 | +drawer.set_pos(0,NOTIFICATION_BAR_HEIGHT) |
| 207 | +drawer.set_scroll_dir(lv.DIR.VER) |
| 208 | +drawer.set_style_pad_all(15,0) |
| 209 | +drawer.set_style_border_width(0,0) |
| 210 | +drawer.set_style_radius(0,0) |
| 211 | +drawer.add_flag(lv.obj.FLAG.HIDDEN) |
| 212 | +drawer.add_event_cb(drawer_scroll_callback,lv.EVENT.SCROLL_BEGIN,None) |
| 213 | +drawer.add_event_cb(drawer_scroll_callback,lv.EVENT.SCROLL,None) |
| 214 | +drawer.add_event_cb(drawer_scroll_callback,lv.EVENT.SCROLL_END,None) |
| 215 | +slider_label=lv.label(drawer) |
| 216 | +prefs=mpos.config.SharedPreferences("com.micropythonos.settings") |
| 217 | +brightness_int=prefs.get_int("display_brightness",100) |
| 218 | +ifdisplay: |
| 219 | +display.set_backlight(brightness_int) |
| 220 | +slider_label.set_text(f"Brightness:{brightness_int}%") |
| 221 | +slider_label.align(lv.ALIGN.TOP_MID,0,lv.pct(4)) |
| 222 | +slider=lv.slider(drawer) |
| 223 | +slider.set_range(1,100) |
| 224 | +slider.set_value(int(brightness_int),False) |
| 225 | +slider.set_width(lv.pct(80)) |
| 226 | +slider.align_to(slider_label,lv.ALIGN.OUT_BOTTOM_MID,0,10) |
| 227 | +defbrightness_slider_changed(e): |
| 228 | +brightness_int=slider.get_value() |
| 229 | +slider_label.set_text(f"Brightness:{brightness_int}%") |
| 230 | +ifdisplay: |
| 231 | +display.set_backlight(brightness_int) |
| 232 | +defbrightness_slider_released(e): |
| 233 | +brightness_int=slider.get_value() |
| 234 | +prefs=mpos.config.SharedPreferences("com.micropythonos.settings") |
| 235 | +old_brightness_int=prefs.get_int("display_brightness") |
| 236 | +ifold_brightness_int!=brightness_int: |
| 237 | +editor=prefs.edit() |
| 238 | +editor.put_int("display_brightness",brightness_int) |
| 239 | +editor.commit() |
| 240 | +slider.add_event_cb(brightness_slider_changed,lv.EVENT.VALUE_CHANGED,None) |
| 241 | +slider.add_event_cb(brightness_slider_released,lv.EVENT.RELEASED,None) |
| 242 | +drawer_button_pct=31 |
| 243 | +wifi_btn=lv.button(drawer) |
| 244 | +wifi_btn.set_size(lv.pct(drawer_button_pct),lv.pct(20)) |
| 245 | +wifi_btn.align(lv.ALIGN.LEFT_MID,0,0) |
| 246 | +wifi_label=lv.label(wifi_btn) |
| 247 | +wifi_label.set_text(lv.SYMBOL.WIFI+" WiFi") |
| 248 | +wifi_label.center() |
| 249 | +defwifi_event(e): |
| 250 | +close_drawer() |
| 251 | +mpos.apps.start_app_by_name("com.micropythonos.wifi") |
| 252 | +wifi_btn.add_event_cb(wifi_event,lv.EVENT.CLICKED,None) |
| 253 | +settings_btn=lv.button(drawer) |
| 254 | +settings_btn.set_size(lv.pct(drawer_button_pct),lv.pct(20)) |
| 255 | +settings_btn.align(lv.ALIGN.RIGHT_MID,0,0) |
| 256 | +settings_label=lv.label(settings_btn) |
| 257 | +settings_label.set_text(lv.SYMBOL.SETTINGS+" Settings") |
| 258 | +settings_label.center() |
| 259 | +defsettings_event(e): |
| 260 | +close_drawer() |
| 261 | +mpos.apps.start_app_by_name("com.micropythonos.settings") |
| 262 | +settings_btn.add_event_cb(settings_event,lv.EVENT.CLICKED,None) |
| 263 | +launcher_btn=lv.button(drawer) |
| 264 | +launcher_btn.set_size(lv.pct(drawer_button_pct),lv.pct(20)) |
| 265 | +launcher_btn.align(lv.ALIGN.CENTER,0,0) |
| 266 | +launcher_label=lv.label(launcher_btn) |
| 267 | +launcher_label.set_text(lv.SYMBOL.HOME+" Home") |
| 268 | +launcher_label.center() |
| 269 | +deflauncher_event(e): |
| 270 | +print("Home button pressed!") |
| 271 | +close_drawer(True) |
| 272 | +mpos.ui.show_launcher() |
| 273 | +launcher_btn.add_event_cb(launcher_event,lv.EVENT.CLICKED,None) |
| 274 | +''' |
| 275 | + sleep_btn=lv.button(drawer) |
| 276 | + sleep_btn.set_size(lv.pct(drawer_button_pct),lv.pct(20)) |
| 277 | + sleep_btn.align(lv.ALIGN.BOTTOM_LEFT,0,0) |
| 278 | + sleep_label=lv.label(sleep_btn) |
| 279 | + sleep_label.set_text("Zz Sleep") |
| 280 | + sleep_label.center() |
| 281 | + def sleep_event(e): |
| 282 | + print("Sleep button pressed!") |
| 283 | + import sys |
| 284 | + if sys.platform == "esp32": |
| 285 | + #On ESP32, there's no power off but there's a hundred-year deepsleep. |
| 286 | + import machine |
| 287 | + machine.deepsleep(10000) # TODO: make it wakeup when it receives an interrupt from the accelerometer or a button press |
| 288 | + else: # assume unix: |
| 289 | + # maybe do a system suspend here? or at least show a popup toast "not supported" |
| 290 | + close_drawer(True) |
| 291 | + show_launcher() |
| 292 | + sleep_btn.add_event_cb(sleep_event,lv.EVENT.CLICKED,None) |
| 293 | + ''' |
| 294 | +restart_btn=lv.button(drawer) |
| 295 | +restart_btn.set_size(lv.pct(45),lv.pct(20)) |
| 296 | +restart_btn.align(lv.ALIGN.BOTTOM_LEFT,0,0) |
| 297 | +restart_label=lv.label(restart_btn) |
| 298 | +restart_label.set_text(lv.SYMBOL.REFRESH+" Reset") |
| 299 | +restart_label.center() |
| 300 | +defreset_cb(e): |
| 301 | +importmachine |
| 302 | +ifhasattr(machine,'reset'): |
| 303 | +machine.reset() |
| 304 | +elifhasattr(machine,'soft_reset'): |
| 305 | +machine.soft_reset() |
| 306 | +else: |
| 307 | +print("Warning: machine has no reset or soft_reset method available") |
| 308 | +restart_btn.add_event_cb(reset_cb,lv.EVENT.CLICKED,None) |
| 309 | +poweroff_btn=lv.button(drawer) |
| 310 | +poweroff_btn.set_size(lv.pct(45),lv.pct(20)) |
| 311 | +poweroff_btn.align(lv.ALIGN.BOTTOM_RIGHT,0,0) |
| 312 | +poweroff_label=lv.label(poweroff_btn) |
| 313 | +poweroff_label.set_text(lv.SYMBOL.POWER+" Off") |
| 314 | +poweroff_label.center() |
| 315 | +defpoweroff_cb(e): |
| 316 | +print("Power off action...") |
| 317 | +remove_and_stop_current_activity()# make sure current app, like camera, does cleanup, saves progress, stops hardware etc. |
| 318 | +importsys |
| 319 | +ifsys.platform=="esp32": |
| 320 | +#On ESP32, there's no power off but there is a forever sleep |
| 321 | +importmachine |
| 322 | +# DON'T configure BOOT button (Pin 0) as wake-up source because it wakes up immediately. |
| 323 | +# Luckily, the RESET button can be used to wake it up. |
| 324 | +#wake_pin = machine.Pin(0, machine.Pin.IN, machine.Pin.PULL_UP) # Pull-up enabled, active low |
| 325 | +#import esp32 |
| 326 | +#esp32.wake_on_ext0(pin=wake_pin, level=esp32.WAKEUP_ALL_LOW) |
| 327 | +print("Entering deep sleep. Press BOOT button to wake up.") |
| 328 | +machine.deepsleep()# sleep forever |
| 329 | +else:# assume unix: |
| 330 | +lv.deinit()# Deinitialize LVGL (if supported) |
| 331 | +sys.exit(0) |
| 332 | +poweroff_btn.add_event_cb(poweroff_cb,lv.EVENT.CLICKED,None) |
| 333 | +# Add invisible padding at the bottom to make the drawer scrollable |
| 334 | +l2=lv.label(drawer) |
| 335 | +l2.set_text("\n") |
| 336 | +l2.set_pos(0,mpos.ui.vertical_resolution) |
| 337 | + |
| 338 | + |
| 339 | + |
| 340 | + |
| 341 | +defdrawer_any_event(event): |
| 342 | +event_code=event.get_code() |
| 343 | +# Ignore: |
| 344 | +# ======= |
| 345 | +# 19: HIT_TEST |
| 346 | +# COVER_CHECK |
| 347 | +# DRAW_MAIN |
| 348 | +# DRAW_MAIN_BEGIN |
| 349 | +# DRAW_MAIN_END |
| 350 | +# DRAW_POST |
| 351 | +# DRAW_POST_BEGIN |
| 352 | +# DRAW_POST_END |
| 353 | +# GET_SELF_SIZE |
| 354 | +# 47 STYLE CHANGED |
| 355 | +ifevent_codenotin [19,23,25,26,27,28,29,30,47,49]: |
| 356 | +name=mpos.ui.get_event_name(event_code) |
| 357 | +x,y=mpos.ui.get_pointer_xy() |
| 358 | +print(f"drawer_any_event: code={event_code}, name={name},{x},{y}") |
| 359 | + |
| 360 | +drawer_swipe_start_y=0 |
| 361 | +defdrawer_swipe_cb(event): |
| 362 | +globaldrawer_swipe_start_y |
| 363 | +event_code=event.get_code() |
| 364 | +name=mpos.ui.get_event_name(event_code) |
| 365 | +print(f"drawer_swipe_cb{event_code} and{name}") |
| 366 | +ifevent_code==lv.EVENT.PRESSED: |
| 367 | +x,drawer_swipe_start_y=mpos.ui.get_pointer_xy() |
| 368 | +elifevent_code==lv.EVENT.RELEASED: |
| 369 | +x,end_y=mpos.ui.get_pointer_xy() |
| 370 | +ifend_y<drawer_swipe_start_y-NOTIFICATION_BAR_HEIGHT: |
| 371 | +close_drawer() |
| 372 | +drawer_swipe_start_y=0 |
| 373 | + |
| 374 | +defdrawer_scroll_callback(event): |
| 375 | +globalscroll_start_y |
| 376 | +event_code=event.get_code() |
| 377 | +x,y=mpos.ui.get_pointer_xy() |
| 378 | +#name = mpos.ui.get_event_name(event_code) |
| 379 | +#print(f"drawer_scroll: code={event_code}, name={name}, ({x},{y})") |
| 380 | +ifevent_code==lv.EVENT.SCROLL_BEGINandscroll_start_y==None: |
| 381 | +scroll_start_y=y |
| 382 | +#print(f"scroll_starts at: {x},{y}") |
| 383 | +elifevent_code==lv.EVENT.SCROLLandscroll_start_y!=None: |
| 384 | +diff=y-scroll_start_y |
| 385 | +print(f"scroll distance:{diff}") |
| 386 | +ifdiff<-NOTIFICATION_BAR_HEIGHT: |
| 387 | +close_drawer() |
| 388 | +scroll_start_y=None |
| 389 | +elifevent_code==lv.EVENT.SCROLL_ENDandscroll_start_y!=None: |
| 390 | +scroll_start_y=None |
| 391 | + |