Skip to content

Commit 6d2bb94

Browse files
authored
fix: Re-enable strip libs feature (#117)
1 parent 98af399 commit 6d2bb94

10 files changed

Lines changed: 466 additions & 373 deletions

File tree

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ kotlin = "2.3.20"
66
# CLI
77
picocli = "4.7.7"
88
arsclib = "9696ffecda"
9-
morphe-patcher = "1.4.2-dev.1" # TODO: Change to stable patcher
9+
morphe-patcher = "1.4.2"
1010
morphe-library = "1.3.0"
1111

1212
# Compose Desktop

src/main/kotlin/app/morphe/gui/data/model/AppConfig.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ package app.morphe.gui.data.model
77

88
import app.morphe.engine.PatchEngine.Config.Companion.DEFAULT_KEYSTORE_ALIAS
99
import app.morphe.engine.PatchEngine.Config.Companion.DEFAULT_KEYSTORE_PASSWORD
10-
import kotlinx.serialization.Serializable
1110
import app.morphe.gui.ui.theme.ThemePreference
11+
import app.morphe.gui.util.FileUtils.ANDROID_ARCHITECTURES
12+
import kotlinx.serialization.Serializable
1213

1314
/**
1415
* Application configuration stored in config.json
@@ -36,7 +37,14 @@ data class AppConfig(
3637
val keystorePath: String? = null,
3738
val keystorePassword: String? = null,
3839
val keystoreAlias: String = DEFAULT_KEYSTORE_ALIAS,
39-
val keystoreEntryPassword: String = DEFAULT_KEYSTORE_PASSWORD
40+
val keystoreEntryPassword: String = DEFAULT_KEYSTORE_PASSWORD,
41+
// User's global keep-list for strip libs. Defaults to all common modern arches
42+
// (equivalent to no stripping). Stripping is only applied when the APK contains
43+
// an arch NOT in this set. See PatchSelectionViewModel.computeStripLibsStatus.
44+
val keepArchitectures: Set<String> = ANDROID_ARCHITECTURES,
45+
// Persisted expand/collapse state for each section in the Settings dialog.
46+
// Keyed by section title (e.g. "STRIP LIBS"). Missing key = section starts collapsed.
47+
val collapsibleSectionStates: Map<String, Boolean> = emptyMap()
4048
) {
4149
fun getThemePreference(): ThemePreference {
4250
return try {

src/main/kotlin/app/morphe/gui/data/repository/ConfigRepository.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,24 @@ class ConfigRepository {
127127
saveConfig(current.copy(useSimplifiedMode = enabled))
128128
}
129129

130+
/**
131+
* Update the user's global keep-architectures list for strip libs.
132+
*/
133+
suspend fun setKeepArchitectures(keep: Set<String>) {
134+
val current = loadConfig()
135+
saveConfig(current.copy(keepArchitectures = keep))
136+
}
137+
138+
/**
139+
* Persist the expand/collapse state of a Settings dialog section so it restores
140+
* on next open instead of resetting to collapsed.
141+
*/
142+
suspend fun setCollapsibleSectionExpanded(id: String, expanded: Boolean) {
143+
val current = loadConfig()
144+
val updated = current.collapsibleSectionStates + (id to expanded)
145+
saveConfig(current.copy(collapsibleSectionStates = updated))
146+
}
147+
130148
/**
131149
* Update keystore path only (used for auto-remember on first creation).
132150
*/

src/main/kotlin/app/morphe/gui/di/AppModule.kt

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,34 @@ val appModule = module {
6767
}
6868
factory { params ->
6969
val psm = get<PatchSourceManager>()
70-
PatchesViewModel(params.get(), params.get(), psm.getActiveRepositorySync(), get(), psm.getLocalFilePath(), psm)
70+
PatchesViewModel(
71+
params.get(),
72+
params.get(),
73+
psm.getActiveRepositorySync(),
74+
get(),
75+
psm.getLocalFilePath(),
76+
psm
77+
)
7178
}
7279
factory { params ->
7380
val psm = get<PatchSourceManager>()
74-
PatchSelectionViewModel(params.get(), params.get(), params.get(), params.get(), params.get(), get(), psm.getActiveRepositorySync(), psm.getLocalFilePath())
81+
PatchSelectionViewModel(
82+
params.get(),
83+
params.get(),
84+
params.get(),
85+
params.get(),
86+
params.get(),
87+
get(),
88+
psm.getActiveRepositorySync(),
89+
get(),
90+
psm.getLocalFilePath()
91+
)
92+
}
93+
factory { params ->
94+
PatchingViewModel(
95+
params.get(),
96+
get(),
97+
get()
98+
)
7599
}
76-
factory { params -> PatchingViewModel(params.get(), get(), get()) }
77100
}

src/main/kotlin/app/morphe/gui/ui/components/SettingsButton.kt

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ import app.morphe.gui.ui.theme.LocalThemeState
4646
fun SettingsButton(
4747
modifier: Modifier = Modifier,
4848
allowCacheClear: Boolean = true,
49-
isPatching: Boolean = false
49+
isPatching: Boolean = false,
50+
onDismiss: () -> Unit = {}
5051
) {
5152
val corners = LocalMorpheCorners.current
5253
val themeState = LocalThemeState.current
@@ -63,6 +64,8 @@ fun SettingsButton(
6364
var keystorePassword by remember { mutableStateOf<String?>(null) }
6465
var keystoreAlias by remember { mutableStateOf(DEFAULT_KEYSTORE_ALIAS) }
6566
var keystoreEntryPassword by remember { mutableStateOf(DEFAULT_KEYSTORE_PASSWORD) }
67+
var keepArchitectures by remember { mutableStateOf<Set<String>>(emptySet()) }
68+
var collapsibleSectionStates by remember { mutableStateOf<Map<String, Boolean>>(emptyMap()) }
6669

6770
LaunchedEffect(showSettingsDialog) {
6871
if (showSettingsDialog) {
@@ -74,6 +77,8 @@ fun SettingsButton(
7477
keystorePassword = config.keystorePassword
7578
keystoreAlias = config.keystoreAlias
7679
keystoreEntryPassword = config.keystoreEntryPassword
80+
keepArchitectures = config.keepArchitectures
81+
collapsibleSectionStates = config.collapsibleSectionStates
7782
}
7883
}
7984

@@ -118,7 +123,10 @@ fun SettingsButton(
118123
onExpertModeChange = { enabled ->
119124
modeState.onChange(!enabled)
120125
},
121-
onDismiss = { showSettingsDialog = false },
126+
onDismiss = {
127+
showSettingsDialog = false
128+
onDismiss()
129+
},
122130
allowCacheClear = allowCacheClear,
123131
isPatching = isPatching,
124132
patchSources = patchSources,
@@ -181,6 +189,16 @@ fun SettingsButton(
181189
entryPassword = entryPwd
182190
)
183191
}
192+
},
193+
keepArchitectures = keepArchitectures,
194+
onKeepArchitecturesChange = { updated ->
195+
keepArchitectures = updated
196+
scope.launch { configRepository.setKeepArchitectures(updated) }
197+
},
198+
collapsibleSectionStates = collapsibleSectionStates,
199+
onCollapsibleSectionToggle = { id, expanded ->
200+
collapsibleSectionStates = collapsibleSectionStates + (id to expanded)
201+
scope.launch { configRepository.setCollapsibleSectionExpanded(id, expanded) }
184202
}
185203
)
186204
}

src/main/kotlin/app/morphe/gui/ui/components/SettingsDialog.kt

Lines changed: 114 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,11 @@ fun SettingsDialog(
7878
keystoreAlias: String = DEFAULT_KEYSTORE_ALIAS,
7979
keystoreEntryPassword: String = DEFAULT_KEYSTORE_PASSWORD,
8080
onKeystorePathChange: (String?) -> Unit = {},
81-
onKeystoreCredentialsChange: (password: String?, alias: String, entryPassword: String) -> Unit = { _, _, _ -> }
81+
onKeystoreCredentialsChange: (password: String?, alias: String, entryPassword: String) -> Unit = { _, _, _ -> },
82+
keepArchitectures: Set<String> = emptySet(),
83+
onKeepArchitecturesChange: (Set<String>) -> Unit = {},
84+
collapsibleSectionStates: Map<String, Boolean> = emptyMap(),
85+
onCollapsibleSectionToggle: (id: String, expanded: Boolean) -> Unit = { _, _ -> }
8286
) {
8387
val corners = LocalMorpheCorners.current
8488
val mono = LocalMorpheFont.current
@@ -206,7 +210,22 @@ fun SettingsDialog(
206210
mono = mono,
207211
accentColor = accents.primary,
208212
borderColor = borderColor,
209-
enabled = !isPatching
213+
enabled = !isPatching,
214+
expanded = collapsibleSectionStates["SIGNING"] == true,
215+
onExpandedChange = { onCollapsibleSectionToggle("SIGNING", it) }
216+
)
217+
218+
SettingsDivider(borderColor)
219+
220+
// ── Strip Libs ──
221+
StripLibsSection(
222+
keepArchitectures = keepArchitectures,
223+
onChange = onKeepArchitecturesChange,
224+
mono = mono,
225+
accentColor = accents.primary,
226+
enabled = !isPatching,
227+
expanded = collapsibleSectionStates["STRIP LIBS"] == true,
228+
onExpandedChange = { onCollapsibleSectionToggle("STRIP LIBS", it) }
210229
)
211230

212231
SettingsDivider(borderColor)
@@ -225,7 +244,9 @@ fun SettingsDialog(
225244
mono = mono,
226245
accentColor = accents.primary,
227246
borderColor = borderColor,
228-
enabled = !isPatching
247+
enabled = !isPatching,
248+
expanded = collapsibleSectionStates["PATCH SOURCES"] == true,
249+
onExpandedChange = { onCollapsibleSectionToggle("PATCH SOURCES", it) }
229250
)
230251

231252
SettingsDivider(borderColor)
@@ -253,16 +274,6 @@ fun SettingsDialog(
253274

254275
Spacer(Modifier.height(6.dp))
255276

256-
ActionButton(
257-
label = "VIEW LICENSES",
258-
icon = Icons.Default.Description,
259-
mono = mono,
260-
borderColor = borderColor,
261-
onClick = { showLicensesDialog = true }
262-
)
263-
264-
Spacer(Modifier.height(6.dp))
265-
266277
ActionButton(
267278
label = "OPEN APP DATA",
268279
icon = Icons.Default.FolderOpen,
@@ -282,6 +293,16 @@ fun SettingsDialog(
282293

283294
Spacer(Modifier.height(6.dp))
284295

296+
ActionButton(
297+
label = "VIEW LICENSES",
298+
icon = Icons.Default.Description,
299+
mono = mono,
300+
borderColor = borderColor,
301+
onClick = { showLicensesDialog = true }
302+
)
303+
304+
Spacer(Modifier.height(6.dp))
305+
285306
// Clear cache
286307
val cacheColor = when {
287308
cacheCleared -> MorpheColors.Teal
@@ -518,11 +539,11 @@ private fun SectionLabel(
518539
private fun CollapsibleSection(
519540
title: String,
520541
mono: androidx.compose.ui.text.font.FontFamily,
521-
initiallyExpanded: Boolean = false,
542+
expanded: Boolean,
543+
onExpandedChange: (Boolean) -> Unit,
522544
content: @Composable () -> Unit
523545
) {
524546
val corners = LocalMorpheCorners.current
525-
var expanded by remember { mutableStateOf(initiallyExpanded) }
526547
val rotationAngle by androidx.compose.animation.core.animateFloatAsState(
527548
targetValue = if (expanded) -90f else 0f,
528549
animationSpec = androidx.compose.animation.core.tween(200)
@@ -539,7 +560,7 @@ private fun CollapsibleSection(
539560
if (isHovered) MaterialTheme.colorScheme.onSurface.copy(alpha = 0.04f)
540561
else Color.Transparent
541562
)
542-
.clickable { expanded = !expanded }
563+
.clickable { onExpandedChange(!expanded) }
543564
.padding(horizontal = 8.dp, vertical = 6.dp),
544565
horizontalArrangement = Arrangement.SpaceBetween,
545566
verticalAlignment = Alignment.CenterVertically
@@ -691,12 +712,19 @@ private fun PatchSourcesSection(
691712
mono: androidx.compose.ui.text.font.FontFamily,
692713
accentColor: Color,
693714
borderColor: Color,
694-
enabled: Boolean = true
715+
enabled: Boolean = true,
716+
expanded: Boolean = false,
717+
onExpandedChange: (Boolean) -> Unit = {}
695718
) {
696719
val corners = LocalMorpheCorners.current
697720
val alpha = if (enabled) 1f else 0.4f
698721
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
699-
CollapsibleSection("PATCH SOURCES", mono) {
722+
CollapsibleSection(
723+
title = "PATCH SOURCES",
724+
mono = mono,
725+
expanded = expanded,
726+
onExpandedChange = onExpandedChange
727+
) {
700728
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
701729
Text(
702730
text = if (!enabled) "Disabled while patching" else "Select where patches are loaded from",
@@ -1211,6 +1239,65 @@ private fun EditPatchSourceDialog(
12111239
)
12121240
}
12131241

1242+
// ── Strip Libs Section ──
1243+
1244+
/**
1245+
* Architectures exposed in the strip libs settings. Each entry has the
1246+
* patcher-facing value (matching CpuArchitecture.arch) and a short display name.
1247+
* Only modern arches are listed — legacy mips/armeabi are intentionally omitted.
1248+
*/
1249+
private val STRIP_LIBS_ARCHS = listOf(
1250+
"arm64-v8a" to "ARM 64-bit (most modern phones)",
1251+
"armeabi-v7a" to "ARM 32-bit (older phones)",
1252+
"x86_64" to "Intel 64-bit (emulators / Chromebooks)",
1253+
"x86" to "Intel 32-bit (legacy emulators)"
1254+
)
1255+
1256+
@Composable
1257+
private fun StripLibsSection(
1258+
keepArchitectures: Set<String>,
1259+
onChange: (Set<String>) -> Unit,
1260+
mono: androidx.compose.ui.text.font.FontFamily,
1261+
accentColor: Color,
1262+
enabled: Boolean = true,
1263+
expanded: Boolean = false,
1264+
onExpandedChange: (Boolean) -> Unit = {}
1265+
) {
1266+
CollapsibleSection(
1267+
title = "STRIP LIBS",
1268+
mono = mono,
1269+
expanded = expanded,
1270+
onExpandedChange = onExpandedChange
1271+
) {
1272+
Column(
1273+
verticalArrangement = Arrangement.spacedBy(10.dp)
1274+
) {
1275+
Text(
1276+
text = "Uncheck architectures you don't need. When patching, the output APK will keep only the architectures present in the APK AND in this list. If none overlap, nothing is stripped to avoid broken APKs.",
1277+
fontSize = 11.sp,
1278+
fontFamily = mono,
1279+
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f)
1280+
)
1281+
STRIP_LIBS_ARCHS.forEach { (arch, description) ->
1282+
val checked = arch in keepArchitectures
1283+
SettingToggleRow(
1284+
label = arch,
1285+
description = description,
1286+
checked = checked,
1287+
onCheckedChange = { keepIt ->
1288+
val updated = if (keepIt) keepArchitectures + arch
1289+
else keepArchitectures - arch
1290+
onChange(updated)
1291+
},
1292+
accentColor = accentColor,
1293+
mono = mono,
1294+
enabled = enabled
1295+
)
1296+
}
1297+
}
1298+
}
1299+
}
1300+
12141301
// ── Signing / Keystore Section ──
12151302

12161303
@Composable
@@ -1224,7 +1311,9 @@ private fun SigningSection(
12241311
mono: androidx.compose.ui.text.font.FontFamily,
12251312
accentColor: Color,
12261313
borderColor: Color,
1227-
enabled: Boolean = true
1314+
enabled: Boolean = true,
1315+
expanded: Boolean = false,
1316+
onExpandedChange: (Boolean) -> Unit = {}
12281317
) {
12291318
val corners = LocalMorpheCorners.current
12301319
val alpha = if (enabled) 1f else 0.4f
@@ -1241,7 +1330,12 @@ private fun SigningSection(
12411330
val keystoreExists = keystoreFile?.exists() == true
12421331

12431332
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
1244-
CollapsibleSection("SIGNING", mono) {
1333+
CollapsibleSection(
1334+
title = "SIGNING",
1335+
mono = mono,
1336+
expanded = expanded,
1337+
onExpandedChange = onExpandedChange
1338+
) {
12451339
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
12461340
Text(
12471341
text = if (!enabled) "Disabled while patching"

0 commit comments

Comments
 (0)