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"