77import time
88import _thread
99
10-
1110from mpos .apps import Activity , Intent
1211from mpos .app import App
1312from mpos import TaskManager
1413import mpos .ui
1514from mpos .content .package_manager import PackageManager
1615
17-
1816class AppStore (Activity ):
17+
18+ _BADGEHUB_API_BASE_URL = "https://badgehub.p1m.nl/api/v3"
19+ _BADGEHUB_LIST = "project-summaries?badge=fri3d_2024"
20+ _BADGEHUB_DETAILS = "projects"
21+
22+ _BACKEND_API_GITHUB = "github"
23+ _BACKEND_API_BADGEHUB = "badgehub"
24+
1925 apps = []
20- app_index_url = "https://apps.micropythonos.com/app_index.json"
26+ # These might become configurations:
27+ #backend_api = _BACKEND_API_BADGEHUB
28+ backend_api = _BACKEND_API_GITHUB
29+ app_index_url_github = "https://apps.micropythonos.com/app_index.json"
30+ app_index_url_badgehub = _BADGEHUB_API_BASE_URL + "/" + _BADGEHUB_LIST
31+ app_detail_url_badgehub = _BADGEHUB_API_BASE_URL + "/" + _BADGEHUB_DETAILS
2132 can_check_network = True
2233 aiohttp_session = None # one session for the whole app is more performant
2334
@@ -48,7 +59,10 @@ def onResume(self, screen):
4859 if self .can_check_network and not network .WLAN (network .STA_IF ).isconnected ():
4960 self .please_wait_label .set_text ("Error: WiFi is not connected." )
5061 else :
51- TaskManager .create_task (self .download_app_index (self .app_index_url ))
62+ if self .backend_api == self ._BACKEND_API_BADGEHUB :
63+ TaskManager .create_task (self .download_app_index (self .app_index_url_badgehub ))
64+ else :
65+ TaskManager .create_task (self .download_app_index (self .app_index_url_github ))
5266
5367 def onDestroy (self , screen ):
5468 await self .aiohttp_session .close ()
@@ -60,9 +74,14 @@ async def download_app_index(self, json_url):
6074 return
6175 print (f"Got response text: { response [0 :20 ]} " )
6276 try :
63- for app in json .loads (response ):
77+ parsed = json .loads (response )
78+ print (f"parsed json: { parsed } " )
79+ for app in parsed :
6480 try :
65- self .apps .append (App (app ["name" ], app ["publisher" ], app ["short_description" ], app ["long_description" ], app ["icon_url" ], app ["download_url" ], app ["fullname" ], app ["version" ], app ["category" ], app ["activities" ]))
81+ if self .backend_api == self ._BACKEND_API_BADGEHUB :
82+ self .apps .append (AppStore .badgehub_app_to_mpos_app (app ))
83+ else :
84+ self .apps .append (App (app ["name" ], app ["publisher" ], app ["short_description" ], app ["long_description" ], app ["icon_url" ], app ["download_url" ], app ["fullname" ], app ["version" ], app ["category" ], app ["activities" ]))
6685 except Exception as e :
6786 print (f"Warning: could not add app from { json_url } to apps list: { e } " )
6887 except Exception as e :
@@ -157,6 +176,7 @@ async def download_icons(self):
157176 def show_app_detail (self , app ):
158177 intent = Intent (activity_class = AppDetail )
159178 intent .putExtra ("app" , app )
179+ intent .putExtra ("appstore" , self )
160180 self .startActivity (intent )
161181
162182 async def download_url (self , url ):
@@ -170,6 +190,86 @@ async def download_url(self, url):
170190 except Exception as e :
171191 print (f"download_url got exception { e } " )
172192
193+ @staticmethod
194+ def badgehub_app_to_mpos_app (bhapp ):
195+ #print(f"Converting {bhapp} to MPOS app object...")
196+ name = bhapp .get ("name" )
197+ print (f"Got app name: { name } " )
198+ publisher = None
199+ short_description = bhapp .get ("description" )
200+ long_description = None
201+ try :
202+ icon_url = bhapp .get ("icon_map" ).get ("64x64" ).get ("url" )
203+ except Exception as e :
204+ icon_url = None
205+ print ("Could not find icon_map 64x64 url" )
206+ download_url = None
207+ fullname = bhapp .get ("slug" )
208+ version = None
209+ try :
210+ category = bhapp .get ("categories" )[0 ]
211+ except Exception as e :
212+ category = None
213+ print ("Could not parse category" )
214+ activities = None
215+ return App (name , publisher , short_description , long_description , icon_url , download_url , fullname , version , category , activities )
216+
217+ async def fetch_badgehub_app_details (self , app_obj ):
218+ details_url = self .app_detail_url_badgehub + "/" + app_obj .fullname
219+ response = await self .download_url (details_url )
220+ if not response :
221+ print (f"Could not download app details from from\n { details_url } " )
222+ return
223+ print (f"Got response text: { response [0 :20 ]} " )
224+ try :
225+ parsed = json .loads (response )
226+ print (f"parsed json: { parsed } " )
227+ print ("Using short_description as long_description because backend doesn't support it..." )
228+ app_obj .long_description = app_obj .short_description
229+ print ("Finding version number..." )
230+ try :
231+ version = parsed .get ("version" )
232+ except Exception as e :
233+ print (f"Could not get version object from appdetails: { e } " )
234+ return
235+ print (f"got version object: { version } " )
236+ # Find .mpk download URL:
237+ try :
238+ files = version .get ("files" )
239+ for file in files :
240+ print (f"parsing file: { file } " )
241+ ext = file .get ("ext" ).lower ()
242+ print (f"file has extension: { ext } " )
243+ if ext == ".mpk" :
244+ app_obj .download_url = file .get ("url" )
245+ break # only one .mpk per app is supported
246+ except Exception as e :
247+ print (f"Could not get files from version: { e } " )
248+ try :
249+ app_metadata = version .get ("app_metadata" )
250+ except Exception as e :
251+ print (f"Could not get app_metadata object from version object: { e } " )
252+ return
253+ try :
254+ author = app_metadata .get ("author" )
255+ print ("Using author as publisher because that's all the backend supports..." )
256+ app_obj .publisher = author
257+ except Exception as e :
258+ print (f"Could not get author from version object: { e } " )
259+ try :
260+ app_version = app_metadata .get ("version" )
261+ print (f"what: { version .get ('app_metadata' )} " )
262+ print (f"app has app_version: { app_version } " )
263+ app_obj .version = app_version
264+ except Exception as e :
265+ print (f"Could not get version from app_metadata: { e } " )
266+ except Exception as e :
267+ err = f"ERROR: could not parse app details JSON: { e } "
268+ print (err )
269+ self .please_wait_label .set_text (err )
270+ return
271+
272+
173273class AppDetail (Activity ):
174274
175275 action_label_install = "Install"
@@ -182,10 +282,19 @@ class AppDetail(Activity):
182282 update_button = None
183283 progress_bar = None
184284 install_label = None
285+ long_desc_label = None
286+ version_label = None
287+ buttoncont = None
288+ publisher_label = None
289+
290+ # Received from the Intent extras:
291+ app = None
292+ appstore = None
185293
186294 def onCreate (self ):
187295 print ("Creating app detail screen..." )
188- app = self .getIntent ().extras .get ("app" )
296+ self .app = self .getIntent ().extras .get ("app" )
297+ self .appstore = self .getIntent ().extras .get ("appstore" )
189298 app_detail_screen = lv .obj ()
190299 app_detail_screen .set_style_pad_all (5 , 0 )
191300 app_detail_screen .set_size (lv .pct (100 ), lv .pct (100 ))
@@ -200,10 +309,10 @@ def onCreate(self):
200309 headercont .set_scrollbar_mode (lv .SCROLLBAR_MODE .OFF )
201310 icon_spacer = lv .image (headercont )
202311 icon_spacer .set_size (64 , 64 )
203- if app .icon_data :
312+ if self . app .icon_data :
204313 image_dsc = lv .image_dsc_t ({
205- 'data_size' : len (app .icon_data ),
206- 'data' : app .icon_data
314+ 'data_size' : len (self . app .icon_data ),
315+ 'data' : self . app .icon_data
207316 })
208317 icon_spacer .set_src (image_dsc )
209318 else :
@@ -216,54 +325,80 @@ def onCreate(self):
216325 detail_cont .set_size (lv .pct (75 ), lv .SIZE_CONTENT )
217326 detail_cont .set_scrollbar_mode (lv .SCROLLBAR_MODE .OFF )
218327 name_label = lv .label (detail_cont )
219- name_label .set_text (app .name )
328+ name_label .set_text (self . app .name )
220329 name_label .set_style_text_font (lv .font_montserrat_24 , 0 )
221- publisher_label = lv .label (detail_cont )
222- publisher_label .set_text (app .publisher )
223- publisher_label .set_style_text_font (lv .font_montserrat_16 , 0 )
330+ self .publisher_label = lv .label (detail_cont )
331+ if self .app .publisher :
332+ self .publisher_label .set_text (self .app .publisher )
333+ else :
334+ self .publisher_label .set_text ("Unknown publisher" )
335+ self .publisher_label .set_style_text_font (lv .font_montserrat_16 , 0 )
224336
225337 self .progress_bar = lv .bar (app_detail_screen )
226338 self .progress_bar .set_width (lv .pct (100 ))
227339 self .progress_bar .set_range (0 , 100 )
228340 self .progress_bar .add_flag (lv .obj .FLAG .HIDDEN )
229341 # Always have this button:
230- buttoncont = lv .obj (app_detail_screen )
231- buttoncont .set_style_border_width (0 , 0 )
232- buttoncont .set_style_radius (0 , 0 )
233- buttoncont .set_style_pad_all (0 , 0 )
234- buttoncont .set_flex_flow (lv .FLEX_FLOW .ROW )
235- buttoncont .set_size (lv .pct (100 ), lv .SIZE_CONTENT )
236- buttoncont .set_scrollbar_mode (lv .SCROLLBAR_MODE .OFF )
237- print (f"Adding (un)install button for url: { app .download_url } " )
342+ self .buttoncont = lv .obj (app_detail_screen )
343+ self .buttoncont .set_style_border_width (0 , 0 )
344+ self .buttoncont .set_style_radius (0 , 0 )
345+ self .buttoncont .set_style_pad_all (0 , 0 )
346+ self .buttoncont .set_flex_flow (lv .FLEX_FLOW .ROW )
347+ self .buttoncont .set_size (lv .pct (100 ), lv .SIZE_CONTENT )
348+ self .buttoncont .set_scrollbar_mode (lv .SCROLLBAR_MODE .OFF )
349+ self .add_action_buttons (self .buttoncont , self .app )
350+ # version label:
351+ self .version_label = lv .label (app_detail_screen )
352+ self .version_label .set_width (lv .pct (100 ))
353+ if self .app .version :
354+ self .version_label .set_text (f"Latest version: { self .app .version } " ) # would be nice to make this bold if this is newer than the currently installed one
355+ else :
356+ self .version_label .set_text (f"Unknown version" )
357+ self .version_label .set_style_text_font (lv .font_montserrat_12 , 0 )
358+ self .version_label .align_to (self .install_button , lv .ALIGN .OUT_BOTTOM_MID , 0 , lv .pct (5 ))
359+ self .long_desc_label = lv .label (app_detail_screen )
360+ self .long_desc_label .align_to (self .version_label , lv .ALIGN .OUT_BOTTOM_MID , 0 , lv .pct (5 ))
361+ if self .app .long_description :
362+ self .long_desc_label .set_text (self .app .long_description )
363+ else :
364+ self .long_desc_label .set_text (self .app .short_description )
365+ self .long_desc_label .set_style_text_font (lv .font_montserrat_12 , 0 )
366+ self .long_desc_label .set_width (lv .pct (100 ))
367+ print ("Loading app detail screen..." )
368+ self .setContentView (app_detail_screen )
369+
370+ def onResume (self , screen ):
371+ if self .appstore .backend_api == self .appstore ._BACKEND_API_BADGEHUB :
372+ TaskManager .create_task (self .fetch_and_set_app_details ())
373+ else :
374+ print ("No need to fetch app details as the github app index already contains all the app data." )
375+
376+ def add_action_buttons (self , buttoncont , app ):
377+ buttoncont .clean ()
378+ print (f"Adding (un)install button for url: { self .app .download_url } " )
238379 self .install_button = lv .button (buttoncont )
239- self .install_button .add_event_cb (lambda e , d = app . download_url , f = app . fullname : self .toggle_install (d , f ), lv .EVENT .CLICKED , None )
380+ self .install_button .add_event_cb (lambda e , a = self . app : self .toggle_install (a ), lv .EVENT .CLICKED , None )
240381 self .install_button .set_size (lv .pct (100 ), 40 )
241382 self .install_label = lv .label (self .install_button )
242383 self .install_label .center ()
243- self .set_install_label (app .fullname )
244- if PackageManager .is_update_available (app .fullname , app .version ):
384+ self .set_install_label (self . app .fullname )
385+ if app . version and PackageManager .is_update_available (self . app .fullname , app .version ):
245386 self .install_button .set_size (lv .pct (47 ), 40 ) # make space for update button
246387 print ("Update available, adding update button." )
247388 self .update_button = lv .button (buttoncont )
248389 self .update_button .set_size (lv .pct (47 ), 40 )
249- self .update_button .add_event_cb (lambda e , d = app . download_url , f = app . fullname : self .update_button_click (d , f ), lv .EVENT .CLICKED , None )
390+ self .update_button .add_event_cb (lambda e , a = self . app : self .update_button_click (a ), lv .EVENT .CLICKED , None )
250391 update_label = lv .label (self .update_button )
251392 update_label .set_text ("Update" )
252393 update_label .center ()
253- # version label:
254- version_label = lv .label (app_detail_screen )
255- version_label .set_width (lv .pct (100 ))
256- version_label .set_text (f"Latest version: { app .version } " ) # make this bold if this is newer than the currently installed one
257- version_label .set_style_text_font (lv .font_montserrat_12 , 0 )
258- version_label .align_to (self .install_button , lv .ALIGN .OUT_BOTTOM_MID , 0 , lv .pct (5 ))
259- long_desc_label = lv .label (app_detail_screen )
260- long_desc_label .align_to (version_label , lv .ALIGN .OUT_BOTTOM_MID , 0 , lv .pct (5 ))
261- long_desc_label .set_text (app .long_description )
262- long_desc_label .set_style_text_font (lv .font_montserrat_12 , 0 )
263- long_desc_label .set_width (lv .pct (100 ))
264- print ("Loading app detail screen..." )
265- self .setContentView (app_detail_screen )
266-
394+
395+ async def fetch_and_set_app_details (self ):
396+ await self .appstore .fetch_badgehub_app_details (self .app )
397+ print (f"app has version: { self .app .version } " )
398+ self .version_label .set_text (self .app .version )
399+ self .long_desc_label .set_text (self .app .long_description )
400+ self .publisher_label .set_text (self .app .publisher )
401+ self .add_action_buttons (self .buttoncont , self .app )
267402
268403 def set_install_label (self , app_fullname ):
269404 # Figure out whether to show:
@@ -292,8 +427,11 @@ def set_install_label(self, app_fullname):
292427 action_label = self .action_label_install
293428 self .install_label .set_text (action_label )
294429
295- def toggle_install (self , download_url , fullname ):
296- print (f"Install button clicked for { download_url } and fullname { fullname } " )
430+ def toggle_install (self , app_obj ):
431+ print (f"Install button clicked for { app_obj } " )
432+ download_url = app_obj .download_url
433+ fullname = app_obj .fullname
434+ print (f"With { download_url } and fullname { fullname } " )
297435 label_text = self .install_label .get_text ()
298436 if label_text == self .action_label_install :
299437 try :
@@ -309,7 +447,9 @@ def toggle_install(self, download_url, fullname):
309447 except Exception as e :
310448 print ("Could not start uninstall_app thread: " , e )
311449
312- def update_button_click (self , download_url , fullname ):
450+ def update_button_click (self , app_obj ):
451+ download_url = app_obj .download_url
452+ fullname = app_obj .fullname
313453 print (f"Update button clicked for { download_url } and fullname { fullname } " )
314454 self .update_button .add_flag (lv .obj .FLAG .HIDDEN )
315455 self .install_button .set_size (lv .pct (100 ), 40 )
0 commit comments