|
28 | 28 |
|
29 | 29 | ''' |
30 | 30 |
|
| 31 | +# PackageManager.py (MicroPython) |
31 | 32 |
|
32 | | -classPackageManager(): |
| 33 | +importos |
| 34 | + |
| 35 | +classPackageManager: |
| 36 | +"""Registry of all discovered apps. |
| 37 | +
|
| 38 | + * PackageManager.get_app_list() -> list of App objects (sorted by name) |
| 39 | + * PackageManager[fullname] -> App (raises KeyError if missing) |
| 40 | + * PackageManager.get(fullname) -> App or None |
| 41 | + """ |
33 | 42 |
|
34 | | -app_list= []# list of App objects, sorted alphabetically by app.name, unique by full_name (com.example.appname) |
| 43 | +# --------------------------------------------------------------------- # |
| 44 | +# internal storage |
| 45 | +# --------------------------------------------------------------------- # |
| 46 | +_app_list= []# sorted by app.name |
| 47 | +_by_fullname= {}# fullname -> App |
35 | 48 |
|
| 49 | +# --------------------------------------------------------------------- # |
| 50 | +# public list access (kept for backward compatibility) |
| 51 | +# --------------------------------------------------------------------- # |
36 | 52 | @classmethod |
37 | 53 | defget_app_list(cls): |
38 | | -iflen(cls.app_list)==0: |
| 54 | +ifnotcls._app_list: |
39 | 55 | cls.find_apps() |
40 | | -returncls.app_list |
| 56 | +returncls._app_list |
| 57 | + |
| 58 | +# --------------------------------------------------------------------- # |
| 59 | +# dict-style lookup: PackageManager["com.example.myapp"] |
| 60 | +# --------------------------------------------------------------------- # |
| 61 | +def__class_getitem__(cls,fullname): |
| 62 | +try: |
| 63 | +returncls._by_fullname[fullname] |
| 64 | +exceptKeyError: |
| 65 | +raiseKeyError("No app with fullname='{}'".format(fullname)) |
41 | 66 |
|
| 67 | +# --------------------------------------------------------------------- # |
| 68 | +# safe lookup: PackageManager.get("com.example.myapp") -> App or None |
| 69 | +# --------------------------------------------------------------------- # |
| 70 | +@classmethod |
| 71 | +defget(cls,fullname): |
| 72 | +returncls._by_fullname.get(fullname) |
| 73 | + |
| 74 | +# --------------------------------------------------------------------- # |
| 75 | +# discovery & population |
| 76 | +# --------------------------------------------------------------------- # |
42 | 77 | @classmethod |
43 | 78 | deffind_apps(cls): |
44 | 79 | print("\n\n\nPackageManager finding apps...") |
45 | | -seen_fullnames=set() |
46 | | -# Check and collect subdirectories from existing directories |
47 | | -apps_dir="apps" |
| 80 | + |
| 81 | +seen=set()# avoid processing the same fullname twice |
| 82 | +apps_dir="apps" |
48 | 83 | apps_dir_builtin="builtin/apps" |
49 | 84 |
|
50 | | -# Check and collect unique subdirectories |
51 | | -fordir_pathin [apps_dir,apps_dir_builtin]: |
| 85 | +forbasein (apps_dir,apps_dir_builtin): |
52 | 86 | try: |
53 | | -ifos.stat(dir_path)[0]&0x4000:# Verify directory exists |
| 87 | +# ---- does the directory exist? -------------------------------- |
| 88 | +st=os.stat(base) |
| 89 | +ifnot (st[0]&0x4000):# 0x4000 = directory bit |
| 90 | +continue |
| 91 | + |
| 92 | +# ---- iterate over immediate children ------------------------- |
| 93 | +fornameinos.listdir(base): |
| 94 | +full_path="{}/{}".format(base,name) |
| 95 | + |
| 96 | +# ---- is it a directory? --------------------------------- |
54 | 97 | try: |
55 | | -fordinos.listdir(dir_path): |
56 | | -full_path=f"{dir_path}/{d}" |
57 | | -print(f"full_path:{full_path}") |
58 | | -try: |
59 | | -ifos.stat(full_path)[0]&0x4000:# Check if it's a directory |
60 | | -fullname=d |
61 | | -iffullnamenotinseen_fullnames:# Avoid duplicates |
62 | | -seen_fullnames.add(fullname) |
63 | | -app=mpos.apps.parse_manifest(full_path) |
64 | | -cls.app_list.append(app) |
65 | | -print(f"added app{app}") |
66 | | -exceptExceptionase: |
67 | | -print(f"PackageManager: stat of{full_path} got exception:{e}") |
| 98 | +st=os.stat(full_path) |
| 99 | +ifnot (st[0]&0x4000): |
| 100 | +continue |
68 | 101 | exceptExceptionase: |
69 | | -print(f"PackageManager: listdir of{dir_path} got exception:{e}") |
| 102 | +print("PackageManager: stat of {} got exception: {}".format(full_path,e)) |
| 103 | +continue |
| 104 | + |
| 105 | +fullname=name |
| 106 | + |
| 107 | +# ---- skip duplicates ------------------------------------ |
| 108 | +iffullnameinseen: |
| 109 | +continue |
| 110 | +seen.add(fullname) |
| 111 | + |
| 112 | +# ---- parse the manifest --------------------------------- |
| 113 | +try: |
| 114 | +app=mpos.apps.parse_manifest(full_path) |
| 115 | +exceptExceptionase: |
| 116 | +print("PackageManager: parsing {} failed: {}".format(full_path,e)) |
| 117 | +continue |
| 118 | + |
| 119 | +# ---- store in both containers --------------------------- |
| 120 | +cls._app_list.append(app) |
| 121 | +cls._by_fullname[fullname]=app |
| 122 | +print("added app {}".format(app)) |
| 123 | + |
70 | 124 | exceptExceptionase: |
71 | | -print(f"PackageManager: stat of{dir_path} got exception:{e}") |
| 125 | +print("PackageManager: handling {} got exception: {}".format(base,e)) |
| 126 | + |
| 127 | +# ---- sort the list by display name (case-insensitive) ------------ |
| 128 | +cls._app_list.sort(key=lambdaa:a.name.lower()) |
72 | 129 |
|
73 | | -# Sort apps alphabetically by app.name |
74 | | -cls.app_list.sort(key=lambdax:x.name.lower())# Case-insensitive sorting by name |
75 | 130 |
|
76 | 131 | @staticmethod |
77 | 132 | defuninstall_app(app_fullname): |
|