feat(clients): in-app OSS / third-party-license screens
Surface THIRD-PARTY-NOTICES.txt in every GUI client (the desktop packages already
ship it as a file; this adds the on-glass screen):
- Linux: Preferences -> About -> Third-party licenses (adw::AboutDialog with the app
license + Legal sections; include_str! the root notices).
- Apple: macOS About tab / iOS+tvOS Acknowledgements link; notices bundled as
PunktfunkKit SPM resources, read via Bundle.module (the Xcode app links the SPM
product, so they ride along - no .pbxproj edit).
- Android: Settings -> About -> Open-source licenses (reads the bundled asset).
- (Windows landed earlier in d1d2ca2: Settings -> About -> Third-party licenses.)
gen-third-party-notices.sh now copies the generated file into the Apple Resources/
and Android assets/ trees so the in-tree copies never drift.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,66 @@
|
||||
package io.unom.punktfunk
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
/**
|
||||
* Open-source licenses: punktfunk's own license (MIT OR Apache-2.0) plus the third-party software
|
||||
* notices, read from the bundled `THIRD-PARTY-NOTICES.txt` asset (generated by
|
||||
* scripts/gen-third-party-notices.sh). Reached from [SettingsScreen]; Back returns there.
|
||||
*/
|
||||
@Composable
|
||||
fun LicensesScreen(onBack: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
BackHandler(onBack = onBack)
|
||||
|
||||
val notices = remember {
|
||||
runCatching {
|
||||
context.assets.open("THIRD-PARTY-NOTICES.txt").bufferedReader().use { it.readText() }
|
||||
}.getOrDefault("Third-party notices unavailable.")
|
||||
}
|
||||
val version = remember {
|
||||
runCatching {
|
||||
@Suppress("DEPRECATION")
|
||||
context.packageManager.getPackageInfo(context.packageName, 0).versionName
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(horizontal = 20.dp, vertical = 24.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
Text("Open-source licenses", style = MaterialTheme.typography.headlineMedium)
|
||||
if (version != null) {
|
||||
Text(
|
||||
"punktfunk $version",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
"punktfunk is licensed under MIT OR Apache-2.0, at your option. It uses the open-source " +
|
||||
"components below, each under its own license.",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
Text(
|
||||
notices,
|
||||
style = MaterialTheme.typography.bodySmall.copy(fontFamily = FontFamily.Monospace),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import android.content.pm.PackageManager
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
@@ -44,6 +45,7 @@ import androidx.core.content.ContextCompat
|
||||
fun SettingsScreen(initial: Settings, onChange: (Settings) -> Unit, onBack: () -> Unit) {
|
||||
var s by remember { mutableStateOf(initial) }
|
||||
val context = LocalContext.current
|
||||
var showLicenses by remember { mutableStateOf(false) }
|
||||
fun update(next: Settings) {
|
||||
s = next
|
||||
onChange(next)
|
||||
@@ -56,6 +58,11 @@ fun SettingsScreen(initial: Settings, onChange: (Settings) -> Unit, onBack: () -
|
||||
ActivityResultContracts.RequestPermission(),
|
||||
) { granted -> update(s.copy(micEnabled = granted)) }
|
||||
|
||||
if (showLicenses) {
|
||||
LicensesScreen(onBack = { showLicenses = false })
|
||||
return
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@@ -143,6 +150,14 @@ fun SettingsScreen(initial: Settings, onChange: (Settings) -> Unit, onBack: () -
|
||||
onCheckedChange = { on -> update(s.copy(statsHudEnabled = on)) },
|
||||
)
|
||||
}
|
||||
|
||||
SettingsGroup("About") {
|
||||
ClickableRow(
|
||||
title = "Open-source licenses",
|
||||
subtitle = "Third-party notices and credits",
|
||||
onClick = { showLicenses = true },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,6 +202,24 @@ private fun ToggleRow(
|
||||
}
|
||||
}
|
||||
|
||||
/** A title + subtitle on the left; the whole row is clickable (opens a sub-screen). */
|
||||
@Composable
|
||||
private fun ClickableRow(title: String, subtitle: String, onClick: () -> Unit) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().clickable(onClick = onClick),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Column(Modifier.weight(1f)) {
|
||||
Text(title, style = MaterialTheme.typography.bodyLarge)
|
||||
Text(
|
||||
subtitle,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A labelled read-only dropdown over [options] (value → label); calls [onSelect] on a pick. */
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
||||
Reference in New Issue
Block a user