Skip to content

Commit 42e8d5a

Browse files
committed
allow non-ab devices
1 parent 902e7e5 commit 42e8d5a

File tree

8 files changed

+119
-89
lines changed

8 files changed

+119
-89
lines changed

app/src/main/java/com/github/capntrips/kernelflasher/MainActivity.kt

Lines changed: 57 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ class MainActivity : ComponentActivity() {
6666

6767
private var rootServiceConnected: Boolean = false
6868
private var viewModel: MainViewModel? = null
69-
private var isAb: Boolean? = null
7069
private lateinit var mainListener: MainListener
7170
var isAwaitingResult = false
7271

@@ -141,7 +140,7 @@ class MainActivity : ComponentActivity() {
141140
content.viewTreeObserver.addOnPreDrawListener(
142141
object : ViewTreeObserver.OnPreDrawListener {
143142
override fun onPreDraw(): Boolean {
144-
return if (viewModel?.isRefreshing == false || isAb == false || Shell.isAppGrantedRoot() == false) {
143+
return if (viewModel?.isRefreshing == false || Shell.isAppGrantedRoot() == false) {
145144
content.viewTreeObserver.removeOnPreDrawListener(this)
146145
true
147146
} else {
@@ -153,17 +152,8 @@ class MainActivity : ComponentActivity() {
153152

154153
Shell.getShell()
155154
if (Shell.isAppGrantedRoot()!!) {
156-
isAb = Shell.cmd("getprop ro.build.ab_update").exec().out[0] == "true"
157-
if (isAb!!) {
158-
val intent = Intent(this, FilesystemService::class.java)
159-
RootService.bind(intent, AidlConnection())
160-
} else {
161-
setContent {
162-
KernelFlasherTheme {
163-
ErrorScreen(stringResource(R.string.non_ab_unsupported))
164-
}
165-
}
166-
}
155+
val intent = Intent(this, FilesystemService::class.java)
156+
RootService.bind(intent, AidlConnection())
167157
} else {
168158
setContent {
169159
KernelFlasherTheme {
@@ -206,60 +196,79 @@ class MainActivity : ComponentActivity() {
206196
val updatesViewModel = mainViewModel.updates
207197
val rebootViewModel = mainViewModel.reboot
208198
BackHandler(enabled = mainViewModel.isRefreshing, onBack = {})
199+
val slotContent: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit = { backStackEntry ->
200+
val slotSuffix = backStackEntry.arguments?.getString("slotSuffix") ?: ""
201+
val slotViewModel = if (slotSuffix == "_b") slotViewModelB else slotViewModelA
202+
if (slotViewModel!!.wasFlashSuccess != null && listOf("slot{slotSuffix}", "slot").any { navController.currentDestination!!.route.equals(it) }) {
203+
slotViewModel.clearFlash(this@MainActivity)
204+
}
205+
RefreshableScreen(mainViewModel, navController, swipeEnabled = true) {
206+
SlotContent(slotViewModel, slotSuffix, navController)
207+
}
208+
209+
}
209210
val slotFlashContent: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit = { backStackEntry ->
210-
val slotSuffix = backStackEntry.arguments?.getString("slotSuffix")!!
211-
val slotViewModel = if (slotSuffix == "_a") slotViewModelA else slotViewModelB
211+
val slotSuffix = backStackEntry.arguments?.getString("slotSuffix") ?: ""
212+
val slotViewModel = if (slotSuffix == "_b") slotViewModelB else slotViewModelA
212213
RefreshableScreen(mainViewModel, navController) {
213-
SlotFlashContent(slotViewModel, slotSuffix, navController)
214+
SlotFlashContent(slotViewModel!!, slotSuffix, navController)
214215
}
215216
}
216217
val slotBackupsContent: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit = { backStackEntry ->
217-
val slotSuffix = backStackEntry.arguments?.getString("slotSuffix")!!
218-
val slotViewModel = if (slotSuffix == "_a") slotViewModelA else slotViewModelB
218+
val slotSuffix = backStackEntry.arguments?.getString("slotSuffix") ?: ""
219+
val slotViewModel = if (slotSuffix == "_b") slotViewModelB else slotViewModelA
219220
if (backStackEntry.arguments?.getString("backupId") != null) {
220221
backupsViewModel.currentBackup = backStackEntry.arguments?.getString("backupId")
221222
} else {
222223
backupsViewModel.clearCurrent()
223224
}
224225
RefreshableScreen(mainViewModel, navController) {
225-
SlotBackupsContent(slotViewModel, backupsViewModel, slotSuffix, navController)
226+
SlotBackupsContent(slotViewModel!!, backupsViewModel, slotSuffix, navController)
227+
}
228+
}
229+
val slotBackupFlashContent: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit = { backStackEntry ->
230+
val slotSuffix = backStackEntry.arguments?.getString("slotSuffix") ?: ""
231+
val slotViewModel = if (slotSuffix == "_b") slotViewModelB else slotViewModelA
232+
backupsViewModel.currentBackup = backStackEntry.arguments?.getString("backupId")
233+
if (backupsViewModel.backups.containsKey(backupsViewModel.currentBackup)) {
234+
RefreshableScreen(mainViewModel, navController) {
235+
SlotFlashContent(slotViewModel!!, slotSuffix, navController)
236+
}
226237
}
238+
227239
}
228240
NavHost(navController = navController, startDestination = "main") {
229241
composable("main") {
230242
RefreshableScreen(mainViewModel, navController, swipeEnabled = true) {
231243
MainContent(mainViewModel, navController)
232244
}
233245
}
234-
composable("slot{slotSuffix}") { backStackEntry ->
235-
val slotSuffix = backStackEntry.arguments?.getString("slotSuffix")!!
236-
val slotViewModel = if (slotSuffix == "_a") slotViewModelA else slotViewModelB
237-
if (slotViewModel.wasFlashSuccess != null && navController.currentDestination!!.route.equals("slot{slotSuffix}")) {
238-
slotViewModel.clearFlash(this@MainActivity)
239-
}
240-
RefreshableScreen(mainViewModel, navController, swipeEnabled = true) {
241-
SlotContent(slotViewModel, slotSuffix, navController)
242-
}
243-
}
244-
composable("slot{slotSuffix}/flash", content = slotFlashContent)
245-
composable("slot{slotSuffix}/flash/ak3", content = slotFlashContent)
246-
composable("slot{slotSuffix}/flash/image", content = slotFlashContent)
247-
composable("slot{slotSuffix}/flash/image/flash", content = slotFlashContent)
248-
composable("slot{slotSuffix}/backup", content = slotFlashContent)
249-
composable("slot{slotSuffix}/backup/backup", content = slotFlashContent)
250-
composable("slot{slotSuffix}/backups", content = slotBackupsContent)
251-
composable("slot{slotSuffix}/backups/{backupId}", content = slotBackupsContent)
252-
composable("slot{slotSuffix}/backups/{backupId}/restore", content = slotBackupsContent)
253-
composable("slot{slotSuffix}/backups/{backupId}/restore/restore", content = slotBackupsContent)
254-
composable("slot{slotSuffix}/backups/{backupId}/flash/ak3") { backStackEntry ->
255-
val slotSuffix = backStackEntry.arguments?.getString("slotSuffix")!!
256-
val slotViewModel = if (slotSuffix == "_a") slotViewModelA else slotViewModelB
257-
backupsViewModel.currentBackup = backStackEntry.arguments?.getString("backupId")
258-
if (backupsViewModel.backups.containsKey(backupsViewModel.currentBackup)) {
259-
RefreshableScreen(mainViewModel, navController) {
260-
SlotFlashContent(slotViewModel, slotSuffix, navController)
261-
}
262-
}
246+
if (mainViewModel.isAb) {
247+
composable("slot{slotSuffix}", content = slotContent)
248+
composable("slot{slotSuffix}/flash", content = slotFlashContent)
249+
composable("slot{slotSuffix}/flash/ak3", content = slotFlashContent)
250+
composable("slot{slotSuffix}/flash/image", content = slotFlashContent)
251+
composable("slot{slotSuffix}/flash/image/flash", content = slotFlashContent)
252+
composable("slot{slotSuffix}/backup", content = slotFlashContent)
253+
composable("slot{slotSuffix}/backup/backup", content = slotFlashContent)
254+
composable("slot{slotSuffix}/backups", content = slotBackupsContent)
255+
composable("slot{slotSuffix}/backups/{backupId}", content = slotBackupsContent)
256+
composable("slot{slotSuffix}/backups/{backupId}/restore", content = slotBackupsContent)
257+
composable("slot{slotSuffix}/backups/{backupId}/restore/restore", content = slotBackupsContent)
258+
composable("slot{slotSuffix}/backups/{backupId}/flash/ak3", content = slotBackupFlashContent)
259+
} else {
260+
composable("slot", content = slotContent)
261+
composable("slot/flash", content = slotFlashContent)
262+
composable("slot/flash/ak3", content = slotFlashContent)
263+
composable("slot/flash/image", content = slotFlashContent)
264+
composable("slot/flash/image/flash", content = slotFlashContent)
265+
composable("slot/backup", content = slotFlashContent)
266+
composable("slot/backup/backup", content = slotFlashContent)
267+
composable("slot/backups", content = slotBackupsContent)
268+
composable("slot/backups/{backupId}", content = slotBackupsContent)
269+
composable("slot/backups/{backupId}/restore", content = slotBackupsContent)
270+
composable("slot/backups/{backupId}/restore/restore", content = slotBackupsContent)
271+
composable("slot/backups/{backupId}/flash/ak3", content = slotBackupFlashContent)
263272
}
264273
composable("backups") {
265274
backupsViewModel.clearCurrent()

app/src/main/java/com/github/capntrips/kernelflasher/ui/screens/backups/SlotBackupsContent.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ fun ColumnScope.SlotBackupsContent(
5151
navController: NavController
5252
) {
5353
val context = LocalContext.current
54-
if (!navController.currentDestination!!.route!!.startsWith("slot{slotSuffix}/backups/{backupId}/restore")) {
54+
if (!navController.currentDestination!!.route!!.contains("/backups/{backupId}/restore")) {
5555
SlotCard(
56-
title = stringResource(if (slotSuffix == "_a") R.string.slot_a else R.string.slot_b),
56+
title = stringResource(if (slotSuffix == "_a") R.string.slot_a else if (slotSuffix == "_b") R.string.slot_b else R.string.slot),
5757
viewModel = slotViewModel,
5858
navController = navController,
5959
isSlotScreen = true,
@@ -120,7 +120,7 @@ fun ColumnScope.SlotBackupsContent(
120120
onClick = {
121121
slotViewModel.flashAk3(context, backupsViewModel.currentBackup!!, currentBackup.filename!!)
122122
navController.navigate("slot$slotSuffix/backups/${backupsViewModel.currentBackup!!}/flash/ak3") {
123-
popUpTo("slot{slotSuffix}")
123+
popUpTo("slot$slotSuffix")
124124
}
125125
}
126126
) {
@@ -167,7 +167,7 @@ fun ColumnScope.SlotBackupsContent(
167167
)
168168
}
169169
}
170-
} else if (navController.currentDestination!!.route!! == "slot{slotSuffix}/backups/{backupId}/restore") {
170+
} else if (navController.currentDestination!!.route!!.endsWith("/backups/{backupId}/restore")) {
171171
DataCard (stringResource(R.string.restore))
172172
Spacer(Modifier.height(5.dp))
173173
val disabledColor = ButtonDefaults.buttonColors(
@@ -216,7 +216,7 @@ fun ColumnScope.SlotBackupsContent(
216216
onClick = {
217217
backupsViewModel.restore(context, slotSuffix)
218218
navController.navigate("slot$slotSuffix/backups/${backupsViewModel.currentBackup!!}/restore/restore") {
219-
popUpTo("slot{slotSuffix}")
219+
popUpTo("slot$slotSuffix")
220220
}
221221
},
222222
enabled = currentBackup.hashes == null || (PartitionUtil.PartitionNames.none { currentBackup.hashes.get(it) != null && backupsViewModel.backupPartitions[it] == null } && backupsViewModel.backupPartitions.filter { it.value }.isNotEmpty())

app/src/main/java/com/github/capntrips/kernelflasher/ui/screens/main/MainContent.kt

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ import com.github.capntrips.kernelflasher.R
2222
import com.github.capntrips.kernelflasher.ui.components.DataCard
2323
import com.github.capntrips.kernelflasher.ui.components.DataRow
2424
import com.github.capntrips.kernelflasher.ui.components.SlotCard
25+
import kotlinx.serialization.ExperimentalSerializationApi
2526

2627
@ExperimentalMaterial3Api
28+
@ExperimentalSerializationApi
2729
@Composable
2830
fun ColumnScope.MainContent(
2931
viewModel: MainViewModel,
@@ -35,20 +37,24 @@ fun ColumnScope.MainContent(
3537
DataRow(stringResource(R.string.model), "${Build.MODEL} (${Build.DEVICE})", mutableMaxWidth = cardWidth)
3638
DataRow(stringResource(R.string.build_number), Build.ID, mutableMaxWidth = cardWidth)
3739
DataRow(stringResource(R.string.kernel_version), viewModel.kernelVersion, mutableMaxWidth = cardWidth, clickable = true)
38-
DataRow(stringResource(R.string.slot_suffix), viewModel.slotSuffix, mutableMaxWidth = cardWidth)
40+
if (viewModel.isAb) {
41+
DataRow(stringResource(R.string.slot_suffix), viewModel.slotSuffix, mutableMaxWidth = cardWidth)
42+
}
3943
}
4044
Spacer(Modifier.height(16.dp))
4145
SlotCard(
42-
title = stringResource(R.string.slot_a),
46+
title = stringResource(if (viewModel.isAb) R.string.slot_a else R.string.slot),
4347
viewModel = viewModel.slotA,
4448
navController = navController
4549
)
46-
Spacer(Modifier.height(16.dp))
47-
SlotCard(
48-
title = stringResource(R.string.slot_b),
49-
viewModel = viewModel.slotB,
50-
navController = navController
51-
)
50+
if (viewModel.isAb) {
51+
Spacer(Modifier.height(16.dp))
52+
SlotCard(
53+
title = stringResource(R.string.slot_b),
54+
viewModel = viewModel.slotB!!,
55+
navController = navController
56+
)
57+
}
5258
Spacer(Modifier.height(16.dp))
5359
AnimatedVisibility(!viewModel.isRefreshing) {
5460
OutlinedButton(

app/src/main/java/com/github/capntrips/kernelflasher/ui/screens/main/MainViewModel.kt

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ class MainViewModel(
3737
val slotSuffix: String
3838

3939
val kernelVersion: String
40+
val isAb: Boolean
4041
val slotA: SlotViewModel
41-
val slotB: SlotViewModel
42+
val slotB: SlotViewModel?
4243
val backups: BackupsViewModel
4344
val updates: UpdatesViewModel
4445
val reboot: RebootViewModel
@@ -57,22 +58,34 @@ class MainViewModel(
5758

5859
init {
5960
PartitionUtil.init(context, fileSystemManager)
60-
val bootA = PartitionUtil.findPartitionBlockDevice(context, "boot", "_a")!!
61-
val bootB = PartitionUtil.findPartitionBlockDevice(context, "boot", "_b")!!
62-
val initBootA = PartitionUtil.findPartitionBlockDevice(context, "init_boot", "_a")
63-
val initBootB = PartitionUtil.findPartitionBlockDevice(context, "init_boot", "_b")
6461
kernelVersion = Shell.cmd("echo $(uname -r) $(uname -v)").exec().out[0]
6562
slotSuffix = Shell.cmd("getprop ro.boot.slot_suffix").exec().out[0]
6663
backups = BackupsViewModel(context, fileSystemManager, navController, _isRefreshing, _backups)
6764
updates = UpdatesViewModel(context, fileSystemManager, navController, _isRefreshing)
6865
reboot = RebootViewModel(context, fileSystemManager, navController, _isRefreshing)
69-
slotA = SlotViewModel(context, fileSystemManager, navController, _isRefreshing, slotSuffix == "_a", "_a", bootA, initBootA, _backups)
70-
if (slotA.hasError && slotSuffix == "_a") {
71-
_error = slotA.error
72-
}
73-
slotB = SlotViewModel(context, fileSystemManager, navController, _isRefreshing, slotSuffix == "_b", "_b", bootB, initBootB, _backups)
74-
if (slotB.hasError && slotSuffix == "_b") {
75-
_error = slotB.error
66+
// https://cs.android.com/android/platform/superproject/+/android-14.0.0_r18:bootable/recovery/recovery.cpp;l=320
67+
isAb = slotSuffix.isNotEmpty()
68+
if (isAb) {
69+
val bootA = PartitionUtil.findPartitionBlockDevice(context, "boot", "_a")!!
70+
val bootB = PartitionUtil.findPartitionBlockDevice(context, "boot", "_b")!!
71+
val initBootA = PartitionUtil.findPartitionBlockDevice(context, "init_boot", "_a")
72+
val initBootB = PartitionUtil.findPartitionBlockDevice(context, "init_boot", "_b")
73+
slotA = SlotViewModel(context, fileSystemManager, navController, _isRefreshing, slotSuffix == "_a", "_a", bootA, initBootA, _backups)
74+
if (slotA.hasError && slotSuffix == "_a") {
75+
_error = slotA.error
76+
}
77+
slotB = SlotViewModel(context, fileSystemManager, navController, _isRefreshing, slotSuffix == "_b", "_b", bootB, initBootB, _backups)
78+
if (slotB.hasError && slotSuffix == "_b") {
79+
_error = slotB.error
80+
}
81+
} else {
82+
val boot = PartitionUtil.findPartitionBlockDevice(context, "boot", "")!!
83+
val initBoot = PartitionUtil.findPartitionBlockDevice(context, "init_boot", "")
84+
slotA = SlotViewModel(context, fileSystemManager, navController, _isRefreshing, true, "", boot, initBoot, _backups)
85+
if (slotA.hasError) {
86+
_error = slotA.error
87+
}
88+
slotB = null
7689
}
7790

7891
hasRamoops = fileSystemManager.getFile("/sys/fs/pstore/console-ramoops-0").exists()
@@ -82,7 +95,9 @@ class MainViewModel(
8295
fun refresh(context: Context) {
8396
launch {
8497
slotA.refresh(context)
85-
slotB.refresh(context)
98+
if (isAb) {
99+
slotB!!.refresh(context)
100+
}
86101
backups.refresh(context)
87102
}
88103
}

app/src/main/java/com/github/capntrips/kernelflasher/ui/screens/slot/SlotContent.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ fun ColumnScope.SlotContent(
3232
) {
3333
val context = LocalContext.current
3434
SlotCard(
35-
title = stringResource(if (slotSuffix == "_a") R.string.slot_a else R.string.slot_b),
35+
title = stringResource(if (slotSuffix == "_a") R.string.slot_a else if (slotSuffix == "_b") R.string.slot_b else R.string.slot),
3636
viewModel = viewModel,
3737
navController = navController,
3838
isSlotScreen = true

0 commit comments

Comments
 (0)