Skip to content

Commit d9bf02f

Browse files
committed
Enable Both slot Flash options
Enabling flashing AnyKernel Zips for both slots (useful during OTAs to maintain root)
1 parent 5b9ec5c commit d9bf02f

File tree

6 files changed

+140
-19
lines changed

6 files changed

+140
-19
lines changed

.github/workflows/build.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ jobs:
5252
name: KernelFlasher
5353
path: KernelFlasher.apk
5454

55-
- name: Rename apk
56-
run: |
57-
ls -al
58-
DATE=$(date +'%y.%m.%d')
59-
echo "TAG=$DATE" >> $GITHUB_ENV
55+
# - name: Rename apk
56+
# run: |
57+
# ls -al
58+
# DATE=$(date +'%y.%m.%d')
59+
# echo "TAG=$DATE" >> $GITHUB_ENV
6060

6161
# - name: Upload release
6262
# uses: ncipollo/[email protected]
@@ -67,4 +67,4 @@ jobs:
6767
# tag: "v1.${{ github.run_number }}.0"
6868
# body: |
6969
# Note: QMod KernelFlasher, support ksu-lkm
70-
# artifacts: "*.apk"
70+
# artifacts: "*.apk"

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,8 @@
99
.externalNativeBuild
1010
.cxx
1111
local.properties
12+
.secrets
13+
.env
14+
.github/workflows/build_local.yml
15+
KernelFlasher.apk
16+
/app/build/*

app/src/main/assets/flash_ak3.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ TMP=$F/tmp;
1313

1414
$F/busybox rm -rf $TMP 2>/dev/null;
1515
$F/busybox mkdir -p $TMP;
16-
$F/busybox sed -i "/export ZIPFILE=\"\$3\";/a export STATE=\"\$4\";\nexport SLOT=\"\$5\";" $F/update-binary;
16+
## $F/busybox sed -i "/export ZIPFILE=\"\$3\";/a export STATE=\"\$4\";\nexport SLOT=\"\$5\";" $F/update-binary;
1717
## $F/busybox sed -i 's/\[ -e \/dev\/block\/$byname\/system \] || slot=\$(find_slot);/[ -e \/dev\/block\/$byname\/system ] || slot=$SLOT;/' $F/update-binary;
18-
$F/busybox sed -i '/setup_env;/i sed -i "/is_slot_device=auto/i slot_select=$4" anykernel.sh' $F/update-binary;
19-
$F/busybox sed -i '/setup_env;/i sed -i '\''s/is_slot_device=auto/is_slot_device=1/'\'' anykernel.sh' $F/update-binary;
18+
## $F/busybox sed -i '/setup_env;/i sed -i "/is_slot_device=auto/i slot_select=$4" anykernel.sh' $F/update-binary;
19+
## $F/busybox sed -i '/setup_env;/i sed -i '\''s/is_slot_device=auto/is_slot_device=1/'\'' anykernel.sh' $F/update-binary;
2020

2121
# update-binary <RECOVERY_API_VERSION> <OUTFD> <ZIPFILE>
2222
AKHOME=$TMP/anykernel $F/busybox ash $F/update-binary 3 1 "$Z" "$S" "$P";
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.github.capntrips.kernelflasher.ui.components
2+
3+
import androidx.compose.foundation.layout.PaddingValues
4+
import androidx.compose.foundation.layout.padding
5+
import androidx.compose.foundation.shape.RoundedCornerShape
6+
import androidx.compose.material3.ButtonDefaults
7+
import androidx.compose.material3.Text
8+
import androidx.compose.material3.TextButton
9+
import androidx.compose.material3.MaterialTheme
10+
import androidx.compose.runtime.Composable
11+
import androidx.compose.ui.Modifier
12+
import androidx.compose.ui.res.stringResource
13+
import androidx.compose.ui.unit.LayoutDirection
14+
import androidx.compose.ui.unit.dp
15+
import com.github.capntrips.kernelflasher.R
16+
17+
@Composable
18+
fun DialogButton(
19+
buttonText: String,
20+
onClick: () -> Unit
21+
) {
22+
TextButton(
23+
modifier = Modifier.padding(0.dp),
24+
shape = RoundedCornerShape(4.0.dp),
25+
contentPadding = PaddingValues(
26+
horizontal = ButtonDefaults.ContentPadding.calculateLeftPadding(LayoutDirection.Ltr) - (6.667).dp,
27+
vertical = ButtonDefaults.ContentPadding.calculateTopPadding()
28+
),
29+
onClick = onClick
30+
) {
31+
Text(buttonText,
32+
maxLines = 1,
33+
color = MaterialTheme.colorScheme.primary
34+
)
35+
}
36+
}

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,18 @@ import androidx.compose.foundation.layout.fillMaxWidth
1010
import androidx.compose.foundation.layout.height
1111
import androidx.compose.foundation.layout.offset
1212
import androidx.compose.foundation.shape.RoundedCornerShape
13+
import androidx.compose.foundation.layout.Arrangement
14+
import androidx.compose.foundation.layout.padding
1315
import androidx.compose.material.ExperimentalMaterialApi
1416
import androidx.compose.material3.ButtonDefaults
1517
import androidx.compose.material3.Checkbox
1618
import androidx.compose.material3.ExperimentalMaterial3Api
1719
import androidx.compose.material3.MaterialTheme
1820
import androidx.compose.material3.OutlinedButton
1921
import androidx.compose.material3.Text
22+
import androidx.compose.material3.AlertDialog
2023
import androidx.compose.runtime.Composable
24+
import androidx.compose.runtime.getValue
2125
import androidx.compose.ui.Alignment
2226
import androidx.compose.ui.Modifier
2327
import androidx.compose.ui.draw.alpha
@@ -33,6 +37,7 @@ import com.github.capntrips.kernelflasher.ui.components.DataCard
3337
import com.github.capntrips.kernelflasher.ui.components.FlashButton
3438
import com.github.capntrips.kernelflasher.ui.components.FlashList
3539
import com.github.capntrips.kernelflasher.ui.components.SlotCard
40+
import com.github.capntrips.kernelflasher.ui.components.DialogButton
3641
import kotlinx.serialization.ExperimentalSerializationApi
3742

3843
@ExperimentalAnimationApi
@@ -47,6 +52,7 @@ fun ColumnScope.SlotFlashContent(
4752
navController: NavController
4853
) {
4954
val context = LocalContext.current
55+
5056
if (!listOf("/flash/ak3", "/flash/image/flash", "/backup/backup").any { navController.currentDestination!!.route!!.endsWith(it) }) {
5157
SlotCard(
5258
title = stringResource(if (slotSuffix == "_a") R.string.slot_a else if (slotSuffix == "_b") R.string.slot_b else R.string.slot),
@@ -170,6 +176,38 @@ fun ColumnScope.SlotFlashContent(
170176
}
171177
}
172178
}
179+
if (navController.currentDestination!!.route!!.contains("ak3") && viewModel.wasFlashSuccess == true && viewModel.showCautionDialog == true){
180+
AlertDialog(
181+
onDismissRequest = { viewModel.hideCautionDialog() },
182+
title = { Text("CAUTION", style = MaterialTheme.typography.titleLarge) },
183+
text = {
184+
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
185+
Text("You have flashed AnyKernel Zip to inactive slot!")
186+
Text("But the active slot is not changed after flashing.")
187+
Text("Change active slot or return to System Updater to complete OTA.")
188+
Text("Do not reboot from here, unless you know what you are doing.")
189+
}
190+
},
191+
confirmButton = {
192+
DialogButton(
193+
"CHANGE SLOT",
194+
{
195+
viewModel.hideCautionDialog()
196+
viewModel.switchSlot(context)
197+
}
198+
)
199+
},
200+
dismissButton = {
201+
DialogButton(
202+
"CANCEL",
203+
{
204+
viewModel.hideCautionDialog()
205+
}
206+
)
207+
},
208+
modifier = Modifier.padding(16.dp)
209+
)
210+
}
173211
if (viewModel.wasFlashSuccess != false && navController.currentDestination!!.route!!.endsWith("/backup/backup")) {
174212
OutlinedButton(
175213
modifier = Modifier

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

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,14 @@ class SlotViewModel(
6565
private val hashAlgorithm: String = "SHA-256"
6666
private var inInit = true
6767
private var _error: String? = null
68+
private val _showCautionDialog: MutableState<Boolean> = mutableStateOf(false)
6869

6970
val sha1: String?
7071
get() = _sha1
7172
val flashOutput: List<String>
7273
get() = _flashOutput
7374
val uiPrintedOutput: List<String>
74-
get() = _flashOutput.filter { it.startsWith("ui_print") }.map{ it.substring("ui_print".length + 1) }
75+
get() = _flashOutput.filter { it.startsWith("ui_print") }.map { it.substringAfter("ui_print").trim() }.filter { it.isNotEmpty() || it == "" }
7576
val wasFlashSuccess: Boolean?
7677
get() = _wasFlashSuccess.value
7778
val backupPartitions: MutableMap<String, Boolean>
@@ -82,6 +83,8 @@ class SlotViewModel(
8283
get() = _error != null
8384
val error: String
8485
get() = _error!!
86+
val showCautionDialog: Boolean
87+
get() = _showCautionDialog.value
8588

8689
init {
8790
refresh(context)
@@ -154,6 +157,14 @@ class SlotViewModel(
154157
_isRefreshing.value = false
155158
}
156159
}
160+
161+
private fun showCautionDialog() {
162+
_showCautionDialog.value = true
163+
}
164+
165+
fun hideCautionDialog() {
166+
_showCautionDialog.value = false
167+
}
157168

158169
// TODO: use base class for common functions
159170
@Suppress("SameParameterValue")
@@ -176,7 +187,7 @@ class SlotViewModel(
176187
private fun uiPrint(message: String) {
177188
viewModelScope.launch(Dispatchers.Main) {
178189
_flashOutput.add("ui_print $message")
179-
_flashOutput.add(" ui_print")
190+
// _flashOutput.add("ui_print")
180191
}
181192
}
182193

@@ -429,10 +440,10 @@ class SlotViewModel(
429440
}
430441

431442
private fun resetSlot() {
432-
// val activeSlotSuffix = Shell.cmd("getprop ro.boot.slot_suffix").exec().out[0]
433-
// val newSlot = if (activeSlotSuffix == "_a") "_b" else "_a"
434-
// Shell.cmd("magisk resetprop -n ro.boot.slot_suffix $newSlot").exec()
435-
// wasSlotReset = !wasSlotReset
443+
val activeSlotSuffix = Shell.cmd("getprop ro.boot.slot_suffix").exec().out[0]
444+
val newSlot = if (activeSlotSuffix == "_a") "_b" else "_a"
445+
Shell.cmd("resetprop -n ro.boot.slot_suffix $newSlot").exec()
446+
wasSlotReset = !wasSlotReset
436447
}
437448

438449
@Suppress("FunctionName")
@@ -496,7 +507,7 @@ class SlotViewModel(
496507
}
497508

498509
@Suppress("FunctionName")
499-
private suspend fun _flashAk3(context: Context, slotSuffix: String) {
510+
private suspend fun _flashAk3(context: Context) {
500511
if (!isActive) {
501512
resetSlot()
502513
}
@@ -508,7 +519,7 @@ class SlotViewModel(
508519
val files = File(context.filesDir.canonicalPath)
509520
val flashScript = File(files, "flash_ak3.sh")
510521
val slot_inactive_state = if(isActive) "active" else "inactive"
511-
val result = Shell.Builder.create().setFlags(Shell.FLAG_MOUNT_MASTER or Shell.FLAG_REDIRECT_STDERR).build().newJob().add("F=$files Z=\"$zip\" S=\"$slot_inactive_state\" P=\"$slotSuffix\" /system/bin/sh $flashScript").to(flashOutput).exec()
522+
val result = Shell.Builder.create().setFlags(Shell.FLAG_MOUNT_MASTER or Shell.FLAG_REDIRECT_STDERR).build().newJob().add("F=$files Z=\"$zip\" /system/bin/sh $flashScript").to(flashOutput).exec()
512523
if (result.isSuccess) {
513524
log(context, "Kernel flashed successfully")
514525
_wasFlashSuccess.value = true
@@ -525,24 +536,55 @@ class SlotViewModel(
525536
} finally {
526537
uiPrint("")
527538
if (wasSlotReset) {
539+
// uiPrint("CAUTION: You have flashed AnyKernel Zip to inactive slot!")
540+
// uiPrint("But the active slot is not changed after flashing.")
541+
// uiPrint("Use bootctl to change active slot or Return to System Updater to complete OTA.")
542+
// uiPrint("Do not reboot from here, unless you know what you are doing.")
528543
resetSlot()
544+
viewModelScope.launch(Dispatchers.Main) {
545+
showCautionDialog() // Show dialog instead of uiPrint
546+
}
529547
}
530548
}
531549
}
550+
551+
fun switchSlot(context: Context) {
552+
viewModelScope.launch(Dispatchers.IO) {
553+
try {
554+
// Get current slot
555+
val currentSlot = Shell.cmd("getprop ro.boot.slot_suffix").exec().out.firstOrNull() ?: "_a"
556+
val targetSlot = if (currentSlot == "_a") "b" else "a"
557+
558+
// Execute bootctl command
559+
val result = Shell.cmd("bootctl set-active-boot-slot $targetSlot").exec()
560+
561+
if (result.isSuccess) {
562+
log(context, "Slot was successfully switched to $targetSlot", shouldThrow = false)
563+
} else {
564+
log(context, "Failed to switch slot", shouldThrow = false)
565+
}
566+
} catch (e: Exception) {
567+
withContext(Dispatchers.Main) {
568+
Toast.makeText(context, "Error: ${e.message}", Toast.LENGTH_SHORT).show()
569+
}
570+
throw e
571+
}
572+
}
573+
}
532574

533575
fun flashAk3(context: Context, currentBackup: String, filename: String) {
534576
launch {
535577
_clearFlash()
536578
_copyFile(context, currentBackup, filename)
537-
_flashAk3(context,slotSuffix)
579+
_flashAk3(context)
538580
}
539581
}
540582

541583
fun flashAk3(context: Context, uri: Uri) {
542584
launch {
543585
_clearFlash()
544586
_copyFile(context, uri)
545-
_flashAk3(context, slotSuffix)
587+
_flashAk3(context)
546588
}
547589
}
548590

0 commit comments

Comments
 (0)