diff --git a/openandroidinstaller/app_state.py b/openandroidinstaller/app_state.py index ed0b8eae..bb19afc8 100644 --- a/openandroidinstaller/app_state.py +++ b/openandroidinstaller/app_state.py @@ -13,6 +13,7 @@ # If not, see .""" # Author: Tobias Sterbak +import copy from pathlib import Path from installer_config import _load_config @@ -37,6 +38,7 @@ def __init__( # placeholders self.advanced = False + self.install_addons = False self.config = None self.image_path = None self.recovery_path = None @@ -48,8 +50,6 @@ def load_config(self, device_code: str): """Load the config from file to state by device code.""" self.config = _load_config(device_code, self.config_path) if self.config: - self.steps = ( - self.config.unlock_bootloader - + self.config.flash_recovery - + self.config.install_os + self.steps = copy.deepcopy(self.config.unlock_bootloader) + copy.deepcopy( + self.config.flash_recovery ) diff --git a/openandroidinstaller/assets/configs/a5xelte.yaml b/openandroidinstaller/assets/configs/a5xelte.yaml index 617b7657..4d4d40bb 100644 --- a/openandroidinstaller/assets/configs/a5xelte.yaml +++ b/openandroidinstaller/assets/configs/a5xelte.yaml @@ -19,12 +19,4 @@ steps: content: > Unplug the USB cable from your device. Then manually reboot into recovery by pressing the *Volume Down* + *Power buttons* for 8~10 seconds until the screen turns black & release the buttons immediately when it does, then boot to recovery with the device powered off, - hold *Volume Up* + *Home* + *Power button*. - install_os: - - type: call_button - content: > - In the next steps, you finally flash the selected OS image. - Connect your device with your computer with the USB-Cable. - This step will format your phone and wipe all the data. It will also remove encryption and delete all files stored - in the internal storage. Then the OS image will be installed. Confirm to run. This might take a while. At the end your phone will boot into the new OS. - command: adb_twrp_wipe_and_install \ No newline at end of file + hold *Volume Up* + *Home* + *Power button*. \ No newline at end of file diff --git a/openandroidinstaller/assets/configs/sargo.yaml b/openandroidinstaller/assets/configs/sargo.yaml index ed40f9ac..2e19de38 100644 --- a/openandroidinstaller/assets/configs/sargo.yaml +++ b/openandroidinstaller/assets/configs/sargo.yaml @@ -34,12 +34,4 @@ steps: - type: call_button content: Flash a custom recovery (temporarily) by pressing 'Confirm and run'. Once your phone screen looks like the picture on the left, continue. command: fastboot_flash_recovery - img: twrp-start.jpeg - install_os: - - type: call_button - content: > - In the next steps, you finally flash the selected OS image. - Wait until the TWRP screen appears. Then run the command. - This step will format your phone and wipe all the data. It will also remove encryption and delete all files stored - in the internal storage. Then the OS image will be installed. Confirm to run. This might take a while. At the end your phone will boot into the new OS. - command: adb_twrp_wipe_and_install \ No newline at end of file + img: twrp-start.jpeg \ No newline at end of file diff --git a/openandroidinstaller/installer_config.py b/openandroidinstaller/installer_config.py index 2eae535d..574864de 100644 --- a/openandroidinstaller/installer_config.py +++ b/openandroidinstaller/installer_config.py @@ -69,13 +69,11 @@ def __init__( self, unlock_bootloader: List[Step], flash_recovery: List[Step], - install_os: List[Step], metadata: dict, requirements: dict, ): self.unlock_bootloader = unlock_bootloader self.flash_recovery = flash_recovery - self.install_os = install_os self.metadata = metadata self.requirements = requirements self.device_code = metadata.get("devicecode") @@ -112,13 +110,7 @@ def from_file(cls, path): Step(**raw_step, title="Flash custom recovery") for raw_step in raw_steps.get("flash_recovery", []) ] - install_os = [ - Step(**raw_step, title="Install OS") - for raw_step in raw_steps.get("install_os", []) - ] - return cls( - unlock_bootloader, flash_recovery, install_os, metadata, requirements - ) + return cls(unlock_bootloader, flash_recovery, metadata, requirements) def _load_config(device_code: str, config_path: Path) -> Optional[InstallerConfig]: @@ -181,7 +173,6 @@ def validate_config(config: str) -> bool: "steps": { "unlock_bootloader": schema.Or(None, [step_schema]), "flash_recovery": [step_schema], - "install_os": [step_schema], }, } ) diff --git a/openandroidinstaller/openandroidinstaller.py b/openandroidinstaller/openandroidinstaller.py index 5920bb3e..f39b59c9 100644 --- a/openandroidinstaller/openandroidinstaller.py +++ b/openandroidinstaller/openandroidinstaller.py @@ -45,6 +45,7 @@ SuccessView, StartView, RequirementsView, + InstallView, WelcomeView, ) from tooling import run_command @@ -93,10 +94,21 @@ def __init__(self, state: AppState): start_view, welcome_view, ] + + # create the install view + self.install_view = InstallView(on_confirm=self.confirm, state=self.state) + # create the final success view self.final_view = SuccessView(state=self.state) + # final default views, ordered to allow to pop + self.final_default_views = [ + self.final_view, + self.install_view, + ] + self.state.default_views = self.default_views + self.state.final_default_views = self.final_default_views self.state.final_view = self.final_view def build(self): @@ -118,9 +130,13 @@ def confirm(self, e): on_confirm=self.confirm, ) ) - else: - # display the final view - self.view.controls.append(self.final_view) + elif self.final_default_views: + # here we expect the install view to populate the step views again if necessary + self.view.controls.append(self.final_default_views.pop()) + + # else: + # # display the final view + # self.view.controls.append(self.final_view) logger.info("Confirmed.") self.view.update() diff --git a/openandroidinstaller/views/__init__.py b/openandroidinstaller/views/__init__.py index 6c564c44..c15247fe 100644 --- a/openandroidinstaller/views/__init__.py +++ b/openandroidinstaller/views/__init__.py @@ -4,4 +4,5 @@ from .requirements_view import RequirementsView # noqa from .select_view import SelectFilesView # noqa from .step_view import StepView # noqa +from .install_view import InstallView # noqa from .success_view import SuccessView # noqa diff --git a/openandroidinstaller/views/install_view.py b/openandroidinstaller/views/install_view.py new file mode 100644 index 00000000..327fa96c --- /dev/null +++ b/openandroidinstaller/views/install_view.py @@ -0,0 +1,201 @@ +"""Contains the install view.""" + +# This file is part of OpenAndroidInstaller. +# OpenAndroidInstaller is free software: you can redistribute it and/or modify it under the terms of +# the GNU General Public License as published by the Free Software Foundation, +# either version 3 of the License, or (at your option) any later version. + +# OpenAndroidInstaller is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License along with OpenAndroidInstaller. +# If not, see .""" +# Author: Tobias Sterbak + +from loguru import logger +from time import sleep +from typing import Callable + +from flet import ( + Column, + ElevatedButton, + Row, + Text, + icons, + Switch, + colors, + Markdown, +) + +from views import BaseView +from app_state import AppState +from tooling import adb_twrp_wipe_and_install +from widgets import ( + confirm_button, + get_title, +) +from views.step_view import TerminalBox, ProgressIndicator + + +class InstallView(BaseView): + def __init__( + self, + state: AppState, + on_confirm: Callable, + ): + super().__init__(state=state) + self.on_confirm = on_confirm + + def build(self): + """Create the content of the view.""" + # error text + self.error_text = Text("", color=colors.RED) + + # switch to enable advanced output - here it means show terminal input/output in tool + def check_advanced_switch(e): + """Check the box to enable advanced output.""" + if self.advanced_switch.value: + logger.info("Enable advanced output.") + self.state.advanced = True + self.terminal_box.toggle_visibility() + else: + logger.info("Disable advanced output.") + self.state.advanced = False + self.terminal_box.toggle_visibility() + + self.advanced_switch = Switch( + label="Advanced output", + on_change=check_advanced_switch, + disabled=False, + ) + # switch for installing addons + def check_addons_switch(e): + """Check the switch to enable the addons installation process.""" + if self.install_addons_switch.value: + logger.info("Enable flashing addons.") + self.state.install_addons = True + self.state.steps.extend(self.state.config.flash_recovery) + else: + logger.info("Disable flashing addons.") + self.state.install_addons = False + + self.install_addons_switch = Switch( + label="Install addons", + on_change=check_addons_switch, + disabled=False, + ) + # text box for terminal output + self.terminal_box = TerminalBox(expand=True) + + # container for progress indicators + self.progress_indicator = ProgressIndicator(expand=True) + + # main controls + self.right_view_header.controls = [ + get_title( + "Install OS", + step_indicator_img="steps-header-install.png", + ) + ] + self.right_view.controls = [ + Markdown( + """In the next steps, you finally flash the selected OS image. + +Connect your device with your computer with the USB-Cable. This step will format your phone and wipe all the data. +It will also remove encryption and delete all files stored in the internal storage. +Then the OS image will be installed. Confirm to install. + +#### If you want to install any addons like Google Apps, microg or F-droid, use the toggle below **before** starting the install process! +After the installation you'll be taken trough the process. + +This might take a while. At the end your phone will boot into the new OS. +""" + ) + ] + # basic view + logger.info("Starting installation.") + self.confirm_button = confirm_button(self.on_confirm) + self.confirm_button.disabled = True + # button to run the installation process + self.install_button = ElevatedButton( + "Confirm and install", + on_click=self.run_install, + expand=True, + icon=icons.DIRECTIONS_RUN_OUTLINED, + ) + # build the view + self.right_view.controls.extend( + [ + Row([self.error_text]), + Row([self.progress_indicator]), + Column( + [ + self.install_addons_switch, + self.advanced_switch, + Row([self.install_button, self.confirm_button]), + ] + ), + Row([self.terminal_box]), + ] + ) + + # if skipping is allowed add a button to the view + if self.state.test: + self.right_view.controls.append( + Row( + [ + Text("Do you want to skip?"), + ElevatedButton( + "Skip", + on_click=self.on_confirm, + icon=icons.NEXT_PLAN_OUTLINED, + expand=True, + ), + ] + ) + ) + return self.view + + def run_install(self, e): + """ + Run the installation process trought twrp. + + Some parts of the command are changed by placeholders. + """ + # disable the call button while the command is running + self.install_button.disabled = True + self.install_addons_switch.disabled = True + # reset the progress indicators + self.progress_indicator.clear() + # reset terminal output + if self.state.advanced: + self.terminal_box.clear() + self.right_view.update() + + # run the install script + for line in adb_twrp_wipe_and_install( + target=self.state.image_path, + config_path=self.state.config_path, + bin_path=self.state.bin_path, + ): + # write the line to advanced output terminal + self.terminal_box.write_line(line) + # in case the install command is run, we want to update the progress bar + self.progress_indicator.display_progress_bar(line) + self.progress_indicator.update() + success = line # the last element of the iterable is a boolean encoding success/failure + + # update the view accordingly + if not success: + # enable call button to retry + self.install_button.disabled = False + # also remove the last error text if it happened + self.error_text.value = "Installation failed! Try again or make sure everything is setup correctly." + else: + sleep(5) # wait to make sure everything is fine + logger.success("Installation process was successful. Allow to continue.") + # enable the confirm button and disable the call button + self.confirm_button.disabled = False + self.install_button.disabled = True + self.view.update() diff --git a/openandroidinstaller/views/start_view.py b/openandroidinstaller/views/start_view.py index a4bb0e09..cccace4d 100644 --- a/openandroidinstaller/views/start_view.py +++ b/openandroidinstaller/views/start_view.py @@ -13,6 +13,7 @@ # If not, see .""" # Author: Tobias Sterbak +import copy from loguru import logger from typing import Callable @@ -85,17 +86,13 @@ def check_bootloader_unlocked(e): """Enable skipping unlocking the bootloader if selected.""" if self.bootloader_switch.value: logger.info("Skipping bootloader unlocking.") - self.state.steps = ( - self.state.config.flash_recovery + self.state.config.install_os - ) + self.state.steps = copy.deepcopy(self.state.config.flash_recovery) self.state.num_total_steps = len(self.state.steps) else: logger.info("Enabled unlocking the bootloader again.") - self.state.steps = ( + self.state.steps = copy.deepcopy( self.state.config.unlock_bootloader - + self.state.config.flash_recovery - + self.state.config.install_os - ) + ) + copy.deepcopy(self.state.config.flash_recovery) self.state.num_total_steps = len(self.state.steps) self.bootloader_switch = Switch( diff --git a/openandroidinstaller/views/step_view.py b/openandroidinstaller/views/step_view.py index 03714cfc..ea54874f 100644 --- a/openandroidinstaller/views/step_view.py +++ b/openandroidinstaller/views/step_view.py @@ -43,7 +43,6 @@ adb_reboot_bootloader, adb_reboot_download, adb_sideload, - adb_twrp_wipe_and_install, adb_twrp_copy_partitions, fastboot_flash_recovery, fastboot_oem_unlock, @@ -109,7 +108,6 @@ def check_advanced_switch(e): steps_indictor_img_lookup = { "Unlock the bootloader": "steps-header-unlock.png", "Flash custom recovery": "steps-header-recovery.png", - "Install OS": "steps-header-install.png", } self.right_view_header.controls = [ get_title( @@ -209,11 +207,6 @@ def call_to_phone(self, e, command: str): "adb_reboot_bootloader": adb_reboot_bootloader, "adb_reboot_download": adb_reboot_download, "adb_sideload": partial(adb_sideload, target=self.state.image_path), - "adb_twrp_wipe_and_install": partial( - adb_twrp_wipe_and_install, - target=self.state.image_path, - config_path=self.state.config_path, - ), "adb_twrp_copy_partitions": partial( adb_twrp_copy_partitions, config_path=self.state.config_path ), @@ -237,12 +230,7 @@ def call_to_phone(self, e, command: str): for line in cmd_mapping.get(command)(bin_path=self.state.bin_path): # write the line to advanced output terminal self.terminal_box.write_line(line) - # in case the install command is run, we want to update the progress bar - if command == "adb_twrp_wipe_and_install": - self.progress_indicator.display_progress_bar(line) - self.progress_indicator.update() - else: - self.progress_indicator.display_progress_ring() + self.progress_indicator.display_progress_ring() else: msg = f"Unknown command type: {command}. Stopping." logger.error(msg) @@ -263,8 +251,7 @@ def call_to_phone(self, e, command: str): self.confirm_button.disabled = False self.call_button.disabled = True # reset the progress indicator (let the progressbar stay for the install command) - if command != "adb_twrp_wipe_and_install": - self.progress_indicator.clear() + self.progress_indicator.clear() self.view.update() diff --git a/pyproject.toml b/pyproject.toml index 5b6b3841..16ec7675 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "openandroidinstaller" -version = "0.3.2-alpha" +version = "0.3.3-alpha" description = "Install lineage OS in a nice and easy way." authors = ["Tobias Sterbak "] license = "GPLv3"