Skip to content

Commit 5562cc9

Browse files
committed
initial create part
1 parent fcce816 commit 5562cc9

File tree

6 files changed

+291
-88
lines changed

6 files changed

+291
-88
lines changed

app/src/main/java/org/andbootmgr/app/CreatePartFlow.kt

Lines changed: 222 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import androidx.compose.material3.Icon
2424
import androidx.compose.material3.MaterialTheme
2525
import androidx.compose.material3.RadioButton
2626
import androidx.compose.material3.RangeSlider
27+
import androidx.compose.material3.Slider
2728
import androidx.compose.material3.Text
2829
import androidx.compose.material3.TextField
2930
import androidx.compose.runtime.Composable
@@ -50,6 +51,7 @@ import kotlinx.coroutines.Dispatchers
5051
import kotlinx.coroutines.withContext
5152
import org.andbootmgr.app.CreatePartDataHolder.Part
5253
import org.andbootmgr.app.util.ConfigFile
54+
import org.andbootmgr.app.util.SDLessUtils
5355
import org.andbootmgr.app.util.SDUtils
5456
import org.andbootmgr.app.util.SOUtils
5557
import org.json.JSONObject
@@ -66,7 +68,10 @@ class CreatePartFlow(private val desiredStartSector: Long?): WizardFlow() {
6668
NavButton(vm.activity.getString(R.string.cancel)) { it.finish() },
6769
NavButton("") {}
6870
) {
69-
Start(c)
71+
if (c.vm.deviceInfo.metaonsd)
72+
Start(c)
73+
else
74+
StartSdLess(c)
7075
}, WizardPage("shop",
7176
NavButton(vm.activity.getString(R.string.prev)) { it.navigate("start") },
7277
NavButton("") {}
@@ -92,10 +97,12 @@ class CreatePartFlow(private val desiredStartSector: Long?): WizardFlow() {
9297
}
9398

9499
private class CreatePartDataHolder(val vm: WizardState, val desiredStartSector: Long?) {
95-
var meta by mutableStateOf<SDUtils.SDPartitionMeta?>(null)
96-
lateinit var p: SDUtils.Partition.FreeSpace
97-
var startSectorRelative = 0L
98-
var endSectorRelative = 0L
100+
var meta by mutableStateOf<SDUtils.SDPartitionMeta?>(null) // metaonsd only
101+
lateinit var p: SDUtils.Partition.FreeSpace // metaonsd only
102+
var freeSpace: Long? = null // !metaonsd only
103+
var startSectorRelative = 0L // metaonsd only
104+
var endSectorRelative = 0L // metaonsd only
105+
var desiredSize = 0L // !metaonsd only
99106
var partitionName: String? = null
100107

101108
var painter: @Composable (() -> Painter)? = null
@@ -108,13 +115,20 @@ private class CreatePartDataHolder(val vm: WizardState, val desiredStartSector:
108115
var code by mutableStateOf(code)
109116
var id by mutableStateOf(id)
110117
var sparse by mutableStateOf(sparse)
111-
fun resolveSectorSize(c: CreatePartDataHolder, remaining: Long): Long {
118+
fun resolveSectorSize(c: CreatePartDataHolder, remaining: Long): Long { // metaonsd only
112119
return if (!isPercent /*bytes*/) {
113120
size / c.meta!!.logicalSectorSizeBytes
114121
} else /*percent*/ {
115122
(BigDecimal(remaining).multiply(BigDecimal(size).divide(BigDecimal(100)))).toLong()
116123
}
117124
}
125+
fun resolveBytesSize(c: CreatePartDataHolder, remaining: Long): Long { // !metaonsd only
126+
return if (!isPercent /*bytes*/) {
127+
size
128+
} else /*percent*/ {
129+
(BigDecimal(remaining).multiply(BigDecimal(size).divide(BigDecimal(100)))).toLong()
130+
}
131+
}
118132
}
119133
val parts = mutableStateListOf<Part>()
120134
val extraIdNeeded = mutableListOf<String>()
@@ -134,17 +148,15 @@ private class CreatePartDataHolder(val vm: WizardState, val desiredStartSector:
134148

135149
@Composable
136150
private fun Start(c: CreatePartDataHolder) {
137-
LaunchedEffect(Unit) {
138-
if (c.meta == null) {
151+
if (c.meta == null) {
152+
LaunchedEffect(Unit) {
139153
withContext(Dispatchers.IO) {
140-
val meta = SDUtils.generateMeta(c.vm.deviceInfo.asMetaOnSdDeviceInfo())!! // TODO !metaonsd
154+
val meta = SDUtils.generateMeta(c.vm.deviceInfo.asMetaOnSdDeviceInfo())!!
141155
c.p =
142156
meta.s.find { c.desiredStartSector == it.startSector } as SDUtils.Partition.FreeSpace
143157
c.meta = meta
144158
}
145159
}
146-
}
147-
if (c.meta == null) {
148160
LoadingCircle(stringResource(R.string.loading), modifier = Modifier.fillMaxSize())
149161
return
150162
}
@@ -256,6 +268,116 @@ private fun Start(c: CreatePartDataHolder) {
256268
}
257269
}
258270

271+
@Composable
272+
private fun StartSdLess(c: CreatePartDataHolder) {
273+
if (c.freeSpace == null) {
274+
LaunchedEffect(Unit) {
275+
withContext(Dispatchers.IO) {
276+
c.freeSpace = SDLessUtils.getFreeSpaceBytes()
277+
}
278+
}
279+
LoadingCircle(stringResource(R.string.loading), modifier = Modifier.fillMaxSize())
280+
return
281+
}
282+
283+
val verticalScrollState = rememberScrollState()
284+
var size by remember { mutableStateOf("0") }
285+
val sizeInvalid by remember { derivedStateOf { !size.matches(numberRegex) } }
286+
//var partitionName by remember { mutableStateOf("") }
287+
//val partitionNameInvalid by remember { derivedStateOf { !partitionName.matches(asciiNonEmptyRegex) } }
288+
Column(
289+
Modifier
290+
.fillMaxWidth()
291+
.verticalScroll(verticalScrollState)) {
292+
Card(modifier = Modifier
293+
.fillMaxWidth()
294+
.padding(10.dp)) {
295+
Column(modifier = Modifier
296+
.fillMaxWidth()
297+
.padding(10.dp)) {
298+
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(10.dp)) {
299+
Icon(painterResource(id = R.drawable.ic_settings), stringResource(R.string.icon_content_desc), Modifier.padding(end = 10.dp))
300+
Text(stringResource(R.string.general_settings))
301+
}
302+
Column(
303+
Modifier
304+
.fillMaxWidth()
305+
.padding(5.dp)) {
306+
TextField(modifier = Modifier.fillMaxWidth(), value = size, onValueChange = {
307+
size = it
308+
}, isError = sizeInvalid, label = {
309+
Text(stringResource(R.string.size))
310+
})
311+
Slider(modifier = Modifier.fillMaxWidth(),
312+
value = size.toLongOrNull()?.toFloat() ?: c.freeSpace!!.toFloat(), onValueChange = {
313+
size = it.toLong().toString()
314+
}, valueRange = 0F..c.freeSpace!!.toFloat())
315+
316+
Text(stringResource(R.string.approx_size, if (!sizeInvalid)
317+
SOUtils.humanReadableByteCountBin(size.toLong()) else stringResource(R.string.invalid_input)))
318+
Text(stringResource(R.string.available_space_bytes,
319+
SOUtils.humanReadableByteCountBin(c.freeSpace!!), c.freeSpace!!))
320+
}
321+
}
322+
}
323+
324+
/*if (c.vm.mvm.noobMode) TODO support portable partition for sd-less
325+
MyInfoCard(stringResource(R.string.option_select), padding = 10.dp)
326+
327+
Card(modifier = Modifier
328+
.fillMaxWidth()
329+
.padding(10.dp)) {
330+
Column(modifier = Modifier
331+
.fillMaxWidth()
332+
.padding(10.dp)) {
333+
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(10.dp)) {
334+
Icon(painterResource(id = R.drawable.ic_sd), stringResource(R.string.icon_content_desc), Modifier.padding(end = 10.dp))
335+
Text(stringResource(R.string.portable_part))
336+
}
337+
TextField(value = partitionName, onValueChange = {
338+
partitionName = it
339+
}, isError = partitionNameInvalid && partitionName.isNotEmpty(), label = {
340+
Text(stringResource(R.string.part_name))
341+
})
342+
Row(horizontalArrangement = Arrangement.End, modifier = Modifier
343+
.fillMaxWidth()
344+
.padding(5.dp)) {
345+
Button(enabled = !(sizeInvalid || partitionNameInvalid), onClick = {
346+
c.desiredSize = size.toLong()
347+
c.partitionName = partitionName
348+
c.vm.navigate("flash")
349+
}) {
350+
Text(stringResource(id = R.string.create))
351+
}
352+
}
353+
}
354+
}*/
355+
Card(modifier = Modifier
356+
.fillMaxWidth()
357+
.padding(10.dp)) {
358+
Column(modifier = Modifier
359+
.fillMaxWidth()
360+
.padding(10.dp)) {
361+
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(10.dp)) {
362+
Icon(painterResource(id = R.drawable.ic_droidbooticon), stringResource(R.string.icon_content_desc), Modifier.padding(end = 10.dp))
363+
Text(stringResource(R.string.install_os))
364+
}
365+
Row(horizontalArrangement = Arrangement.End, modifier = Modifier
366+
.fillMaxWidth()
367+
.padding(5.dp)) {
368+
Button(enabled = !sizeInvalid, onClick = {
369+
c.desiredSize = size.toLong()
370+
c.partitionName = null
371+
c.vm.navigate("shop")
372+
}) {
373+
Text(stringResource(R.string.cont))
374+
}
375+
}
376+
}
377+
}
378+
}
379+
}
380+
259381
@Composable
260382
private fun Shop(c: CreatePartDataHolder) {
261383
var loading by remember { mutableStateOf(true) }
@@ -621,55 +743,100 @@ private fun Flash(c: CreatePartDataHolder) {
621743
c.vm.logic.extractToolkit(terminal)
622744
c.vm.downloadRemainingFiles(terminal)
623745
if (c.partitionName == null) { // OS install
624-
val createdParts = mutableListOf<Pair<Part, Int>>() // order is important
746+
val createdParts = mutableListOf<Pair<Part, Pair<Int, File>>>() // order is important
625747
val fn = c.romFolderName
626748
terminal.add(vm.activity.getString(R.string.term_f_name, fn))
627749
terminal.add(vm.activity.getString(R.string.term_g_name, c.romDisplayName))
628750
val tmpFile = c.vm.chosen["_install.sh_"]!!.toFile(vm)
629751
tmpFile.setExecutable(true)
752+
val entryFolder = File(vm.logic.abmBootset, fn)
753+
if (!SuFile.open(entryFolder.toURI()).mkdir()) {
754+
terminal.add(vm.activity.getString(R.string.term_mkdir_failed))
755+
return@WizardTerminalWork
756+
}
630757
terminal.add(vm.activity.getString(R.string.term_creating_pt))
631758

632-
vm.logic.unmountBootset(vm.deviceInfo)
633-
val startSectorAbsolute = c.p.startSector + c.startSectorRelative
634-
val endSectorAbsolute = c.p.startSector + c.endSectorRelative
635-
if (endSectorAbsolute > c.p.endSector)
636-
throw IllegalArgumentException("$endSectorAbsolute can't be bigger than ${c.p.endSector}")
637-
c.parts.forEachIndexed { index, part -> // TODO !metaonsd
638-
terminal.add(vm.activity.getString(R.string.term_create_part))
639-
val start = c.p.startSector.coerceAtLeast(startSectorAbsolute)
640-
val end = c.p.endSector.coerceAtMost(endSectorAbsolute)
641-
val k = part.resolveSectorSize(c, end - start)
642-
if (start + k > end)
643-
throw IllegalStateException("$start + $k = ${start + k} shouldn't be bigger than $end")
644-
if (k < 0)
645-
throw IllegalStateException("$k shouldn't be smaller than 0")
646-
// create(start, end) values are relative to the free space area
647-
val r = vm.logic.create(c.p, start - c.p.startSector,
648-
(start + k) - c.p.startSector, part.code, "").to(terminal).exec()
649-
if (r.out.joinToString("\n").contains("kpartx")) {
650-
terminal.add(vm.activity.getString(R.string.term_reboot_asap))
759+
if (vm.deviceInfo.metaonsd) {
760+
vm.logic.unmountBootset(vm.deviceInfo)
761+
val startSectorAbsolute = c.p.startSector + c.startSectorRelative
762+
val endSectorAbsolute = c.p.startSector + c.endSectorRelative
763+
if (endSectorAbsolute > c.p.endSector)
764+
throw IllegalArgumentException("$endSectorAbsolute can't be bigger than ${c.p.endSector}")
765+
c.parts.forEachIndexed { index, part ->
766+
terminal.add(vm.activity.getString(R.string.term_create_part))
767+
val start = c.p.startSector.coerceAtLeast(startSectorAbsolute)
768+
val end = c.p.endSector.coerceAtMost(endSectorAbsolute)
769+
val k = part.resolveSectorSize(c, end - start)
770+
if (start + k > end)
771+
throw IllegalStateException("$start + $k = ${start + k} shouldn't be bigger than $end")
772+
if (k < 0)
773+
throw IllegalStateException("$k shouldn't be smaller than 0")
774+
// create(start, end) values are relative to the free space area
775+
val r = vm.logic.create(
776+
c.p, start - c.p.startSector,
777+
(start + k) - c.p.startSector, part.code, ""
778+
).to(terminal).exec()
779+
if (r.out.joinToString("\n").contains("kpartx")) {
780+
terminal.add(vm.activity.getString(R.string.term_reboot_asap))
781+
}
782+
val nid = c.meta!!.nid
783+
c.meta = SDUtils.generateMeta(c.vm.deviceInfo.asMetaOnSdDeviceInfo())!!
784+
createdParts.add(part to (nid to File(c.meta!!.dumpKernelPartition(nid).path)))
785+
// do not assert there is leftover space if we just created the last partition we want to create
786+
if (index < c.parts.size - 1) {
787+
c.p =
788+
c.meta!!.s.find { it.type == SDUtils.PartitionType.FREE && start + k < it.startSector } as SDUtils.Partition.FreeSpace
789+
}
790+
if (r.isSuccess) {
791+
terminal.add(vm.activity.getString(R.string.term_created_part))
792+
} else {
793+
terminal.add(vm.activity.getString(R.string.term_failure))
794+
return@WizardTerminalWork
795+
}
651796
}
652-
createdParts.add(Pair(part, c.meta!!.nid))
653-
c.meta = SDUtils.generateMeta(c.vm.deviceInfo.asMetaOnSdDeviceInfo())
654-
// do not assert there is leftover space if we just created the last partition we want to create
655-
if (index < c.parts.size - 1) {
656-
c.p =
657-
c.meta!!.s.find { it.type == SDUtils.PartitionType.FREE && start + k < it.startSector } as SDUtils.Partition.FreeSpace
797+
if (c.meta == null) {
798+
terminal.add(vm.activity.getString(R.string.term_cant_get_meta))
799+
return@WizardTerminalWork
658800
}
659-
if (r.isSuccess) {
801+
vm.logic.mountBootset(vm.deviceInfo)
802+
} else {
803+
var space = c.desiredSize
804+
val imgFolder = File(c.vm.logic.abmSdLessBootset, fn)
805+
var i = 0
806+
if (imgFolder.exists())
807+
throw IllegalStateException("image folder ${imgFolder.absolutePath} already exists")
808+
if (!imgFolder.mkdir())
809+
throw IllegalStateException("image folder ${imgFolder.absolutePath} could not be created")
810+
c.parts.forEachIndexed { index, part ->
811+
terminal.add(vm.activity.getString(R.string.term_create_part))
812+
val id = i++
813+
val img = File(imgFolder, id.toString())
814+
val map = File(entryFolder, "$id.map")
815+
val mappedName = "abm_${fn}_$id"
816+
val bytes = part.resolveBytesSize(c, space)
817+
space -= bytes
818+
if (space < 0)
819+
throw IllegalStateException("remaining space $space shouldn't be smaller than 0")
820+
if (!Shell.cmd("fallocate -l $bytes" + img.absolutePath).to(terminal).exec().isSuccess) {
821+
terminal.add(vm.activity.getString(R.string.term_failed_fallocate))
822+
return@WizardTerminalWork
823+
}
824+
if (!Shell.cmd("uncrypt ${img.absolutePath} " + map.absolutePath).to(terminal).exec().isSuccess) {
825+
terminal.add(vm.activity.getString(R.string.term_failed_uncrypt))
826+
return@WizardTerminalWork
827+
}
828+
if (!SDLessUtils.unmap(vm.logic, mappedName, false, terminal))
829+
throw IllegalStateException("failed to unmap $mappedName which shouldn't even exist?")
830+
if (!SDLessUtils.map(vm.logic, mappedName, map, terminal)) {
831+
terminal.add(vm.activity.getString(R.string.term_failed_map_other))
832+
return@WizardTerminalWork
833+
}
834+
val mapped = File(vm.logic.dmBase, mappedName)
835+
createdParts.add(part to (i to mapped))
660836
terminal.add(vm.activity.getString(R.string.term_created_part))
661-
} else {
662-
terminal.add(vm.activity.getString(R.string.term_failure))
663-
return@WizardTerminalWork
664837
}
665838
}
666839
terminal.add(vm.activity.getString(R.string.term_created_pt))
667-
vm.logic.mountBootset(vm.deviceInfo)
668-
val meta = SDUtils.generateMeta(vm.deviceInfo.asMetaOnSdDeviceInfo())
669-
if (meta == null) {
670-
terminal.add(vm.activity.getString(R.string.term_cant_get_meta))
671-
return@WizardTerminalWork
672-
}
673840
terminal.add(vm.activity.getString(R.string.term_building_cfg))
674841

675842
val entry = ConfigFile()
@@ -681,21 +848,17 @@ private fun Flash(c: CreatePartDataHolder) {
681848
entry["dtbo"] = "$fn/dtbo.dtbo"
682849
entry["options"] = c.cmdline
683850
entry["xtype"] = c.rtype
684-
entry["xpart"] = createdParts.map { it.second }.joinToString(":")
851+
entry["xpart"] = createdParts.map { it.second.first }.joinToString(":")
685852
if (c.dmaMeta.contains("updateJson") && c.dmaMeta["updateJson"] != null)
686853
entry["xupdate"] = c.dmaMeta["updateJson"]!!
687854
entry.exportToFile(File(vm.logic.abmEntries, "$fn.conf"))
688-
if (!SuFile.open(File(vm.logic.abmBootset, fn).toURI()).mkdir()) {
689-
terminal.add(vm.activity.getString(R.string.term_mkdir_failed))
690-
return@WizardTerminalWork
691-
}
692855

693856
terminal.add(vm.activity.getString(R.string.term_flashing_imgs))
694857
for (part in c.parts) {
695858
if (!c.vm.idNeeded.contains(part.id)) continue
696859
terminal.add(vm.activity.getString(R.string.term_flashing_s, part.id))
697860
val f = c.vm.chosen[part.id]!!
698-
val tp = File(meta.dumpKernelPartition(createdParts.find { it.first == part }!!.second).path)
861+
val tp = createdParts.first { it.first == part }.second.second
699862
if (part.sparse) {
700863
val result2 = Shell.cmd(
701864
File(
@@ -720,7 +883,12 @@ private fun Flash(c: CreatePartDataHolder) {
720883
cmd += " " + c.vm.chosen[i]!!.toFile(vm).absolutePath
721884
}
722885
for (i in c.parts) {
723-
cmd += " " + createdParts.find { it.first == i }!!.second
886+
cmd += " " + createdParts.first { it.first == i }.second.let {
887+
if (vm.deviceInfo.metaonsd)
888+
it.first // partition number
889+
else
890+
it.second.absolutePath // path to .img file
891+
}
724892
}
725893
val result = vm.logic.runShFileWithArgs(cmd).to(terminal).exec()
726894
if (!result.isSuccess) {

app/src/main/java/org/andbootmgr/app/DeviceInfo.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ abstract class SdLessDeviceInfo : DeviceInfo {
9090
return !SuFile.open(logic.abmDb, "db.conf").exists()
9191
}
9292
override fun getAbmSettings(logic: DeviceLogic): String? {
93-
return logic.dmPath.absolutePath
93+
return File(logic.dmBase, logic.dmName).absolutePath
9494
}
9595
}
9696

@@ -120,7 +120,7 @@ class JsonDeviceInfoFactory(private val ctx: Context) {
120120
val jsonText = try {
121121
try {
122122
ctx.assets.open("abm.json").readBytes().toString(Charsets.UTF_8)
123-
} catch (e: FileNotFoundException) {
123+
} catch (_: FileNotFoundException) {
124124
URL("https://raw.githubusercontent.com/Android-Boot-Manager/ABM-json/master/devices/$codename.json").readText()
125125
}
126126
} catch (e: Exception) {

0 commit comments

Comments
 (0)