feat: custom oss attribution screen
Signed-off-by: Infi <wingit@geist.ga>
This commit is contained in:
parent
e9f4259da5
commit
9490ff24d8
|
|
@ -0,0 +1,45 @@
|
||||||
|
package chat.revolt.components.screens.settings
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import chat.revolt.R
|
||||||
|
import chat.revolt.screens.about.Library
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AttributionItem(
|
||||||
|
library: Library,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
SelectionContainer {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.clickable(onClick = onClick)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = library.name,
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.oss_attribution_tap_to_view_license),
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,20 +1,149 @@
|
||||||
package chat.revolt.screens.about
|
package chat.revolt.screens.about
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.LocalContentColor
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
|
import androidx.compose.material3.surfaceColorAtElevation
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import chat.revolt.R
|
import chat.revolt.R
|
||||||
import chat.revolt.components.generic.PageHeader
|
import chat.revolt.components.generic.PageHeader
|
||||||
import com.mikepenz.aboutlibraries.ui.compose.LibrariesContainer
|
import chat.revolt.components.screens.settings.AttributionItem
|
||||||
import com.mikepenz.aboutlibraries.ui.compose.LibraryDefaults
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlinx.serialization.json.JsonArray
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class AboutLibraries(
|
||||||
|
val metadata: Metadata,
|
||||||
|
val libraries: List<Library>,
|
||||||
|
val licenses: Map<String, License>
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Metadata(
|
||||||
|
val generated: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class License(
|
||||||
|
val content: String? = null,
|
||||||
|
val hash: String,
|
||||||
|
val internalHash: String? = null,
|
||||||
|
val url: String,
|
||||||
|
val spdxId: String? = null,
|
||||||
|
val name: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Library(
|
||||||
|
val uniqueId: String,
|
||||||
|
val funding: JsonArray,
|
||||||
|
val developers: List<Developer>,
|
||||||
|
val artifactVersion: String,
|
||||||
|
val description: String,
|
||||||
|
val scm: Scm? = null,
|
||||||
|
val name: String,
|
||||||
|
val licenses: List<String>,
|
||||||
|
val website: String? = null,
|
||||||
|
val organization: Organization? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Organization(
|
||||||
|
val url: String,
|
||||||
|
val name: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Developer(
|
||||||
|
val organisationUrl: String? = null,
|
||||||
|
val name: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Scm(
|
||||||
|
val connection: String? = null,
|
||||||
|
val url: String,
|
||||||
|
val developerConnection: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun AttributionScreen(navController: NavController) {
|
fun AttributionScreen(navController: NavController) {
|
||||||
|
var libraries by remember { mutableStateOf<AboutLibraries?>(null) }
|
||||||
|
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
var licenceSheetOpen by remember { mutableStateOf(false) }
|
||||||
|
var licenseSheetTarget by remember { mutableStateOf("") }
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
context.resources.openRawResource(R.raw.aboutlibraries).use { stream ->
|
||||||
|
val text = stream.bufferedReader().use { it.readText() }
|
||||||
|
libraries = Json.decodeFromString(AboutLibraries.serializer(), text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (licenceSheetOpen) {
|
||||||
|
val licenceSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||||
|
|
||||||
|
ModalBottomSheet(
|
||||||
|
sheetState = licenceSheetState,
|
||||||
|
onDismissRequest = {
|
||||||
|
licenceSheetOpen = false
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = licenseSheetTarget,
|
||||||
|
style = MaterialTheme.typography.headlineMedium
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
libraries?.let {
|
||||||
|
val license = it.licenses[licenseSheetTarget]
|
||||||
|
if (license != null) {
|
||||||
|
Text(text = license.content ?: "No license content found.")
|
||||||
|
} else {
|
||||||
|
Text(text = "No license found.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.safeDrawingPadding()
|
.safeDrawingPadding()
|
||||||
|
|
@ -24,17 +153,59 @@ fun AttributionScreen(navController: NavController) {
|
||||||
showBackButton = true,
|
showBackButton = true,
|
||||||
onBackButtonClicked = { navController.popBackStack() })
|
onBackButtonClicked = { navController.popBackStack() })
|
||||||
|
|
||||||
LibrariesContainer(
|
libraries?.let {
|
||||||
modifier = Modifier
|
LazyColumn {
|
||||||
.fillMaxSize()
|
item {
|
||||||
.weight(1f),
|
Column(
|
||||||
colors = LibraryDefaults.libraryColors(
|
modifier = Modifier
|
||||||
backgroundColor = MaterialTheme.colorScheme.background,
|
.padding(16.dp)
|
||||||
contentColor = MaterialTheme.colorScheme.onBackground,
|
.clip(MaterialTheme.shapes.medium)
|
||||||
badgeBackgroundColor = MaterialTheme.colorScheme.primary,
|
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp))
|
||||||
badgeContentColor = MaterialTheme.colorScheme.onPrimary
|
.fillMaxWidth()
|
||||||
)
|
.padding(16.dp),
|
||||||
)
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.oss_attribution_body)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.oss_attribution_body_2)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.oss_attribution_warning),
|
||||||
|
color = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(
|
||||||
|
R.string.oss_attribution_generation_date,
|
||||||
|
libraries?.metadata?.generated ?: ""
|
||||||
|
),
|
||||||
|
color = LocalContentColor.current.copy(alpha = 0.6f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
items(
|
||||||
|
items = it.libraries.sortedBy { library -> library.name }
|
||||||
|
) { library ->
|
||||||
|
AttributionItem(library = library) {
|
||||||
|
licenceSheetOpen = true
|
||||||
|
licenseSheetTarget = library.licenses.first()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
item(key = "cat") {
|
||||||
|
Text(
|
||||||
|
text = "🐈",
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(16.dp),
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -72,7 +72,13 @@
|
||||||
|
|
||||||
<string name="about">About</string>
|
<string name="about">About</string>
|
||||||
<string name="app_full_name">Revolt on Android</string>
|
<string name="app_full_name">Revolt on Android</string>
|
||||||
|
|
||||||
<string name="oss_attribution">OSS Attribution</string>
|
<string name="oss_attribution">OSS Attribution</string>
|
||||||
|
<string name="oss_attribution_body">Revolt is built with the help of these awesome open-source projects.</string>
|
||||||
|
<string name="oss_attribution_body_2">We\'re grateful to the developers of these projects for making their work available to the world.</string>
|
||||||
|
<string name="oss_attribution_warning">The list below is automatically generated. It may be incomplete or inaccurate.</string>
|
||||||
|
<string name="oss_attribution_generation_date">Last updated: %1$s</string>
|
||||||
|
<string name="oss_attribution_tap_to_view_license">Tap to view license</string>
|
||||||
|
|
||||||
<string name="comingsoon_toast">Sorry, this feature is not ready yet.</string>
|
<string name="comingsoon_toast">Sorry, this feature is not ready yet.</string>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue