Move to official aboutlibraries m3 widget

This commit is contained in:
GG Bond authored and Vladimir Dostoyevskiy committed 2024-01-07 13:36:53 +08:00
1 parent c82de0b779
commit c3ffab3c79
6 files changed
+20 -354

No files matched your search

+1
View File
@@ -242,6 +242,7 @@ dependencies {
implementation(libs.diff)
implementation(libs.aboutlibraries.core)
implementation(libs.aboutlibraries.compose.m3)
implementation(libs.insetter) // Dead Dependency
@@ -1,9 +1,11 @@
package com.hippo.ehviewer.ui.settings
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
@@ -11,9 +13,13 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.hippo.ehviewer.R
import com.mikepenz.aboutlibraries.ui.compose.LibrariesContainer
import com.hippo.ehviewer.ui.openBrowser
import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer
import com.mikepenz.aboutlibraries.ui.compose.m3.LibraryDefaults
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
@@ -21,6 +27,7 @@ import com.ramcosta.composedestinations.navigation.DestinationsNavigator
@Composable
fun LicenseScreen(navigator: DestinationsNavigator) {
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
val context = LocalContext.current
Scaffold(
topBar = {
TopAppBar(
@@ -33,7 +40,15 @@ fun LicenseScreen(navigator: DestinationsNavigator) {
scrollBehavior = scrollBehavior,
)
},
) {
LibrariesContainer(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), contentPadding = it)
) { paddingValues ->
LibrariesContainer(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
contentPadding = paddingValues,
colors = LibraryDefaults.libraryColors(badgeBackgroundColor = MaterialTheme.colorScheme.tertiary),
padding = LibraryDefaults.libraryPadding(badgeContentPadding = PaddingValues(4.dp)),
onLibraryClick = { library ->
library.library.website?.let { context.openBrowser(it) }
},
)
}
}
@@ -1,72 +0,0 @@
package com.mikepenz.aboutlibraries.ui.compose
import android.content.Context
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.produceState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.hippo.ehviewer.R
import com.hippo.ehviewer.ui.openBrowser
import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.entity.Library
import com.mikepenz.aboutlibraries.util.withJson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
/**
* Displays all provided libraries in a simple list.
*/
@Composable
fun LibrariesContainer(
modifier: Modifier = Modifier,
lazyListState: LazyListState = rememberLazyListState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
librariesBlock: (Context) -> Libs = { context ->
Libs.Builder().withJson(context, R.raw.aboutlibraries).build()
},
showAuthor: Boolean = true,
showVersion: Boolean = true,
showLicenseBadges: Boolean = true,
padding: LibraryPadding = LibraryDefaults.libraryPadding(),
itemContentPadding: PaddingValues = LibraryDefaults.ContentPadding,
itemSpacing: Dp = LibraryDefaults.LibraryItemSpacing,
header: (LazyListScope.() -> Unit)? = null,
onLibraryClick: ((Library) -> Unit)? = null,
) {
val context = LocalContext.current
val libraries = produceState<Libs?>(null) {
value = withContext(Dispatchers.IO) {
librariesBlock(context)
}
}
val libs = libraries.value?.libraries?.sortedBy { it.name.lowercase() }
if (libs != null) {
Libraries(
libraries = libs,
modifier = modifier,
lazyListState = lazyListState,
contentPadding = contentPadding,
showAuthor = showAuthor,
showVersion = showVersion,
showLicenseBadges = showLicenseBadges,
padding = padding,
itemContentPadding = itemContentPadding,
itemSpacing = itemSpacing,
header = header,
onLibraryClick = { library ->
if (onLibraryClick != null) {
onLibraryClick.invoke(library)
} else {
library.website?.let { context.openBrowser(it) }
}
},
)
}
}
@@ -1,266 +0,0 @@
package com.mikepenz.aboutlibraries.ui.compose
import android.widget.TextView
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SuggestionChip
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.Typography
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.text.HtmlCompat
import androidx.core.text.parseAsHtml
import com.mikepenz.aboutlibraries.entity.Library
import com.mikepenz.aboutlibraries.ui.compose.util.author
import com.mikepenz.aboutlibraries.ui.compose.util.htmlReadyLicenseContent
/**
* Displays all provided libraries in a simple list.
*/
@Composable
fun Libraries(
libraries: List<Library>,
modifier: Modifier = Modifier,
lazyListState: LazyListState = rememberLazyListState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
showAuthor: Boolean = true,
showVersion: Boolean = true,
showLicenseBadges: Boolean = true,
padding: LibraryPadding = LibraryDefaults.libraryPadding(),
itemContentPadding: PaddingValues = LibraryDefaults.ContentPadding,
itemSpacing: Dp = LibraryDefaults.LibraryItemSpacing,
header: (LazyListScope.() -> Unit)? = null,
onLibraryClick: ((Library) -> Unit)? = null,
) {
LazyColumn(
modifier,
verticalArrangement = Arrangement.spacedBy(itemSpacing),
state = lazyListState,
contentPadding = contentPadding,
) {
header?.invoke(this)
libraryItems(
libraries,
showAuthor,
showVersion,
showLicenseBadges,
padding,
itemContentPadding,
) {
onLibraryClick?.invoke(it)
}
}
}
internal inline fun LazyListScope.libraryItems(
libraries: List<Library>,
showAuthor: Boolean = true,
showVersion: Boolean = true,
showLicenseBadges: Boolean = true,
padding: LibraryPadding,
itemContentPadding: PaddingValues = LibraryDefaults.ContentPadding,
crossinline onLibraryClick: ((Library) -> Unit),
) {
items(libraries) { library ->
Library(
library,
showAuthor,
showVersion,
showLicenseBadges,
padding,
itemContentPadding,
) {
onLibraryClick.invoke(library)
}
}
}
@Composable
internal fun Library(
library: Library,
showAuthor: Boolean = true,
showVersion: Boolean = true,
showLicenseBadges: Boolean = true,
padding: LibraryPadding = LibraryDefaults.libraryPadding(),
contentPadding: PaddingValues = LibraryDefaults.ContentPadding,
typography: Typography = MaterialTheme.typography,
onClick: () -> Unit,
) {
Column(modifier = Modifier.fillMaxWidth().clickable { onClick.invoke() }.padding(contentPadding)) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
text = library.name,
modifier = Modifier.padding(padding.namePadding).weight(1f),
style = typography.titleMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
val version = library.artifactVersion
if (version != null && showVersion) {
Text(
version,
modifier = Modifier.padding(padding.versionPadding),
style = typography.bodyMedium,
textAlign = TextAlign.Center,
)
}
}
val author = library.author
if (showAuthor && author.isNotBlank()) {
Text(
text = author,
style = typography.bodyMedium,
)
}
var openDialog by remember { mutableStateOf(false) }
if (openDialog) {
LicenseDialog(library = library) {
openDialog = false
}
}
if (showLicenseBadges && library.licenses.isNotEmpty()) {
Row {
library.licenses.forEach {
SuggestionChip(
onClick = { openDialog = true },
label = { Text(text = it.name) },
)
}
}
}
}
}
@Composable
fun LicenseDialog(
library: Library,
onDismiss: () -> Unit,
) {
val scrollState = rememberScrollState()
AlertDialog(
onDismissRequest = onDismiss,
confirmButton = {
TextButton(onClick = onDismiss) {
Text(stringResource(id = android.R.string.ok))
}
},
text = {
Column(modifier = Modifier.verticalScroll(scrollState)) {
HtmlText(html = library.licenses.firstOrNull()?.htmlReadyLicenseContent.orEmpty())
}
},
)
}
@Composable
fun HtmlText(
html: String,
modifier: Modifier = Modifier,
) {
AndroidView(
modifier = modifier,
factory = { context -> TextView(context) },
update = { it.text = html.parseAsHtml(flags = HtmlCompat.FROM_HTML_MODE_COMPACT) },
)
}
/**
* Contains the default values used by [Library]
*/
object LibraryDefaults {
private val LibraryItemPadding = 16.dp
private val LibraryNamePaddingTop = 4.dp
private val LibraryVersionPaddingStart = 8.dp
private val LibraryBadgePaddingTop = 8.dp
private val LibraryBadgePaddingEnd = 4.dp
internal val LibraryItemSpacing = 0.dp
/**
* The default content padding used by [Library]
*/
val ContentPadding = PaddingValues(LibraryItemPadding)
/**
* Creates a [LibraryPadding] that represents the default paddings used in a [Library]
*
* @param namePadding the padding around the name shown as part of a [Library]
* @param versionPadding the padding around the version shown as part of a [Library]
* @param badgePadding the padding around a badge element shown as part of a [Library]
* @param badgeContentPadding the padding around the content of a badge element shown as part of a [Library]
*/
@Composable
fun libraryPadding(
namePadding: PaddingValues = PaddingValues(top = LibraryNamePaddingTop),
versionPadding: PaddingValues = PaddingValues(start = LibraryVersionPaddingStart),
badgePadding: PaddingValues = PaddingValues(
top = LibraryBadgePaddingTop,
end = LibraryBadgePaddingEnd,
),
badgeContentPadding: PaddingValues = PaddingValues(4.dp),
): LibraryPadding = DefaultLibraryPadding(
namePadding = namePadding,
versionPadding = versionPadding,
badgePadding = badgePadding,
badgeContentPadding = badgeContentPadding,
)
}
/**
* Represents the padding values used in a library.
*/
@Stable
interface LibraryPadding {
/** Represents the padding around the name shown as part of a [Library] */
val namePadding: PaddingValues
/** Represents the padding around the version shown as part of a [Library] */
val versionPadding: PaddingValues
/** Represents the padding around a badge element shown as part of a [Library] */
val badgePadding: PaddingValues
/** Represents the padding around the content of a badge element shown as part of a [Library] */
val badgeContentPadding: PaddingValues
}
/**
* Default [LibraryPadding].
*/
@Immutable
private class DefaultLibraryPadding(
override val namePadding: PaddingValues,
override val versionPadding: PaddingValues,
override val badgePadding: PaddingValues,
override val badgeContentPadding: PaddingValues,
) : LibraryPadding
@@ -1,13 +0,0 @@
package com.mikepenz.aboutlibraries.ui.compose.util
import com.mikepenz.aboutlibraries.entity.Library
import com.mikepenz.aboutlibraries.entity.License
val Library.author: String
get() = developers.takeIf { it.isNotEmpty() }?.map { it.name }?.joinToString(", ") ?: organization?.name ?: ""
val License.htmlReadyLicenseContent: String?
get() = licenseContent?.replace("\n", "<br />")
val License.strippedLicenseContent: String?
get() = licenseContent?.replace("<br />", "\n")?.replace("<br/>", "\n")
+1
View File
@@ -34,6 +34,7 @@ splitties = "3.0.0"
[libraries]
aboutlibraries-core = { module = "com.mikepenz:aboutlibraries-core", version.ref = "aboutlibraries" }
aboutlibraries-compose-m3 = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "aboutlibraries" }
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }