Skip to content

Commit d0b6c6f

Browse files
committed
Rework the user messages of the image/recovery selection and the validation process
1 parent cc7444c commit d0b6c6f

File tree

2 files changed

+102
-60
lines changed

2 files changed

+102
-60
lines changed

openandroidinstaller/utils.py

Lines changed: 75 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,35 @@
1414
# Author: Tobias Sterbak
1515

1616
import zipfile
17+
from dataclasses import dataclass
18+
from enum import Enum
1719
from typing import Optional, List
1820

1921
import requests
2022
from 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+
2346
def 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}"
@@ -61,65 +84,80 @@ def retrieve_image_metadata(image_path: str) -> dict:
6184
].decode("utf-8")
6285
logger.info(f"Metadata retrieved from image {image_path.split('/')[-1]}.")
6386
return metadata_dict
64-
except FileNotFoundError:
87+
except (FileNotFoundError, KeyError):
6588
logger.error(
6689
f"Metadata file {metapath} not found in {image_path.split('/')[-1]}."
6790
)
6891
return dict()
6992

7093

71-
def image_works_with_device(supported_device_codes: List[str], image_path: str) -> bool:
94+
def image_sdk_level(image_path: str) -> int:
95+
"""Determine Android version of the selected image.
96+
97+
Examples:
98+
Android 10: 29
99+
Android 11: 30
100+
Android 12: 31
101+
Android 12.1: 32
102+
Android 13: 33
103+
104+
Args:
105+
image_path: Path to the image file.
106+
107+
Returns:
108+
Android version as integer.
109+
"""
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:
72123
"""Determine if an image works for the given device.
73124
74125
Args:
75126
supported_device_codes: List of supported device codes from the config file.
76127
image_path: Path to the image file.
77128
78129
Returns:
79-
True if the image works with the device, False otherwise.
130+
CheckResult object containing the compatibility status and a message.
80131
"""
81132
metadata = retrieve_image_metadata(image_path)
82133
try:
83134
supported_devices = metadata["pre-device"].split(",")
84135
logger.info(f"Image works with the following device(s): {supported_devices}")
85136
if any(code in supported_devices for code in supported_device_codes):
86137
logger.success("Device supported by the selected image.")
87-
return True
138+
return CheckResult(
139+
CompatibilityStatus.COMPATIBLE,
140+
"Device supported by the selected image.",
141+
)
88142
else:
89143
logger.error(f"Image file {image_path.split('/')[-1]} is not supported.")
90-
return False
144+
return CheckResult(
145+
CompatibilityStatus.INCOMPATIBLE,
146+
f"Image file {image_path.split('/')[-1]} is not supported by device code.",
147+
)
91148
except KeyError:
92149
logger.error(
93150
f"Could not determine supported devices for {image_path.split('/')[-1]}."
94151
)
95-
return False
96-
97-
98-
def image_sdk_level(image_path: str) -> int:
99-
"""Determine Android version of the selected image.
100-
101-
Example:
102-
Android 13: 33
103-
104-
Args:
105-
image_path: Path to the image file.
106-
107-
Returns:
108-
Android version as integer.
109-
"""
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 0
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+
)
118156

119157

120158
def recovery_works_with_device(
121159
supported_device_codes: List[str], recovery_path: str
122-
) -> bool:
160+
) -> CheckResult:
123161
"""Determine if a recovery works for the given device.
124162
125163
BEWARE: THE RECOVERY PART IS STILL VERY BASIC!
@@ -129,14 +167,19 @@ def recovery_works_with_device(
129167
recovery_path: Path to the recovery file.
130168
131169
Returns:
132-
True if the recovery works with the device, False otherwise.
170+
CheckResult object containing the compatibility status and a message.
133171
"""
134172
recovery_file_name = recovery_path.split("/")[-1]
135173
if any(code in recovery_file_name for code in supported_device_codes) and (
136174
"twrp" in recovery_file_name
137175
):
138176
logger.success("Device supported by the selected recovery.")
139-
return True
177+
return CheckResult(
178+
CompatibilityStatus.COMPATIBLE, "Device supported by the selected recovery."
179+
)
140180
else:
141181
logger.error(f"Recovery file {recovery_file_name} is not supported.")
142-
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+
)

openandroidinstaller/views/select_view.py

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
image_works_with_device,
4747
recovery_works_with_device,
4848
image_sdk_level,
49+
CheckResult,
50+
CompatibilityStatus,
4951
)
5052

5153

@@ -145,6 +147,9 @@ def init_visuals(
145147
icon=icons.ARROW_BACK,
146148
expand=True,
147149
)
150+
# store image and recovery compatibility
151+
self.image_compatibility: CheckResult | None = None
152+
self.recovery_compatibility: CheckResult | None = None
148153

149154
def build(self):
150155
self.clear()
@@ -533,17 +538,21 @@ def pick_image_result(self, e: FilePickerResultEvent):
533538
logger.info("No image selected.")
534539
# check if the image works with the device and show the filename in different colors accordingly
535540
if e.files:
536-
if image_works_with_device(
541+
self.image_compatibility = image_works_with_device(
537542
supported_device_codes=self.state.config.supported_device_codes,
538543
image_path=self.state.image_path,
539-
):
544+
)
545+
if self.image_compatibility.status == CompatibilityStatus.COMPATIBLE:
540546
self.selected_image.color = colors.GREEN
547+
elif self.image_compatibility.status == CompatibilityStatus.UNKNOWN:
548+
self.selected_image.color = colors.ORANGE
541549
else:
542550
self.selected_image.color = colors.RED
551+
self.selected_image.value += f"\n> {self.image_compatibility.message}"
543552
# if the image works and the sdk level is 33 or higher, show the additional image selection
544553
if self.state.flash_recovery:
545554
if (
546-
self.selected_image.color == colors.GREEN
555+
self.image_compatibility
547556
and image_sdk_level(self.state.image_path) >= 33
548557
):
549558
self.toggle_additional_image_selection()
@@ -567,13 +576,17 @@ def pick_recovery_result(self, e: FilePickerResultEvent):
567576
logger.info("No image selected.")
568577
# check if the recovery works with the device and show the filename in different colors accordingly
569578
if e.files:
570-
if recovery_works_with_device(
579+
self.recovery_compatibility = recovery_works_with_device(
571580
supported_device_codes=self.state.config.supported_device_codes,
572581
recovery_path=self.state.recovery_path,
573-
):
582+
)
583+
if self.recovery_compatibility.status == CompatibilityStatus.COMPATIBLE:
574584
self.selected_recovery.color = colors.GREEN
585+
elif self.recovery_compatibility.status == CompatibilityStatus.UNKNOWN:
586+
self.selected_recovery.color = colors.ORANGE
575587
else:
576588
self.selected_recovery.color = colors.RED
589+
self.selected_recovery.value += f"\n> {self.recovery_compatibility.message}"
577590
# update
578591
self.selected_recovery.update()
579592

@@ -654,23 +667,18 @@ def enable_button_if_ready(self, e):
654667
if (".zip" in self.selected_image.value) and (
655668
".img" in self.selected_recovery.value
656669
):
657-
if not (
658-
image_works_with_device(
659-
supported_device_codes=self.state.config.supported_device_codes,
660-
image_path=self.state.image_path,
661-
)
662-
and recovery_works_with_device(
663-
supported_device_codes=self.state.config.supported_device_codes,
664-
recovery_path=self.state.recovery_path,
665-
)
670+
if (
671+
self.image_compatibility.status == CompatibilityStatus.INCOMPATIBLE
672+
) or (
673+
self.recovery_compatibility.status == CompatibilityStatus.INCOMPATIBLE
666674
):
667675
# if image and recovery work for device allow to move on, otherwise display message
668676
logger.error(
669677
"Image and recovery don't work with the device. Please select different ones."
670678
)
671679
self.info_field.controls = [
672680
Text(
673-
"Image and/or recovery don't work with the device. Make sure you use a TWRP-based recovery.",
681+
"Something is wrong with the selected files.",
674682
color=colors.RED,
675683
weight="bold",
676684
)
@@ -695,12 +703,10 @@ def enable_button_if_ready(self, e):
695703
or "vendor_boot" not in self.state.config.additional_steps,
696704
]
697705
):
698-
logger.error(
699-
"Some additional images don't match or are missing. Please select different ones."
700-
)
706+
logger.error("Some additional images don't match or are missing.")
701707
self.info_field.controls = [
702708
Text(
703-
"Some additional images don't match or are missing. Please select the right ones.",
709+
"Some additional images don't match or are missing.",
704710
color=colors.RED,
705711
weight="bold",
706712
)
@@ -715,16 +721,9 @@ def enable_button_if_ready(self, e):
715721
self.continue_eitherway_button.disabled = True
716722
self.right_view.update()
717723
elif (".zip" in self.selected_image.value) and (not self.state.flash_recovery):
718-
if not (
719-
image_works_with_device(
720-
supported_device_codes=self.state.config.supported_device_codes,
721-
image_path=self.state.image_path,
722-
)
723-
):
724+
if self.image_compatibility.status != CompatibilityStatus.COMPATIBLE:
724725
# if image works for device allow to move on, otherwise display message
725-
logger.error(
726-
"Image doesn't work with the device. Please select a different one."
727-
)
726+
logger.error("Image doesn't work with the device.")
728727
self.info_field.controls = [
729728
Text(
730729
"Image doesn't work with the device.",

0 commit comments

Comments
 (0)