Add LicenseFragment

This commit is contained in:
tarsin committed 2023-05-27 00:55:03 +08:00
1 parent 92240c6c2c
commit 9ad933934f
11 files changed
+407

No files matched your search

+3
View File
@@ -11,6 +11,7 @@ plugins {
id("kotlin-parcelize")
kotlin("plugin.serialization")
id("com.google.devtools.ksp")
id("com.mikepenz.aboutlibraries.plugin")
}
android {
@@ -215,6 +216,8 @@ dependencies {
implementation("com.squareup.okio:okio-jvm:3.3.0")
implementation("com.mikepenz:aboutlibraries-core:10.7.0")
implementation("dev.chrisbanes.insetter:insetter:0.6.1") // Dead Dependency
implementation("dev.rikka.rikkax.core:core-ktx:1.4.1")
implementation("dev.rikka.rikkax.insets:insets:1.3.0")
@@ -59,6 +59,7 @@ open class BasePreferenceFragment :
"mytags" -> MyTagsFragment()
"uconfig" -> UConfigFragment()
"hosts" -> HostsFragment()
"license" -> LicenseFragment()
else -> null
}
fragment?.let {
@@ -0,0 +1,30 @@
package com.hippo.ehviewer.ui.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import com.hippo.ehviewer.R
import com.hippo.ehviewer.ui.compose.setMD3Content
import com.mikepenz.aboutlibraries.ui.compose.LibrariesContainer
class LicenseFragment : BaseFragment() {
override fun getFragmentTitle(): Int {
return R.string.license
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
return ComposeView(inflater.context).apply {
setMD3Content {
LibrariesContainer(modifier = Modifier.fillMaxSize())
}
}
}
}
@@ -0,0 +1,127 @@
package com.mikepenz.aboutlibraries.ui.compose
import android.content.Context
import android.widget.TextView
import androidx.compose.foundation.layout.Column
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.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.text.HtmlCompat
import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.entity.Library
import com.mikepenz.aboutlibraries.ui.compose.util.htmlReadyLicenseContent
import com.mikepenz.aboutlibraries.util.withContext
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().withContext(context).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
if (libs != null) {
val openDialog = remember { mutableStateOf<Library?>(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(library)
} else if (!library.licenses.firstOrNull()?.htmlReadyLicenseContent.isNullOrBlank()) {
openDialog.value = library
}
},
)
val library = openDialog.value
if (library != null) {
LicenseDialog(library = library) {
openDialog.value = null
}
}
}
}
@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 = HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_COMPACT) })
}
@@ -0,0 +1,224 @@
package com.mikepenz.aboutlibraries.ui.compose
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.material3.Badge
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.Typography
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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 com.mikepenz.aboutlibraries.entity.Library
import com.mikepenz.aboutlibraries.ui.compose.util.author
/**
* 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,
)
}
if (showLicenseBadges && library.licenses.isNotEmpty()) {
Row {
library.licenses.forEach {
Badge(
modifier = Modifier.padding(padding.badgePadding),
) {
Text(
modifier = Modifier.padding(padding.badgeContentPadding),
text = it.name,
)
}
}
}
}
}
}
/**
* 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(0.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
@@ -0,0 +1,13 @@
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
@@ -0,0 +1 @@
{"metadata":{"generated":"2023-05-26T16:18:05.612Z"},"libraries":[{"uniqueId":"androidx.activity:activity","funding":[],"developers":[{"name":"The Android Open Source Project"}],"artifactVersion":"1.7.2","description":"Provides the base Activity subclass and the relevant hooks to build a composable structure on top.","scm":{"connection":"scm:git:https://android.googlesource.com/platform/frameworks/support","url":"https://cs.android.com/androidx/platform/frameworks/support"},"name":"Activity","website":"https://developer.android.com/jetpack/androidx/releases/activity#1.7.2","licenses":["Apache-2.0"]},{"uniqueId":"androidx.activity:activity-compose","funding":[],"developers":[{"name":"The Android Open Source Project"}],"artifactVersion":"1.7.2","description":"Compose integration with Activity","scm":{"connection":"scm:git:https://android.googlesource.com/platform/frameworks/support","url":"https://cs.android.com/androidx/platform/frameworks/support"},"name":"Activity Compose","website":"https://developer.android.com/jetpack/androidx/releases/activity#1.7.2","licenses":["Apache-2.0"]},{"uniqueId":"androidx.activity:activity-ktx","funding":[],"developers":[{"name":"The Android Open Source Project"}],"artifactVersion":"1.7.2","description":"Kotlin extensions for 'activity' artifact","scm":{"connection":"scm:git:https://android.googlesource.com/platform/frameworks/support","url":"https://cs.android.com/androidx/platform/frameworks/support"},"name":"Activity Kotlin Extensions","website":"https://developer.android.com/jetpack/androidx/releases/activity#1.7.2","licenses":["Apache-2.0"]},{"uniqueId":"androidx.annotation:annotation-experimental","funding":[],"developers":[{"name":"The Android Open Source Project"}],"artifactVersion":"1.3.0","description":"Java annotation for use on unstable Android API surfaces. When used in conjunction with the Experimental annotation lint checks, this annotation provides functional parity with Kotlin's Experimental annotation.","scm":{"connection":"scm:git:https://android.googlesource.com/platform/frameworks/support","url":"https://cs.android.com/androidx/platform/frameworks/support"},"name":"Experimental annotation","website":"https://developer.android.com/jetpack/androidx/releases/annotation#1.3.0","licenses":["Apache-2.0"]},{"uniqueId":"androidx.annotation:annotation-jvm","funding":[],"developers":[{"name":"The Android Open Source Project"}],"artifactVersion":"1.6.0","description":"The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs.","scm":{"connection":"scm:git:https://android.googlesource.com/platform/frameworks/support","url":"https://cs.android.com/androidx/platform/frameworks/support"},"name":"Support Annotations","website":"https://developer.android.com/jetpack/androidx/releases/annotation#1.6.0","licenses":["Apache-2.0"]},{"uniqueId":"androidx.appcompat:appcompat","funding":[],"developers":[{"name":"The Android Open Source Project"}],"artifactVersion":"1.6.1","description":"The Support Library is a static library that you can add to your Android application in order to use APIs that are either not available for older platform versions or utility APIs that aren't a part of the framework APIs. Compatible on devices running API 14 or later.","scm":{"connection":"scm:git:https://android.googlesource.com/platform/frameworks/support","url":"https://cs.android.com/androidx/platform/frameworks/support"},"name":"Android AppCompat Library","website":"https://developer.android.com/jetpack/androidx/releases/appcompat#1.6.1","licenses":["Apache-2.0"]},{"uniqueId":"androidx.appcompat:appcompat-resources","funding":[],"developers":[{"name":"The Android Open Source Project"}],"artifactVersion":"1.6.1","description":"The Resources Library is a static library that you can add to your Android application in order to use resource APIs that backport the latest APIs to older versions of the platform. Compatible on devices running API 14 or later.","scm":{"connection":"scm:git:https://android.googlesource.com/platform/frameworks/support","url":"https://cs.android.com/androidx/platform/frameworks/support"},"name":"Android Resources Library","website":"https://developer.android.com/jetpack/androidx/releases/appcompat#1.6.1","licenses":["Apache-2.0"]},{"uniqueId":"androidx.arch.core:core-common","funding":[],"developers":[{"name":"The Android Open Source Project"}],"artifactVersion":"2.2.0","description":"Android Arch-Common","scm":{"connection":"scm:git:https://android.googlesource.com/platform/frameworks/support","url":"https://cs.android.com/androidx/platform/frameworks/support"},"name":"Android Arch-Common","website":"https://developer.android.com/jetpack/androidx/releases/arch-core#2.2.0","licenses":["Apache-2.0"]},{"uniqueId":"androidx.arch.core:core-runtime","funding":[],"developers":[{"name":"The Android Open Source ProjecLine truncated
@@ -414,6 +414,7 @@
<string name="settings_about_author">作者</string>
<string name="settings_about_latest_release">最新版本</string>
<string name="settings_about_source">源码</string>
<string name="license">许可证</string>
<string name="settings_about_version">版本号</string>
<string name="settings_about_build_time">于 %s 构建</string>
<string name="settings_about_check_for_updates">检查更新</string>
+1
View File
@@ -463,6 +463,7 @@
<string name="settings_about_version">Build version</string>
<string name="settings_about_build_time">Built at %s</string>
<string name="settings_about_check_for_updates">Check for updates</string>
<string name="license">License</string>
<string name="proxy_direct">Direct</string>
<string name="proxy_system">System Proxy</string>
<string name="proxy_host_or_ip">Host or IP</string>
+5
View File
@@ -43,6 +43,11 @@
app:iconSpaceReserved="false"
app:url="https://github.com/Ehviewer-Overhauled/Ehviewer" />
<Preference
android:key="license"
android:title="@string/license"
android:iconSpaceReserved="false"/>
<com.hippo.ehviewer.preference.VersionPreference
android:key="version"
app:iconSpaceReserved="false" />
+1
View File
@@ -3,6 +3,7 @@ plugins {
kotlin("android") version "1.9.0-Beta" apply false
kotlin("plugin.serialization") version "1.9.0-Beta" apply false
id("com.google.devtools.ksp") version "1.9.0-Beta-1.0.11" apply false
id("com.mikepenz.aboutlibraries.plugin") version "10.7.0" apply false
}
allprojects {