1414# Author: Tobias Sterbak
1515
1616import zipfile
17+ from dataclasses import dataclass
18+ from enum import Enum
1719from typing import Optional , List
1820
1921import requests
2022from loguru import logger
2123
2224
25+ class CompatibilityStatus (Enum ):
26+ """Enum for the compatibility status of a device."""
27+
28+ UNKNOWN = 0
29+ COMPATIBLE = 1
30+ INCOMPATIBLE = 2
31+
32+
33+ @dataclass
34+ class CheckResult :
35+ """Dataclass for the result of a check.
36+
37+ Attributes:
38+ status: Compatibility status of the device.
39+ message: Message to be displayed to the user.
40+ """
41+
42+ status : CompatibilityStatus
43+ message : str
44+
45+
2346def get_download_link (devicecode : str ) -> Optional [str ]:
2447 """Check if a lineageOS version for this device exists on download.lineageos.com and return the respective download link."""
2548 url = f"https://download.lineageos.org/api/v2/devices/{ devicecode } "
@@ -40,54 +63,101 @@ def get_download_link(devicecode: str) -> Optional[str]:
4063 return None
4164
4265
43- def image_works_with_device ( supported_device_codes : List [ str ], image_path : str ) -> bool :
44- """Determine if an image works for the given device .
66+ def retrieve_image_metadata ( image_path : str ) -> dict :
67+ """Retrieve metadata from the selected image .
4568
4669 Args:
47- supported_device_codes: List of supported device codes from the config file.
4870 image_path: Path to the image file.
4971
5072 Returns:
51- True if the image works with the device, False otherwise .
73+ Dictionary containing the metadata .
5274 """
53- with zipfile .ZipFile (image_path ) as image_zip :
54- with image_zip .open (
55- "META-INF/com/android/metadata" , mode = "r"
56- ) as image_metadata :
57- metadata = image_metadata .readlines ()
58- supported_devices = str (metadata [- 1 ]).split ("=" )[- 1 ][:- 3 ].split ("," )
59- logger .info (f"Image works with device: { supported_devices } " )
60-
61- if any (code in supported_devices for code in supported_device_codes ):
62- logger .success ("Device supported by the selected image." )
63- return True
64- else :
65- logger .error (
66- f"Image file { image_path .split ('/' )[- 1 ]} is not supported."
67- )
68- return False
75+ metapath = "META-INF/com/android/metadata"
76+ try :
77+ with zipfile .ZipFile (image_path ) as image_zip :
78+ with image_zip .open (metapath , mode = "r" ) as image_metadata :
79+ metadata = image_metadata .readlines ()
80+ metadata_dict = {}
81+ for line in metadata :
82+ metadata_dict [line [: line .find (b"=" )].decode ("utf-8" )] = line [
83+ line .find (b"=" ) + 1 : - 1
84+ ].decode ("utf-8" )
85+ logger .info (f"Metadata retrieved from image { image_path .split ('/' )[- 1 ]} ." )
86+ return metadata_dict
87+ except (FileNotFoundError , KeyError ):
88+ logger .error (
89+ f"Metadata file { metapath } not found in { image_path .split ('/' )[- 1 ]} ."
90+ )
91+ return dict ()
6992
7093
7194def image_sdk_level (image_path : str ) -> int :
7295 """Determine Android version of the selected image.
7396
74- Example:
97+ Examples:
98+ Android 10: 29
99+ Android 11: 30
100+ Android 12: 31
101+ Android 12.1: 32
75102 Android 13: 33
103+
104+ Args:
105+ image_path: Path to the image file.
106+
107+ Returns:
108+ Android version as integer.
76109 """
77- with zipfile .ZipFile (image_path ) as image_zip :
78- with image_zip .open (
79- "META-INF/com/android/metadata" , mode = "r"
80- ) as image_metadata :
81- metadata = image_metadata .readlines ()
82- for line in metadata :
83- if b"sdk-level" in line :
84- return int (line [line .find (b"=" ) + 1 : - 1 ].decode ("utf-8" ))
85- return 0
110+ metadata = retrieve_image_metadata (image_path )
111+ try :
112+ sdk_level = metadata ["post-sdk-level" ]
113+ logger .info (f"Android version of { image_path } : { sdk_level } " )
114+ return int (sdk_level )
115+ except (ValueError , TypeError , KeyError ) as e :
116+ logger .error (f"Could not determine Android version of { image_path } . Error: { e } " )
117+ return - 1
118+
119+
120+ def image_works_with_device (
121+ supported_device_codes : List [str ], image_path : str
122+ ) -> CheckResult :
123+ """Determine if an image works for the given device.
124+
125+ Args:
126+ supported_device_codes: List of supported device codes from the config file.
127+ image_path: Path to the image file.
128+
129+ Returns:
130+ CheckResult object containing the compatibility status and a message.
131+ """
132+ metadata = retrieve_image_metadata (image_path )
133+ try :
134+ supported_devices = metadata ["pre-device" ].split ("," )
135+ logger .info (f"Image works with the following device(s): { supported_devices } " )
136+ if any (code in supported_devices for code in supported_device_codes ):
137+ logger .success ("Device supported by the selected image." )
138+ return CheckResult (
139+ CompatibilityStatus .COMPATIBLE ,
140+ "Device supported by the selected image." ,
141+ )
142+ else :
143+ logger .error (f"Image file { image_path .split ('/' )[- 1 ]} is not supported." )
144+ return CheckResult (
145+ CompatibilityStatus .INCOMPATIBLE ,
146+ f"Image file { image_path .split ('/' )[- 1 ]} is not supported by device code." ,
147+ )
148+ except KeyError :
149+ logger .error (
150+ f"Could not determine supported devices for { image_path .split ('/' )[- 1 ]} ."
151+ )
152+ return CheckResult (
153+ CompatibilityStatus .UNKNOWN ,
154+ f"Could not determine supported devices for { image_path .split ('/' )[- 1 ]} . Missing metadata file? You may try to flash the image anyway." ,
155+ )
86156
87157
88158def recovery_works_with_device (
89159 supported_device_codes : List [str ], recovery_path : str
90- ) -> bool :
160+ ) -> CheckResult :
91161 """Determine if a recovery works for the given device.
92162
93163 BEWARE: THE RECOVERY PART IS STILL VERY BASIC!
@@ -97,14 +167,19 @@ def recovery_works_with_device(
97167 recovery_path: Path to the recovery file.
98168
99169 Returns:
100- True if the recovery works with the device, False otherwise .
170+ CheckResult object containing the compatibility status and a message .
101171 """
102172 recovery_file_name = recovery_path .split ("/" )[- 1 ]
103173 if any (code in recovery_file_name for code in supported_device_codes ) and (
104174 "twrp" in recovery_file_name
105175 ):
106176 logger .success ("Device supported by the selected recovery." )
107- return True
177+ return CheckResult (
178+ CompatibilityStatus .COMPATIBLE , "Device supported by the selected recovery."
179+ )
108180 else :
109181 logger .error (f"Recovery file { recovery_file_name } is not supported." )
110- return False
182+ return CheckResult (
183+ CompatibilityStatus .INCOMPATIBLE ,
184+ f"Recovery file { recovery_file_name } is not supported by device code in file name." ,
185+ )
0 commit comments