Skip to content

Commit 2bb75d0

Browse files
committed
Add an install view and allow to circle back to flash a recovery
1 parent 3d4d8f2 commit 2bb75d0

10 files changed

Lines changed: 235 additions & 58 deletions

File tree

openandroidinstaller/app_state.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# If not, see <https://www.gnu.org/licenses/>."""
1414
# Author: Tobias Sterbak
1515

16+
import copy
1617
from pathlib import Path
1718

1819
from installer_config import _load_config
@@ -37,6 +38,7 @@ def __init__(
3738

3839
# placeholders
3940
self.advanced = False
41+
self.install_addons = False
4042
self.config = None
4143
self.image_path = None
4244
self.recovery_path = None
@@ -48,8 +50,6 @@ def load_config(self, device_code: str):
4850
"""Load the config from file to state by device code."""
4951
self.config = _load_config(device_code, self.config_path)
5052
if self.config:
51-
self.steps = (
52-
self.config.unlock_bootloader
53-
+ self.config.flash_recovery
54-
+ self.config.install_os
53+
self.steps = copy.deepcopy(self.config.unlock_bootloader) + copy.deepcopy(
54+
self.config.flash_recovery
5555
)

openandroidinstaller/assets/configs/a5xelte.yaml

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,4 @@ steps:
1919
content: >
2020
Unplug the USB cable from your device. Then manually reboot into recovery by pressing the *Volume Down* + *Power buttons* for 8~10 seconds
2121
until the screen turns black & release the buttons immediately when it does, then boot to recovery with the device powered off,
22-
hold *Volume Up* + *Home* + *Power button*.
23-
install_os:
24-
- type: call_button
25-
content: >
26-
In the next steps, you finally flash the selected OS image.
27-
Connect your device with your computer with the USB-Cable.
28-
This step will format your phone and wipe all the data. It will also remove encryption and delete all files stored
29-
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.
30-
command: adb_twrp_wipe_and_install
22+
hold *Volume Up* + *Home* + *Power button*.

openandroidinstaller/assets/configs/sargo.yaml

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,4 @@ steps:
3434
- type: call_button
3535
content: Flash a custom recovery (temporarily) by pressing 'Confirm and run'. Once your phone screen looks like the picture on the left, continue.
3636
command: fastboot_flash_recovery
37-
img: twrp-start.jpeg
38-
install_os:
39-
- type: call_button
40-
content: >
41-
In the next steps, you finally flash the selected OS image.
42-
Wait until the TWRP screen appears. Then run the command.
43-
This step will format your phone and wipe all the data. It will also remove encryption and delete all files stored
44-
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.
45-
command: adb_twrp_wipe_and_install
37+
img: twrp-start.jpeg

openandroidinstaller/installer_config.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,11 @@ def __init__(
6969
self,
7070
unlock_bootloader: List[Step],
7171
flash_recovery: List[Step],
72-
install_os: List[Step],
7372
metadata: dict,
7473
requirements: dict,
7574
):
7675
self.unlock_bootloader = unlock_bootloader
7776
self.flash_recovery = flash_recovery
78-
self.install_os = install_os
7977
self.metadata = metadata
8078
self.requirements = requirements
8179
self.device_code = metadata.get("devicecode")
@@ -112,13 +110,7 @@ def from_file(cls, path):
112110
Step(**raw_step, title="Flash custom recovery")
113111
for raw_step in raw_steps.get("flash_recovery", [])
114112
]
115-
install_os = [
116-
Step(**raw_step, title="Install OS")
117-
for raw_step in raw_steps.get("install_os", [])
118-
]
119-
return cls(
120-
unlock_bootloader, flash_recovery, install_os, metadata, requirements
121-
)
113+
return cls(unlock_bootloader, flash_recovery, metadata, requirements)
122114

123115

124116
def _load_config(device_code: str, config_path: Path) -> Optional[InstallerConfig]:
@@ -181,7 +173,6 @@ def validate_config(config: str) -> bool:
181173
"steps": {
182174
"unlock_bootloader": schema.Or(None, [step_schema]),
183175
"flash_recovery": [step_schema],
184-
"install_os": [step_schema],
185176
},
186177
}
187178
)

openandroidinstaller/openandroidinstaller.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
SuccessView,
4646
StartView,
4747
RequirementsView,
48+
InstallView,
4849
WelcomeView,
4950
)
5051
from tooling import run_command
@@ -93,10 +94,21 @@ def __init__(self, state: AppState):
9394
start_view,
9495
welcome_view,
9596
]
97+
98+
# create the install view
99+
self.install_view = InstallView(on_confirm=self.confirm, state=self.state)
100+
96101
# create the final success view
97102
self.final_view = SuccessView(state=self.state)
98103

104+
# final default views, ordered to allow to pop
105+
self.final_default_views = [
106+
self.final_view,
107+
self.install_view,
108+
]
109+
99110
self.state.default_views = self.default_views
111+
self.state.final_default_views = self.final_default_views
100112
self.state.final_view = self.final_view
101113

102114
def build(self):
@@ -118,9 +130,13 @@ def confirm(self, e):
118130
on_confirm=self.confirm,
119131
)
120132
)
121-
else:
122-
# display the final view
123-
self.view.controls.append(self.final_view)
133+
elif self.final_default_views:
134+
# here we expect the install view to populate the step views again if necessary
135+
self.view.controls.append(self.final_default_views.pop())
136+
137+
# else:
138+
# # display the final view
139+
# self.view.controls.append(self.final_view)
124140
logger.info("Confirmed.")
125141
self.view.update()
126142

openandroidinstaller/views/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44
from .requirements_view import RequirementsView # noqa
55
from .select_view import SelectFilesView # noqa
66
from .step_view import StepView # noqa
7+
from .install_view import InstallView # noqa
78
from .success_view import SuccessView # noqa
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
"""Contains the install view."""
2+
3+
# This file is part of OpenAndroidInstaller.
4+
# OpenAndroidInstaller is free software: you can redistribute it and/or modify it under the terms of
5+
# the GNU General Public License as published by the Free Software Foundation,
6+
# either version 3 of the License, or (at your option) any later version.
7+
8+
# OpenAndroidInstaller is distributed in the hope that it will be useful, but WITHOUT ANY
9+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or
10+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
11+
12+
# You should have received a copy of the GNU General Public License along with OpenAndroidInstaller.
13+
# If not, see <https://www.gnu.org/licenses/>."""
14+
# Author: Tobias Sterbak
15+
16+
from loguru import logger
17+
from time import sleep
18+
from typing import Callable
19+
20+
from flet import (
21+
Column,
22+
ElevatedButton,
23+
Row,
24+
Text,
25+
icons,
26+
Switch,
27+
colors,
28+
Markdown,
29+
)
30+
31+
from views import BaseView
32+
from app_state import AppState
33+
from tooling import adb_twrp_wipe_and_install
34+
from widgets import (
35+
confirm_button,
36+
get_title,
37+
)
38+
from views.step_view import TerminalBox, ProgressIndicator
39+
40+
41+
class InstallView(BaseView):
42+
def __init__(
43+
self,
44+
state: AppState,
45+
on_confirm: Callable,
46+
):
47+
super().__init__(state=state)
48+
self.on_confirm = on_confirm
49+
50+
def build(self):
51+
"""Create the content of the view."""
52+
# error text
53+
self.error_text = Text("", color=colors.RED)
54+
55+
# switch to enable advanced output - here it means show terminal input/output in tool
56+
def check_advanced_switch(e):
57+
"""Check the box to enable advanced output."""
58+
if self.advanced_switch.value:
59+
logger.info("Enable advanced output.")
60+
self.state.advanced = True
61+
self.terminal_box.toggle_visibility()
62+
else:
63+
logger.info("Disable advanced output.")
64+
self.state.advanced = False
65+
self.terminal_box.toggle_visibility()
66+
67+
self.advanced_switch = Switch(
68+
label="Advanced output",
69+
on_change=check_advanced_switch,
70+
disabled=False,
71+
)
72+
# switch for installing addons
73+
def check_addons_switch(e):
74+
"""Check the switch to enable the addons installation process."""
75+
if self.install_addons_switch.value:
76+
logger.info("Enable flashing addons.")
77+
self.state.install_addons = True
78+
self.state.steps.extend(self.state.config.flash_recovery)
79+
else:
80+
logger.info("Disable flashing addons.")
81+
self.state.install_addons = False
82+
83+
self.install_addons_switch = Switch(
84+
label="Install addons",
85+
on_change=check_addons_switch,
86+
disabled=False,
87+
)
88+
# text box for terminal output
89+
self.terminal_box = TerminalBox(expand=True)
90+
91+
# container for progress indicators
92+
self.progress_indicator = ProgressIndicator(expand=True)
93+
94+
# main controls
95+
self.right_view_header.controls = [
96+
get_title(
97+
"Install OS",
98+
step_indicator_img="steps-header-install.png",
99+
)
100+
]
101+
self.right_view.controls = [
102+
Markdown(
103+
"""In the next steps, you finally flash the selected OS image.
104+
105+
Connect your device with your computer with the USB-Cable. This step will format your phone and wipe all the data.
106+
It will also remove encryption and delete all files stored in the internal storage.
107+
Then the OS image will be installed. Confirm to install.
108+
109+
#### If you want to install any addons like Google Apps, microg or F-droid, use the toggle below **before** starting the install process!
110+
After the installation you'll be taken trough the process.
111+
112+
This might take a while. At the end your phone will boot into the new OS.
113+
"""
114+
)
115+
]
116+
# basic view
117+
logger.info("Starting installation.")
118+
self.confirm_button = confirm_button(self.on_confirm)
119+
self.confirm_button.disabled = True
120+
# button to run the installation process
121+
self.install_button = ElevatedButton(
122+
"Confirm and install",
123+
on_click=self.run_install,
124+
expand=True,
125+
icon=icons.DIRECTIONS_RUN_OUTLINED,
126+
)
127+
# build the view
128+
self.right_view.controls.extend(
129+
[
130+
Row([self.error_text]),
131+
Row([self.progress_indicator]),
132+
Column(
133+
[
134+
self.install_addons_switch,
135+
self.advanced_switch,
136+
Row([self.install_button, self.confirm_button]),
137+
]
138+
),
139+
Row([self.terminal_box]),
140+
]
141+
)
142+
143+
# if skipping is allowed add a button to the view
144+
if self.state.test:
145+
self.right_view.controls.append(
146+
Row(
147+
[
148+
Text("Do you want to skip?"),
149+
ElevatedButton(
150+
"Skip",
151+
on_click=self.on_confirm,
152+
icon=icons.NEXT_PLAN_OUTLINED,
153+
expand=True,
154+
),
155+
]
156+
)
157+
)
158+
return self.view
159+
160+
def run_install(self, e):
161+
"""
162+
Run the installation process trought twrp.
163+
164+
Some parts of the command are changed by placeholders.
165+
"""
166+
# disable the call button while the command is running
167+
self.install_button.disabled = True
168+
self.install_addons_switch.disabled = True
169+
# reset the progress indicators
170+
self.progress_indicator.clear()
171+
# reset terminal output
172+
if self.state.advanced:
173+
self.terminal_box.clear()
174+
self.right_view.update()
175+
176+
# run the install script
177+
for line in adb_twrp_wipe_and_install(
178+
target=self.state.image_path,
179+
config_path=self.state.config_path,
180+
bin_path=self.state.bin_path,
181+
):
182+
# write the line to advanced output terminal
183+
self.terminal_box.write_line(line)
184+
# in case the install command is run, we want to update the progress bar
185+
self.progress_indicator.display_progress_bar(line)
186+
self.progress_indicator.update()
187+
success = line # the last element of the iterable is a boolean encoding success/failure
188+
189+
# update the view accordingly
190+
if not success:
191+
# enable call button to retry
192+
self.install_button.disabled = False
193+
# also remove the last error text if it happened
194+
self.error_text.value = "Installation failed! Try again or make sure everything is setup correctly."
195+
else:
196+
sleep(5) # wait to make sure everything is fine
197+
logger.success("Installation process was successful. Allow to continue.")
198+
# enable the confirm button and disable the call button
199+
self.confirm_button.disabled = False
200+
self.install_button.disabled = True
201+
self.view.update()

openandroidinstaller/views/start_view.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# If not, see <https://www.gnu.org/licenses/>."""
1414
# Author: Tobias Sterbak
1515

16+
import copy
1617
from loguru import logger
1718
from typing import Callable
1819

@@ -85,17 +86,13 @@ def check_bootloader_unlocked(e):
8586
"""Enable skipping unlocking the bootloader if selected."""
8687
if self.bootloader_switch.value:
8788
logger.info("Skipping bootloader unlocking.")
88-
self.state.steps = (
89-
self.state.config.flash_recovery + self.state.config.install_os
90-
)
89+
self.state.steps = copy.deepcopy(self.state.config.flash_recovery)
9190
self.state.num_total_steps = len(self.state.steps)
9291
else:
9392
logger.info("Enabled unlocking the bootloader again.")
94-
self.state.steps = (
93+
self.state.steps = copy.deepcopy(
9594
self.state.config.unlock_bootloader
96-
+ self.state.config.flash_recovery
97-
+ self.state.config.install_os
98-
)
95+
) + copy.deepcopy(self.state.config.flash_recovery)
9996
self.state.num_total_steps = len(self.state.steps)
10097

10198
self.bootloader_switch = Switch(

0 commit comments

Comments
 (0)