diff --git a/README.md b/README.md index a49b1a4a..2282b9fc 100644 --- a/README.md +++ b/README.md @@ -42,14 +42,18 @@ Linux is currently the best supported platform (tested with Ubuntu 20.04/22.04 L 1. Download the [.exe or appropriate executable file for your OS](https://github.com/openandroidinstaller-dev/openandroidinstaller/releases) from the releases or get the [official flatpak from flathub](https://flathub.org/apps/org.openandroidinstaller.OpenAndroidInstaller). You might need to change permissions to run the executable. - On Windows also [install the Universal USB Drivers](https://adb.clockworkmod.com/) and other potentially drivers needed for your device. 2. Download the custom ROM image and the TWRP recovery image for your device and optionally some addons. A source for files can be found on the following websites: - - some custom ROMs: + - Some custom ROMs: - [LineageOS](https://wiki.lineageos.org/devices) - [/e/OS](https://doc.e.foundation/devices) - [LineageOS for microg](https://download.lineage.microg.org) - [BlissRoms](https://blissroms.org) - [PixelExperience](https://download.pixelexperience.org) - - TWRP Recovery: + - [crDroid](https://crdroid.net/) + - [ArrowOS](https://arrowos.net/) + - [DivestOS](https://divestos.org/) + - Recoveries: - [TWRP recovery](https://twrp.me/Devices) + - [OrangeFox](https://wiki.orangefox.tech) - Optional Addons: - There are different packages of *Google Apps* available. - [MindTheGapps](https://wiki.lineageos.org/gapps#downloads) @@ -214,6 +218,7 @@ Every config file should have `metadata` with the following fields: - `is_ab_device`: bool; A boolean to determine if the device is a/b-partitioned or not. - `device_code`: str; The official device code. - `supported_device_codes`: List[str]; A list of supported device codes for the config. The config will be loaded based on this field. +- `supported_recovery`: [OPTIONAL] List[str]; A list of supported recoveries. For the moment, can be twrp and/or orangefox (twrp by default) - `twrp-link`: [OPTIONAL] str; name of the corresponding twrp page. In addition to these metadata, every config can have optional `requirements`. If these are set, the user is asked to check if they are meet. diff --git a/openandroidinstaller/app_state.py b/openandroidinstaller/app_state.py index eee468a9..55ff526d 100644 --- a/openandroidinstaller/app_state.py +++ b/openandroidinstaller/app_state.py @@ -44,6 +44,7 @@ def __init__( self.config = None self.image_path = None self.recovery_path = None + self.chosen_recovery = None # store views self.default_views: List = [] diff --git a/openandroidinstaller/assets/imgs/ofox.png b/openandroidinstaller/assets/imgs/ofox.png new file mode 100644 index 00000000..2d8197a0 Binary files /dev/null and b/openandroidinstaller/assets/imgs/ofox.png differ diff --git a/openandroidinstaller/installer_config.py b/openandroidinstaller/installer_config.py index 2a857d83..3c75fd32 100644 --- a/openandroidinstaller/installer_config.py +++ b/openandroidinstaller/installer_config.py @@ -64,6 +64,7 @@ def __init__( self.is_ab = metadata.get("is_ab_device", False) self.supported_device_codes = metadata.get("supported_device_codes") self.twrp_link = metadata.get("twrp-link") + self.supported_recovery = metadata.get("supported_recovery", ["twrp"]) # if it is not given, we assume TWRP @classmethod def from_file(cls, path): @@ -150,7 +151,7 @@ def validate_config(config: str) -> bool: ), "content": str, schema.Optional("command"): Regex( - r"adb_reboot|adb_reboot_bootloader|adb_reboot_download|adb_sideload|adb_twrp_wipe_and_install|adb_twrp_copy_partitions|fastboot_boot_recovery|fastboot_flash_boot|fastboot_unlock_with_code|fastboot_get_unlock_data|fastboot_unlock|fastboot_oem_unlock|fastboot_reboot|heimdall_flash_recovery" + r"adb_reboot|adb_reboot_bootloader|adb_reboot_download|adb_sideload|adb_twrp_wipe_and_install|adb_twrp_copy_partitions|fastboot_boot_recovery|fastboot_flash_recovery|fastboot_reboot_recovery|fastboot_flash_boot|fastboot_unlock_with_code|fastboot_get_unlock_data|fastboot_unlock|fastboot_oem_unlock|fastboot_reboot|heimdall_flash_recovery" ), schema.Optional("allow_skip"): bool, schema.Optional("img"): str, @@ -166,6 +167,7 @@ def validate_config(config: str) -> bool: "device_code": str, "supported_device_codes": [str], schema.Optional("twrp-link"): str, + schema.Optional("supported_recovery"): [str], }, schema.Optional("requirements"): { schema.Optional("android"): schema.Or(str, int), diff --git a/openandroidinstaller/tooling.py b/openandroidinstaller/tooling.py index d3a551be..6d663b8f 100644 --- a/openandroidinstaller/tooling.py +++ b/openandroidinstaller/tooling.py @@ -140,6 +140,15 @@ def activate_sideload(bin_path: Path) -> TerminalResponse: yield line +@add_logging("Activate sideloading in OranfeFox.", return_if_fail=True) +def activate_sideload_ofox(bin_path: Path) -> TerminalResponse: + """Activate sideload with adb shell in OrangeFox.""" + for line in run_command( + "adb shell twrp sideload help", bin_path + ): # Why help ? Don't know, but it works + yield line + + @add_logging("Wait for device") def adb_wait_for_device(bin_path: Path) -> TerminalResponse: """Use adb to wait for the device to become available.""" @@ -216,14 +225,15 @@ def adb_twrp_wipe_and_install( target: str, config_path: Path, is_ab: bool, + chosen_recovery: str, install_addons=True, recovery: Optional[str] = None, ) -> TerminalResponse: - """Wipe and format data with twrp, then flash os image with adb. + """Wipe and format data with recovery, then flash os image with adb. - Only works for twrp recovery. + Only works for twrp and OrangeFox recovery. """ - logger.info("Wipe and format data with twrp, then install os image.") + logger.info(f"Wipe and format data with {chosen_recovery}, then install os image.") for line in adb_wait_for_recovery(bin_path): yield line @@ -233,38 +243,46 @@ def adb_twrp_wipe_and_install( sleep(1) # wipe some partitions - for partition in ["cache", "system"]: + for partition in ["cache", "dalvik", "system"]: for line in adb_twrp_wipe_partition(bin_path=bin_path, partition=partition): yield line sleep(1) # activate sideload - logger.info("Wiping is done, now activate sideload.") - for line in activate_sideload(bin_path=bin_path): - yield line + logger.info(f"Wiping is done, now activate sideload with {chosen_recovery}.") + if chosen_recovery == "orangefox": + for line in activate_sideload_ofox(bin_path=bin_path): + yield line + else: + for line in activate_sideload(bin_path=bin_path): + yield line + sleep(5) # now flash os image logger.info("Sideload and install os image.") for line in adb_sideload(bin_path=bin_path, target=target): yield line - # wipe some cache partitions sleep(7) - for partition in ["dalvik", "cache"]: - for line in run_command(f"adb shell twrp wipe {partition}", bin_path): - yield line - sleep(3) - if (type(line) == bool) and not line: - logger.error(f"Wiping {partition} failed.") - # TODO: if this fails, a fix can be to just sideload something and then adb reboot - for line in adb_sideload( - target=f"{config_path.parent.joinpath(Path('helper.txt'))}", - bin_path=bin_path, - ): + # wipe some cache partitions + if (chosen_recovery != "orangefox"): + # OrangeFox go in buggy sideload mode when wiping dalvik here (and already been wiped before) + logger.info("Wiping cache and dalvik...") + for partition in ["dalvik", "cache"]: + for line in run_command(f"adb shell twrp wipe {partition}", bin_path): yield line - sleep(1) + sleep(2) if (type(line) == bool) and not line: - yield False - break - sleep(2) + logger.error(f"Wiping {partition} failed.") + # TODO: if this fails, a fix can be to just sideload something and then adb reboot + for line in adb_sideload( + target=f"{config_path.parent.joinpath(Path('helper.txt'))}", + bin_path=bin_path, + ): + yield line + sleep(1) + if (type(line) == bool) and not line: + yield False + break + sleep(2) # finally reboot into os or to fastboot for flashing addons for line in adb_wait_for_recovery(bin_path): yield line @@ -288,20 +306,25 @@ def adb_twrp_wipe_and_install( def adb_twrp_install_addon( - bin_path: Path, addon_path: str, is_ab: bool + bin_path: Path, addon_path: str, chosen_recovery: str, is_ab: bool ) -> TerminalResponse: """Flash addon through adb and twrp. Only works for twrp recovery. """ - logger.info(f"Install addon {addon_path} with twrp.") + logger.info(f"Install addon {addon_path} with {chosen_recovery}.") sleep(0.5) if is_ab: adb_wait_for_recovery(bin_path=bin_path) # activate sideload logger.info("Activate sideload.") - for line in activate_sideload(bin_path=bin_path): - yield line + if chosen_recovery == "orangefox": + for line in activate_sideload_ofox(bin_path=bin_path): + yield line + else: + for line in activate_sideload(bin_path=bin_path): + yield line + sleep(5) logger.info("Sideload and install addon.") # now flash the addon for line in adb_sideload(bin_path=bin_path, target=addon_path): @@ -440,6 +463,34 @@ def fastboot_flash_boot(bin_path: Path, recovery: str) -> TerminalResponse: yield True +@add_logging("Flash custom recovery with fastboot.") +def fastboot_flash_recovery( + bin_path: Path, recovery: str, is_ab: bool = True +) -> TerminalResponse: + """Flash custom recovery with fastboot.""" + for line in run_command( + "fastboot flash recovery ", target=f"{recovery}", bin_path=bin_path + ): + yield line + if not is_ab: + if (type(line) == bool) and not line: + logger.error("Flashing recovery failed.") + yield False + else: + yield True + + +@add_logging("Rebooting device to recovery.") +def fastboot_reboot_recovery(bin_path: Path) -> TerminalResponse: + """ + Reboot to recovery with fastboot + + WARNING : On some devices, users should perform a key combo + """ + for line in run_command("fastboot reboot recovery", bin_path): + yield line + + def heimdall_wait_for_download_available(bin_path: Path) -> bool: """Use heimdall detect to wait for download mode to become available on the device.""" logger.info("Wait for download mode to become available.") diff --git a/openandroidinstaller/utils.py b/openandroidinstaller/utils.py index 0c17baf5..2968fea8 100644 --- a/openandroidinstaller/utils.py +++ b/openandroidinstaller/utils.py @@ -60,15 +60,44 @@ def image_works_with_device(supported_device_codes: List[str], image_path: str) return False -def recovery_works_with_device(device_code: str, recovery_path: str) -> bool: +def recovery_works_with_device( + supported_device_codes: [str], supported_recovery: [str], recovery_path: str +) -> bool: """Determine if a recovery works for the given device. BEWARE: THE RECOVERY PART IS STILL VERY BASIC! """ recovery_file_name = recovery_path.split("/")[-1] - if (device_code in recovery_file_name) and ("twrp" in recovery_file_name): - logger.success("Device supported by the selected recovery.") - return True - else: - logger.error(f"Recovery file {recovery_file_name} is not supported.") + if recovery_file_name[-4:] != ".img": + logger.error(f"The file {recovery_file_name} is not a recovery file") return False + for codename in supported_device_codes: + if ( + (codename in recovery_file_name) + and ("twrp" in recovery_file_name.lower()) + and ("twrp" in supported_recovery) + ): + logger.success("Selected recovery supported for this device.") + return True + elif recovery_file_name == "recovery.img" and ( + "orangefox" in supported_recovery + ): + logger.error("Cannot check recovery. Supposing it is OrangeFox.") + return True + logger.error(f"Recovery file {recovery_file_name} is not supported.") + return False + + +def which_recovery_from_path(recovery_path: str) -> str: + """Determine which recovery was selected + + This does not replace recovery_works_with_device. + BEWARE: THE RECOVERY PART IS STILL VERY BASIC! + """ + recovery_file_name = recovery_path.split("/")[-1] + if "twrp" in recovery_file_name or "TWRP" in recovery_file_name: + return "twrp" + if recovery_file_name == "recovery.img": + return "orangefox" + logger.error("Unable to determine which recovery was selected !") + return None diff --git a/openandroidinstaller/views/install_addons_view.py b/openandroidinstaller/views/install_addons_view.py index ab8068f9..480ae104 100644 --- a/openandroidinstaller/views/install_addons_view.py +++ b/openandroidinstaller/views/install_addons_view.py @@ -172,6 +172,7 @@ def run_install_addons(self, e): for line in adb_twrp_install_addon( addon_path=addon_path, bin_path=self.state.bin_path, + chosen_recovery=self.state.chosen_recovery, is_ab=self.state.config.is_ab, ): # write the line to advanced output terminal diff --git a/openandroidinstaller/views/install_view.py b/openandroidinstaller/views/install_view.py index de6c3858..04210e74 100644 --- a/openandroidinstaller/views/install_view.py +++ b/openandroidinstaller/views/install_view.py @@ -171,7 +171,7 @@ def check_addons_switch(e): def run_install(self, e): """ - Run the installation process through twrp. + Run the installation process through recovery. Some parts of the command are changed by placeholders. """ @@ -193,6 +193,7 @@ def run_install(self, e): bin_path=self.state.bin_path, install_addons=self.state.install_addons, is_ab=self.state.config.is_ab, + chosen_recovery=self.state.chosen_recovery, recovery=self.state.recovery_path, ): # write the line to advanced output terminal diff --git a/openandroidinstaller/views/select_view.py b/openandroidinstaller/views/select_view.py index 442fa98b..3f126f5e 100644 --- a/openandroidinstaller/views/select_view.py +++ b/openandroidinstaller/views/select_view.py @@ -40,7 +40,12 @@ from views import BaseView from app_state import AppState from widgets import get_title, confirm_button -from utils import get_download_link, image_works_with_device, recovery_works_with_device +from utils import ( + get_download_link, + image_works_with_device, + recovery_works_with_device, + which_recovery_from_path, +) class SelectFilesView(BaseView): @@ -83,7 +88,8 @@ def init_visuals( This custom software can include smaller modifications like rooting your device or even replacing the firmware of the device with a completely custom ROM. -OpenAndroidInstaller works with the [TWRP recovery project](https://twrp.me/about).""", +OpenAndroidInstaller works with the [TWRP recovery project](https://twrp.me/about) +or [OrangeFox recovery](https://wiki.orangefox.tech/en/home), depending of your device.""", ), actions=[ TextButton("Close", on_click=self.close_close_explain_images_dlg), @@ -152,7 +158,7 @@ def build(self): Column( [ Text( - "You can bring your own image and recovery or you download the officially supported image file for your device here:" + "You can bring your own image and recovery or you download the officially supported LineageOS image file for your device here:" ), Row( [ @@ -179,6 +185,17 @@ def build(self): ) ) # attach the controls for uploading image and recovery + recovery, recoveryFile = "", "" + if "twrp" in self.state.config.supported_recovery: + recovery = "TWRP" + recoveryFile = f"`twrp-3.7.0_12-0-{self.state.config.device_code}.img`" + if "orangefox" in self.state.config.supported_recovery: + if recovery != "": + recovery += " or " + recoveryFile += " or " + recovery += "OrangeFox" + recoveryFile += "`recovery.img`" + self.right_view.controls.extend( [ Text("Select an OS image:", style="titleSmall"), @@ -202,18 +219,18 @@ def build(self): ), self.selected_image, Divider(), - Text("Select a TWRP recovery image:", style="titleSmall"), + Text(f"Select a {recovery} recovery image:", style="titleSmall"), Markdown( f""" -The recovery image should look something like `twrp-3.7.0_12-0-{self.state.config.device_code}.img`. +The recovery image should look something like {recoveryFile}. -**Note:** This tool **only supports TWRP recoveries**.""", +**Note:** This tool **only supports {recovery} recovery** for this phone.""", extension_set="gitHubFlavored", ), Row( [ FilledButton( - "Pick TWRP recovery file", + f"Pick {recovery} recovery file", icon=icons.UPLOAD_FILE, on_click=lambda _: self.pick_recovery_dialog.pick_files( allow_multiple=False, @@ -281,14 +298,17 @@ def pick_recovery_result(self, e: FilePickerResultEvent): logger.info("No image selected.") # check if the recovery works with the device and show the filename in different colors accordingly if e.files: - device_code = self.state.config.device_code if recovery_works_with_device( - device_code=device_code, recovery_path=self.state.recovery_path + supported_device_codes=self.state.config.supported_device_codes, + supported_recovery=self.state.config.supported_recovery, + recovery_path=self.state.recovery_path, ): self.selected_recovery.color = colors.GREEN + self.state.chosen_recovery = which_recovery_from_path(self.state.recovery_path) + logger.info(f"Chosen recovery : {self.state.chosen_recovery}") else: self.selected_recovery.color = colors.RED - # update + # update self.selected_recovery.update() def enable_button_if_ready(self, e): @@ -296,14 +316,15 @@ def enable_button_if_ready(self, e): if (".zip" in self.selected_image.value) and ( ".img" in self.selected_recovery.value ): - device_code = self.state.config.device_code if not ( image_works_with_device( supported_device_codes=self.state.config.supported_device_codes, image_path=self.state.image_path, ) and recovery_works_with_device( - device_code=device_code, recovery_path=self.state.recovery_path + supported_device_codes=self.state.config.supported_device_codes, + supported_recovery=self.state.config.supported_recovery, + recovery_path=self.state.recovery_path, ) ): # if image and recovery work for device allow to move on, otherwise display message @@ -312,7 +333,7 @@ def enable_button_if_ready(self, e): ) self.info_field.controls = [ Text( - "Image and/or recovery don't work with the device. Make sure you use a TWRP-based recovery.", + "Image and/or recovery don't work with the device. Make sure you use a supported recovery.", color=colors.RED, weight="bold", ) diff --git a/openandroidinstaller/views/step_view.py b/openandroidinstaller/views/step_view.py index 1e4dffbf..e64b4198 100644 --- a/openandroidinstaller/views/step_view.py +++ b/openandroidinstaller/views/step_view.py @@ -45,6 +45,8 @@ adb_twrp_copy_partitions, fastboot_boot_recovery, fastboot_flash_boot, + fastboot_flash_recovery, + fastboot_reboot_recovery, fastboot_oem_unlock, fastboot_reboot, fastboot_unlock, @@ -231,6 +233,12 @@ def call_to_phone(self, e, command: str): fastboot_flash_boot, recovery=self.state.recovery_path, ), + "fastboot_flash_recovery": partial( + fastboot_flash_recovery, + recovery=self.state.recovery_path, + is_ab=self.state.config.is_ab, + ), + "fastboot_reboot_recovery": fastboot_reboot_recovery, "fastboot_reboot": fastboot_reboot, "heimdall_flash_recovery": partial( heimdall_flash_recovery, recovery=self.state.recovery_path diff --git a/pyproject.toml b/pyproject.toml index eced37a1..4d1ee652 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "openandroidinstaller" -version = "0.4.4-beta" +version = "0.5.0-beta" description = "Install lineage OS in a nice and easy way." authors = ["Tobias Sterbak "] license = "GPLv3"