Skip to content

Commit 5fde5a8

Browse files
Fix appstore, icons etc
1 parent cff1421 commit 5fde5a8

10 files changed

Lines changed: 127 additions & 125 deletions

File tree

internal_filesystem/apps/com.lightningpiggy.displaywallet/META-INF/MANIFEST.JSON

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
"publisher": "LightningPiggy Foundation",
44
"short_description": "Display wallet that shows balance, transactions, receive QR code etc.",
55
"long_description": "See https://www.LightningPiggy.com",
6-
"icon_url": "https://apps.micropythonos.com/icons/com.lightningpiggy.displaywallet_0.0.1.mpk_icon_64x64.png",
7-
"download_url": "https://apps.micropythonos.com/mpks/com.lightningpiggy.displaywallet_0.0.1.mpk",
6+
"icon_url": "https://apps.micropythonos.com/com.lightningpiggy.displaywallet/icons/com.lightningpiggy.displaywallet_0.0.1_64x64.png",
7+
"download_url": "https://apps.micropythonos.com/com.lightningpiggy.displaywallet/mpks/com.lightningpiggy.displaywallet_0.0.1.mpk",
88
"fullname": "com.lightningpiggy.displaywallet",
99
"version": "0.0.1",
1010
"category": "finance",
1111
"activities": [
1212
{
1313
"entrypoint": "assets/displaywallet.py",
14-
"classname": "MainActivity",
14+
"classname": "DisplayWallet",
1515
"intent_filters": [
1616
{
1717
"action": "main",

internal_filesystem/apps/com.lightningpiggy.displaywallet/assets/displaywallet.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from wallet import LNBitsWallet, NWCWallet
55
from captureqr import Camera
66

7-
class MainActivity(Activity):
7+
class DisplayWallet(Activity):
88

99
def __init__(self):
1010
super().__init__()

internal_filesystem/apps/com.micropythonos.camera/META-INF/MANIFEST.JSON

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
"publisher": "MicroPythonOS",
44
"short_description": "Camera with QR decoding",
55
"long_description": "Camera for both internal camera's and webcams, that includes QR decoding.",
6-
"icon_url": "https://apps.micropythonos.com/com.micropython.camera_0.0.3.mpk_icon_64x64.png",
7-
"download_url": "https://apps.micropythonos.com/com.micropython.camera_0.0.3.mpk",
6+
"icon_url": "https://apps.micropythonos.com/com.micropython.camera/icons/com.micropython.camera_0.0.3_64x64.png",
7+
"download_url": "https://apps.micropythonos.com/com.micropython.camera/mpks/com.micropython.camera_0.0.3.mpk",
88
"fullname": "com.micropython.camera",
99
"version": "0.0.3",
1010
"category": "camera",

internal_filesystem/builtin/apps/com.micropythonos.appstore/META-INF/MANIFEST.JSON

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
"publisher": "MicroPythonOS",
44
"short_description": "Store for App(lication)s",
55
"long_description": "",
6-
"icon_url": "https://apps.micropythonos.com/icons/com.micropythonos.appstore_0.0.3.mpk_icon_64x64.png",
7-
"download_url": "https://apps.micropythonos.com/mpks/com.micropythonos.appstore_0.0.3.mpk",
6+
"icon_url": "https://apps.micropythonos.com/com.micropythonos.appstore/icons/com.micropythonos.appstore_0.0.3_64x64.png",
7+
"download_url": "https://apps.micropythonos.com/com.micropythonos.appstore/mpks/com.micropythonos.appstore_0.0.3.mpk",
88
"fullname": "com.micropythonos.appstore",
99
"version": "0.0.3",
1010
"category": "appstore",
1111
"activities": [
1212
{
1313
"entrypoint": "assets/appstore.py",
14-
"classname": "MainActivity",
14+
"classname": "AppStore",
1515
"intent_filters": [
1616
{
1717
"action": "main",

internal_filesystem/builtin/apps/com.micropythonos.appstore/assets/appstore.py

Lines changed: 109 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,18 @@
99
from mpos.apps import Activity, Intent
1010
import mpos.ui
1111

12-
# Screens:
13-
app_detail_screen = None
14-
main_screen = None
15-
16-
action_label_install = "Install"
17-
action_label_uninstall = "Uninstall"
18-
action_label_restore = "Restore Built-in"
19-
action_label_nothing = "Disable" # This doesn't do anything at the moment, but it could mark builtin apps as "Disabled" somehow and also allow for "Enable" then
12+
class AppStore(Activity):
13+
apps = []
14+
app_index_url = "https://apps.micropythonos.com/app_index.json"
15+
can_check_network = True
2016

21-
class MainActivity(Activity):
17+
# Widgets:
2218
main_screen = None
23-
apps = []
2419
update_button = None
2520
install_button = None
2621
install_label = None
2722
please_wait_label = None
2823
progress_bar = None
29-
can_check_network = True
3024

3125
def onCreate(self):
3226
self.main_screen = lv.obj()
@@ -44,7 +38,7 @@ def onStart(self, screen):
4438
self.please_wait_label.set_text("Error: WiFi is not connected.")
4539
else:
4640
_thread.stack_size(mpos.apps.good_stack_size())
47-
_thread.start_new_thread(self.download_app_index, ("https://apps.micropythonos.com/app_index.json",))
41+
_thread.start_new_thread(self.download_app_index, (self.app_index_url,))
4842

4943
def onDestroy(self, screen):
5044
print("appstore.py destroyed, restarting launcher to refresh...")
@@ -81,7 +75,7 @@ def download_app_index(self, json_url):
8175

8276
def create_apps_list(self):
8377
print("create_apps_list")
84-
default_icon_dsc = load_icon("builtin/res/mipmap-mdpi/default_icon_64x64.png")
78+
default_icon_dsc = self.load_icon("builtin/res/mipmap-mdpi/default_icon_64x64.png")
8579
apps_list = lv.list(self.main_screen)
8680
apps_list.set_style_pad_all(0, 0)
8781
apps_list.set_size(lv.pct(100), lv.pct(100))
@@ -123,8 +117,11 @@ def create_apps_list(self):
123117

124118
def download_icons(self):
125119
for app in self.apps:
120+
if app.image_dsc:
121+
print(f"Skipping icon download for {app.name} because already downloaded.")
122+
continue
126123
print(f"Downloading icon for {app.name}")
127-
image_dsc = download_icon(app.icon_url)
124+
image_dsc = self.download_icon(app.icon_url)
128125
app.image_dsc = image_dsc # save it for the app detail page
129126
lv.async_call(lambda l: app.image.set_src(image_dsc), None)
130127
time.sleep_ms(100) # not waiting here will result in some async_calls() not being executed
@@ -135,7 +132,34 @@ def show_app_detail(self, app):
135132
intent.putExtra("app", app)
136133
self.startActivity(intent)
137134

138-
135+
@staticmethod
136+
def download_icon(url):
137+
print(f"Downloading icon from {url}")
138+
try:
139+
response = requests.get(url, timeout=5)
140+
if response.status_code == 200:
141+
image_data = response.content
142+
print("Downloaded image, size:", len(image_data), "bytes")
143+
image_dsc = lv.image_dsc_t({
144+
'data_size': len(image_data),
145+
'data': image_data
146+
})
147+
return image_dsc
148+
else:
149+
print("Failed to download image: Status code", response.status_code)
150+
except Exception as e:
151+
print(f"Exception during download of icon: {e}")
152+
return None
153+
154+
@staticmethod
155+
def load_icon(icon_path):
156+
with open(icon_path, 'rb') as f:
157+
image_data = f.read()
158+
image_dsc = lv.image_dsc_t({
159+
'data_size': len(image_data),
160+
'data': image_data
161+
})
162+
return image_dsc
139163

140164
class AppDetail(Activity):
141165

@@ -144,13 +168,20 @@ class AppDetail(Activity):
144168
except ImportError:
145169
zipfile = None
146170

171+
action_label_install = "Install"
172+
action_label_uninstall = "Uninstall"
173+
action_label_restore = "Restore Built-in"
174+
action_label_nothing = "Disable" # This could mark builtin apps as "Disabled" somehow and also allow for "Enable" then
175+
176+
# Widgets:
147177
install_button = None
148178
update_button = None
149179
progress_bar = None
150180
install_label = None
151181

152182
def onCreate(self):
153183
print("Creating app detail screen...")
184+
app = self.getIntent().extras.get("app")
154185
app_detail_screen = lv.obj()
155186
app_detail_screen.set_size(lv.pct(100), lv.pct(100))
156187
app_detail_screen.set_pos(0, 40)
@@ -194,11 +225,10 @@ def onCreate(self):
194225
self.install_button.set_size(lv.pct(100), 40)
195226
self.install_label = lv.label(self.install_button)
196227
self.install_label.center()
197-
set_install_label(app.fullname)
198-
if is_update_available(app.fullname, app.version):
228+
self.set_install_label(app.fullname)
229+
if self.is_update_available(app.fullname, app.version):
199230
self.install_button.set_size(lv.pct(47), 40) # make space for update button
200231
print("Update available, adding update button.")
201-
global update_button
202232
self.update_button = lv.button(buttoncont)
203233
self.update_button.set_size(lv.pct(47), 40)
204234
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)
@@ -210,7 +240,7 @@ def onCreate(self):
210240
version_label.set_width(lv.pct(100))
211241
version_label.set_text(f"Latest version: {app.version}") # make this bold if this is newer than the currently installed one
212242
version_label.set_style_text_font(lv.font_montserrat_12, 0)
213-
version_label.align_to(install_button, lv.ALIGN.OUT_BOTTOM_MID, 0, lv.pct(5))
243+
version_label.align_to(self.install_button, lv.ALIGN.OUT_BOTTOM_MID, 0, lv.pct(5))
214244
long_desc_label = lv.label(app_detail_screen)
215245
long_desc_label.align_to(version_label, lv.ALIGN.OUT_BOTTOM_MID, 0, lv.pct(5))
216246
long_desc_label.set_text(app.long_description)
@@ -231,8 +261,8 @@ def set_install_label(self, app_fullname):
231261
# - update is separate button, only shown if already installed and new version
232262
is_installed = True
233263
update_available = False
234-
builtin_app = is_builtin_app(app_fullname)
235-
overridden_builtin_app = is_overridden_builtin_app(app_fullname)
264+
builtin_app = self.is_builtin_app(app_fullname)
265+
overridden_builtin_app = self.is_overridden_builtin_app(app_fullname)
236266
if not overridden_builtin_app:
237267
is_installed = is_installed_by_name(app_fullname)
238268
if is_installed:
@@ -249,7 +279,6 @@ def set_install_label(self, app_fullname):
249279

250280

251281
def toggle_install(self, download_url, fullname):
252-
global install_label
253282
print(f"Install button clicked for {download_url} and fullname {fullname}")
254283
label_text = self.install_label.get_text()
255284
if label_text == action_label_install:
@@ -276,7 +305,7 @@ def update_button_click(self, download_url, fullname):
276305
except Exception as e:
277306
print("Could not start download_and_unzip thread: ", e)
278307

279-
def uninstall_app(app_folder, app_fullname):
308+
def uninstall_app(self, app_folder, app_fullname):
280309
self.install_button.remove_flag(lv.obj.FLAG.CLICKABLE) # TODO: change color so it's clear the button is not clickable
281310
self.install_label.set_text("Please wait...") # TODO: Put "Cancel" if cancellation is possible
282311
self.progress_bar.remove_flag(lv.obj.FLAG.HIDDEN)
@@ -295,12 +324,12 @@ def uninstall_app(app_folder, app_fullname):
295324
self.progress_bar.set_value(0, lv.ANIM.OFF)
296325
set_install_label(app_fullname)
297326
self.install_button.add_flag(lv.obj.FLAG.CLICKABLE)
298-
if is_builtin_app(app_fullname):
327+
if self.is_builtin_app(app_fullname):
299328
self.update_button.remove_flag(lv.obj.FLAG.HIDDEN)
300329
self.install_button.set_size(lv.pct(47), 40) # if a builtin app was removed, then it was overridden, and a new version is available, so make space for update button
301330

302331

303-
def download_and_unzip(zip_url, dest_folder, app_fullname):
332+
def download_and_unzip(self, zip_url, dest_folder, app_fullname):
304333
self.install_button.remove_flag(lv.obj.FLAG.CLICKABLE) # TODO: change color so it's clear the button is not clickable
305334
self.install_label.set_text("Please wait...") # TODO: Put "Cancel" if cancellation is possible
306335
self.progress_bar.remove_flag(lv.obj.FLAG.HIDDEN)
@@ -363,94 +392,65 @@ def download_and_unzip(zip_url, dest_folder, app_fullname):
363392
self.progress_bar.set_value(0, lv.ANIM.OFF)
364393
set_install_label(app_fullname)
365394
self.install_button.add_flag(lv.obj.FLAG.CLICKABLE)
366-
367-
368395

369-
# Non-class functions:
396+
@staticmethod
397+
def compare_versions(ver1: str, ver2: str) -> bool:
398+
"""Compare two version numbers (e.g., '1.2.3' vs '4.5.6').
399+
Returns True if ver1 is greater than ver2, False otherwise."""
400+
print(f"Comparing versions: {ver1} vs {ver2}")
401+
v1_parts = [int(x) for x in ver1.split('.')]
402+
v2_parts = [int(x) for x in ver2.split('.')]
403+
print(f"Version 1 parts: {v1_parts}")
404+
print(f"Version 2 parts: {v2_parts}")
405+
for i in range(max(len(v1_parts), len(v2_parts))):
406+
v1 = v1_parts[i] if i < len(v1_parts) else 0
407+
v2 = v2_parts[i] if i < len(v2_parts) else 0
408+
print(f"Comparing part {i}: {v1} vs {v2}")
409+
if v1 > v2:
410+
print(f"{ver1} is greater than {ver2}")
411+
return True
412+
if v1 < v2:
413+
print(f"{ver1} is less than {ver2}")
414+
return False
415+
print(f"Versions are equal or {ver1} is not greater than {ver2}")
416+
return False
370417

371-
def compare_versions(ver1: str, ver2: str) -> bool:
372-
"""Compare two version numbers (e.g., '1.2.3' vs '4.5.6').
373-
Returns True if ver1 is greater than ver2, False otherwise."""
374-
print(f"Comparing versions: {ver1} vs {ver2}")
375-
v1_parts = [int(x) for x in ver1.split('.')]
376-
v2_parts = [int(x) for x in ver2.split('.')]
377-
print(f"Version 1 parts: {v1_parts}")
378-
print(f"Version 2 parts: {v2_parts}")
379-
for i in range(max(len(v1_parts), len(v2_parts))):
380-
v1 = v1_parts[i] if i < len(v1_parts) else 0
381-
v2 = v2_parts[i] if i < len(v2_parts) else 0
382-
print(f"Comparing part {i}: {v1} vs {v2}")
383-
if v1 > v2:
384-
print(f"{ver1} is greater than {ver2}")
385-
return True
386-
if v1 < v2:
387-
print(f"{ver1} is less than {ver2}")
418+
@staticmethod
419+
def is_builtin_app(app_fullname):
420+
return AppStore.is_installed_by_path(f"builtin/apps/{app_fullname}")
421+
422+
@staticmethod
423+
def is_overridden_builtin_app(app_fullname):
424+
return AppStore.is_installed_by_path(f"apps/{app_fullname}") and AppStore.is_installed_by_path(f"builtin/apps/{app_fullname}")
425+
426+
@staticmethod
427+
def is_update_available(app_fullname, new_version):
428+
appdir = f"apps/{app_fullname}"
429+
builtinappdir = f"builtin/apps/{app_fullname}"
430+
installed_app=None
431+
if AppStore.is_installed_by_path(appdir):
432+
print(f"{appdir} found, getting version...")
433+
installed_app = mpos.apps.parse_manifest(f"{appdir}/META-INF/MANIFEST.JSON")
434+
elif AppStore.is_installed_by_path(builtinappdir):
435+
print(f"{builtinappdir} found, getting version...")
436+
installed_app = mpos.apps.parse_manifest(f"{builtinappdir}/META-INF/MANIFEST.JSON")
437+
if not installed_app or installed_app.version == "0.0.0": # special case, if the installed app doesn't have a version number then there's no update
388438
return False
389-
print(f"Versions are equal or {ver1} is not greater than {ver2}")
390-
return False
439+
return compare_versions(new_version, installed_app.version)
391440

392-
def is_builtin_app(app_fullname):
393-
return is_installed_by_path(f"builtin/apps/{app_fullname}")
394-
395-
def is_overridden_builtin_app(app_fullname):
396-
return is_installed_by_path(f"apps/{app_fullname}") and is_installed_by_path(f"builtin/apps/{app_fullname}")
397-
398-
def is_update_available(app_fullname, new_version):
399-
appdir = f"apps/{app_fullname}"
400-
builtinappdir = f"builtin/apps/{app_fullname}"
401-
installed_app=None
402-
if is_installed_by_path(appdir):
403-
print(f"{appdir} found, getting version...")
404-
installed_app = mpos.apps.parse_manifest(f"{appdir}/META-INF/MANIFEST.JSON")
405-
elif is_installed_by_path(builtinappdir):
406-
print(f"{builtinappdir} found, getting version...")
407-
installed_app = mpos.apps.parse_manifest(f"{builtinappdir}/META-INF/MANIFEST.JSON")
408-
if not installed_app or installed_app.version == "0.0.0": # special case, if the installed app doesn't have a version number then there's no update
441+
@staticmethod
442+
def is_installed_by_path(dir_path):
443+
try:
444+
if os.stat(dir_path)[0] & 0x4000:
445+
manifest = f"{dir_path}/META-INF/MANIFEST.JSON"
446+
if os.stat(manifest)[0] & 0x8000:
447+
return True
448+
except OSError:
449+
pass # Skip if directory or manifest doesn't exist
409450
return False
410-
return compare_versions(new_version, installed_app.version)
411-
412-
413-
def is_installed_by_path(dir_path):
414-
try:
415-
if os.stat(dir_path)[0] & 0x4000:
416-
manifest = f"{dir_path}/META-INF/MANIFEST.JSON"
417-
if os.stat(manifest)[0] & 0x8000:
418-
return True
419-
except OSError:
420-
pass # Skip if directory or manifest doesn't exist
421-
return False
422-
423-
def is_installed_by_name(app_fullname):
424-
print(f"Checking if app {app_fullname} is installed...")
425-
return is_installed_by_path(f"apps/{app_fullname}") or is_installed_by_path(f"builtin/apps/{app_fullname}")
426-
427-
428-
def download_icon(url):
429-
print(f"Downloading icon from {url}")
430-
try:
431-
response = requests.get(url, timeout=5)
432-
if response.status_code == 200:
433-
image_data = response.content
434-
print("Downloaded image, size:", len(image_data), "bytes")
435-
image_dsc = lv.image_dsc_t({
436-
'data_size': len(image_data),
437-
'data': image_data
438-
})
439-
return image_dsc
440-
else:
441-
print("Failed to download image: Status code", response.status_code)
442-
except Exception as e:
443-
print(f"Exception during download of icon: {e}")
444-
return None
445-
446-
447-
def load_icon(icon_path):
448-
with open(icon_path, 'rb') as f:
449-
image_data = f.read()
450-
image_dsc = lv.image_dsc_t({
451-
'data_size': len(image_data),
452-
'data': image_data
453-
})
454-
return image_dsc
455451

452+
@staticmethod
453+
def is_installed_by_name(app_fullname):
454+
print(f"Checking if app {app_fullname} is installed...")
455+
return AppStore.is_installed_by_path(f"apps/{app_fullname}") or AppStore.is_installed_by_path(f"builtin/apps/{app_fullname}")
456456

0 commit comments

Comments
 (0)