Skip to content

Commit 77a1dde

Browse files
feat: Add in-app dependency license viewer (MorpheApp#114)
1 parent ec53c30 commit 77a1dde

4 files changed

Lines changed: 119 additions & 1 deletion

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"uniqueId": "app.morphe.cli",
3+
"name": "Morphe CLI",
4+
"developers": [{"name": "Morphe"}],
5+
"licenses": ["GPL-3.0-only"]
6+
}

build.gradle.kts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
2+
import com.mikepenz.aboutlibraries.plugin.DuplicateMode
3+
import com.mikepenz.aboutlibraries.plugin.DuplicateRule
24

35
plugins {
46
alias(libs.plugins.kotlin.jvm)
57
alias(libs.plugins.kotlin.compose)
68
alias(libs.plugins.kotlin.serialization)
79
alias(libs.plugins.compose)
10+
alias(libs.plugins.about.libraries)
811
// Shadow plugin is provided by buildSrc to enable the custom NoticeMergeTransformer.
912
// Applied without a version here; the version is pinned in buildSrc/build.gradle.kts.
1013
id("com.gradleup.shadow")
@@ -109,12 +112,25 @@ dependencies {
109112
// -- APK Parsing (GUI) -------------------------------------------------
110113
implementation(libs.apk.parser)
111114

115+
implementation(libs.about.libraries.core)
116+
implementation(libs.about.libraries.m3)
117+
112118
// -- Testing -----------------------------------------------------------
113119
testImplementation(libs.kotlin.test)
114120
testImplementation(libs.junit.params)
115121
testImplementation(libs.mockk)
116122
}
117123

124+
aboutLibraries {
125+
collect {
126+
configPath = file("aboutlibraries")
127+
}
128+
library {
129+
duplicationMode = DuplicateMode.MERGE
130+
duplicationRule = DuplicateRule.EXACT
131+
}
132+
}
133+
118134
// ============================================================================
119135
// Tasks
120136
// ============================================================================
@@ -136,6 +152,10 @@ tasks {
136152
}
137153

138154
processResources {
155+
// Make sure the licenses are generated before the resources are processed
156+
dependsOn("exportLibraryDefinitions")
157+
from(layout.buildDirectory.file("generated/aboutLibraries/aboutlibraries.json"))
158+
139159
// Only expand properties files, not binary files like PNG/ICO
140160
filesMatching("**/*.properties") {
141161
expand("projectVersion" to project.version)

gradle/libs.versions.toml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ morphe-patcher = "1.4.1"
1010
morphe-library = "1.3.0"
1111

1212
# Compose Desktop
13-
compose = "1.10.0"
13+
compose = "1.10.3"
1414

1515
# Networking
1616
ktor = "3.4.2"
@@ -34,6 +34,9 @@ apk-parser = "2.6.10"
3434
# Testing
3535
mockk = "1.14.9"
3636

37+
# Libraries
38+
about-libraries = "14.0.1"
39+
3740
[libraries]
3841
# Morphe Core
3942
arsclib = { module = "com.github.MorpheApp:ARSCLib", version.ref = "arsclib" }
@@ -78,8 +81,13 @@ apk-parser = { module = "net.dongliu:apk-parser", version.ref = "apk-parser" }
7881
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
7982
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
8083

84+
# About Libraries
85+
about-libraries-core = { group = "com.mikepenz", name = "aboutlibraries-compose-core", version.ref = "about-libraries" }
86+
about-libraries-m3 = { group = "com.mikepenz", name = "aboutlibraries-compose-m3", version.ref = "about-libraries" }
87+
8188
[plugins]
8289
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
8390
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
8491
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
8592
compose = { id = "org.jetbrains.compose", version.ref = "compose" }
93+
about-libraries = { id = "com.mikepenz.aboutlibraries.plugin", version.ref = "about-libraries" }

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

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ import java.security.MessageDigest
5252
import java.security.cert.X509Certificate
5353
import java.text.SimpleDateFormat
5454
import java.util.UUID
55+
import com.mikepenz.aboutlibraries.Libs
56+
import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer
5557

5658
@Composable
5759
fun SettingsDialog(
@@ -84,6 +86,7 @@ fun SettingsDialog(
8486
val borderColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.12f)
8587

8688
var showClearCacheConfirm by remember { mutableStateOf(false) }
89+
var showLicensesDialog by remember { mutableStateOf(false) }
8790
var cacheCleared by remember { mutableStateOf(false) }
8891
var cacheClearFailed by remember { mutableStateOf(false) }
8992
var showAddSourceDialog by remember { mutableStateOf(false) }
@@ -250,6 +253,16 @@ fun SettingsDialog(
250253

251254
Spacer(Modifier.height(6.dp))
252255

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+
253266
ActionButton(
254267
label = "OPEN APP DATA",
255268
icon = Icons.Default.FolderOpen,
@@ -401,6 +414,10 @@ fun SettingsDialog(
401414
)
402415
}
403416

417+
if (showLicensesDialog) {
418+
LicensesDialog(onDismiss = { showLicensesDialog = false })
419+
}
420+
404421
editingSource?.let { source ->
405422
EditPatchSourceDialog(
406423
source = source,
@@ -413,6 +430,73 @@ fun SettingsDialog(
413430
}
414431
}
415432

433+
@Composable
434+
private fun LicensesDialog(onDismiss: () -> Unit) {
435+
val corners = LocalMorpheCorners.current
436+
val mono = LocalMorpheFont.current
437+
val borderColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.12f)
438+
439+
val libraries = remember {
440+
try {
441+
val stream = Thread.currentThread().contextClassLoader.getResourceAsStream("aboutlibraries.json")
442+
val json = stream?.bufferedReader()?.use { it.readText() }
443+
if (json != null) Libs.Builder().withJson(json).build() else null
444+
} catch (e: Exception) {
445+
Logger.error("Failed to load licenses", e)
446+
null
447+
}
448+
}
449+
450+
AlertDialog(
451+
onDismissRequest = onDismiss,
452+
shape = RoundedCornerShape(corners.medium),
453+
containerColor = MaterialTheme.colorScheme.surface,
454+
modifier = Modifier.widthIn(max = 600.dp).heightIn(max = 600.dp),
455+
title = {
456+
Text(
457+
"OPEN SOURCE LICENSES",
458+
fontFamily = mono,
459+
fontWeight = FontWeight.Bold,
460+
fontSize = 13.sp,
461+
letterSpacing = 1.sp
462+
)
463+
},
464+
text = {
465+
Box(modifier = Modifier.fillMaxWidth().height(400.dp)) {
466+
if (libraries != null) {
467+
LibrariesContainer(
468+
libraries = libraries,
469+
modifier = Modifier.fillMaxSize()
470+
)
471+
} else {
472+
Text(
473+
"Failed to load licenses.",
474+
fontFamily = mono,
475+
fontSize = 12.sp,
476+
color = MaterialTheme.colorScheme.error
477+
)
478+
}
479+
}
480+
},
481+
confirmButton = {
482+
OutlinedButton(
483+
onClick = onDismiss,
484+
shape = RoundedCornerShape(corners.small),
485+
border = BorderStroke(1.dp, borderColor)
486+
) {
487+
Text(
488+
"CLOSE",
489+
fontFamily = mono,
490+
fontWeight = FontWeight.SemiBold,
491+
fontSize = 11.sp,
492+
letterSpacing = 0.5.sp,
493+
color = MaterialTheme.colorScheme.onSurfaceVariant
494+
)
495+
}
496+
}
497+
)
498+
}
499+
416500
// ── Shared building blocks ──
417501

418502
@Composable

0 commit comments

Comments
 (0)