Skip to content

Commit

Permalink
Add an install view and allow to circle back to flash a recovery
Browse files Browse the repository at this point in the history
  • Loading branch information
tsterbak committed Jan 30, 2023
1 parent 3d4d8f2 commit 2bb75d0
Show file tree
Hide file tree
Showing 10 changed files with 235 additions and 58 deletions.
8 changes: 4 additions & 4 deletions openandroidinstaller/app_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# If not, see <https://www.gnu.org/licenses/>."""
# Author: Tobias Sterbak

import copy
from pathlib import Path

from installer_config import _load_config
Expand All @@ -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
Expand All @@ -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
)
10 changes: 1 addition & 9 deletions openandroidinstaller/assets/configs/a5xelte.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
hold *Volume Up* + *Home* + *Power button*.
10 changes: 1 addition & 9 deletions openandroidinstaller/assets/configs/sargo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
img: twrp-start.jpeg
11 changes: 1 addition & 10 deletions openandroidinstaller/installer_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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]:
Expand Down Expand Up @@ -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],
},
}
)
Expand Down
22 changes: 19 additions & 3 deletions openandroidinstaller/openandroidinstaller.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
SuccessView,
StartView,
RequirementsView,
InstallView,
WelcomeView,
)
from tooling import run_command
Expand Down Expand Up @@ -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):
Expand All @@ -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()

Expand Down
1 change: 1 addition & 0 deletions openandroidinstaller/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
201 changes: 201 additions & 0 deletions openandroidinstaller/views/install_view.py
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>."""
# 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()
11 changes: 4 additions & 7 deletions openandroidinstaller/views/start_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# If not, see <https://www.gnu.org/licenses/>."""
# Author: Tobias Sterbak

import copy
from loguru import logger
from typing import Callable

Expand Down Expand Up @@ -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(
Expand Down
Loading

0 comments on commit 2bb75d0

Please sign in to comment.