Compose
This commit is contained in:
+44
-1
@@ -5,7 +5,7 @@ plugins {
|
||||
// //id 'kotlin-kapt'
|
||||
// id 'com.google.devtools.ksp'
|
||||
// id "com.google.gms.google-services"
|
||||
|
||||
alias libs.plugins.compose.compiler
|
||||
alias libs.plugins.android.application
|
||||
alias libs.plugins.kotlin.android
|
||||
alias libs.plugins.kotlin.parcelize
|
||||
@@ -48,6 +48,11 @@ def getGitCommitHash() {
|
||||
android {
|
||||
compileSdk = compile_sdk_version
|
||||
namespace = "io.legado.app"
|
||||
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(21))
|
||||
@@ -334,4 +339,42 @@ dependencies {
|
||||
implementation libs.androidx.palette
|
||||
implementation libs.androidx.core.splashscreen
|
||||
implementation libs.androidx.startup.runtime
|
||||
|
||||
// Compose 基础
|
||||
implementation platform(libs.androidx.compose.bom)
|
||||
implementation libs.activity.compose
|
||||
implementation libs.androidx.compose.ui
|
||||
implementation libs.androidx.compose.ui.tooling.preview
|
||||
debugImplementation libs.androidx.compose.ui.tooling
|
||||
|
||||
// Material3
|
||||
implementation libs.androidx.compose.material3
|
||||
|
||||
// Lifecycle
|
||||
implementation libs.androidx.lifecycle.runtime.compose
|
||||
|
||||
// Activity
|
||||
implementation libs.androidx.activity.compose
|
||||
|
||||
// Navigation(用于弹框或未来扩展)
|
||||
implementation libs.navigation.compose
|
||||
|
||||
// Coil
|
||||
implementation libs.coil.compose
|
||||
|
||||
// accompanist – WebView(打开网页)
|
||||
implementation libs.accompanist.webview
|
||||
|
||||
// Markdown 渲染(Compose MD)
|
||||
implementation libs.markdown.compose
|
||||
|
||||
// 协程
|
||||
implementation libs.kotlinx.coroutines.android
|
||||
implementation libs.androidx.lifecycle.viewmodel.compose
|
||||
implementation libs.androidx.lifecycle.runtime.compose
|
||||
implementation libs.androidx.compose.animation
|
||||
implementation libs.androidx.compose.foundation
|
||||
implementation libs.androidx.constraintlayout.compose
|
||||
implementation libs.androidx.compose.ui.viewbinding
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,11 @@ package io.legado.app.ui.about
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.core.app.ComponentActivity
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import io.legado.app.BuildConfig
|
||||
@@ -47,9 +51,7 @@ import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
|
||||
class AboutActivity : BaseActivity<ActivityAboutBinding>() {
|
||||
|
||||
override val binding by viewBinding(ActivityAboutBinding::inflate)
|
||||
class AboutActivity : AppCompatActivity() {
|
||||
|
||||
private val waitDialog by lazy {
|
||||
WaitDialog(this).setText(R.string.checking_update)
|
||||
@@ -57,96 +59,36 @@ class AboutActivity : BaseActivity<ActivityAboutBinding>() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setSupportActionBar(binding.topBar)
|
||||
|
||||
binding.tvVersion.text = appInfo.versionName
|
||||
|
||||
binding.btnUpdate.setOnClickListener {
|
||||
checkUpdate()
|
||||
}
|
||||
|
||||
binding.btnGithub.setOnClickListener {
|
||||
openUrl(R.string.github_url)
|
||||
}
|
||||
|
||||
binding.btnWeb.setOnClickListener {
|
||||
openUrl(R.string.legado_url)
|
||||
}
|
||||
|
||||
binding.llContributors.setOnClickListener {
|
||||
openUrl(R.string.contributors_url)
|
||||
}
|
||||
|
||||
binding.llUpdateLog.setOnClickListener {
|
||||
lifecycleScope.launch {
|
||||
upVersion()
|
||||
setContent {
|
||||
MaterialTheme {
|
||||
AboutScreen(
|
||||
onCheckUpdate = { checkUpdate() },
|
||||
onOpenUrl = { openUrl(it) },
|
||||
onShowMdFile = { title, file -> showMdFile(title, file) },
|
||||
onSaveLog = { saveLog() },
|
||||
onCreateHeapDump = { createHeapDump() },
|
||||
onShowCrashLogs = { showDialogFragment<CrashLogsDialog>() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
binding.llPrivacyPolicy.setOnClickListener {
|
||||
showMdFile(getString(R.string.privacy_policy), "privacyPolicy.md")
|
||||
}
|
||||
|
||||
binding.llLicense.setOnClickListener {
|
||||
showMdFile(getString(R.string.license), "LICENSE.md")
|
||||
}
|
||||
|
||||
binding.llDisclaimer.setOnClickListener {
|
||||
showMdFile(getString(R.string.disclaimer), "disclaimer.md")
|
||||
}
|
||||
|
||||
binding.llCrashLog.setOnClickListener {
|
||||
showDialogFragment<CrashLogsDialog>()
|
||||
}
|
||||
|
||||
binding.llSaveLog.setOnClickListener {
|
||||
saveLog()
|
||||
}
|
||||
|
||||
binding.llCreateHeapDump.setOnClickListener {
|
||||
createHeapDump()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
menuInflater.inflate(R.menu.about, menu)
|
||||
return super.onCompatCreateOptionsMenu(menu)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.menu_share_it -> share(
|
||||
getString(R.string.app_share_description),
|
||||
getString(R.string.app_name)
|
||||
)
|
||||
}
|
||||
return super.onCompatOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private suspend fun upVersion() {
|
||||
try {
|
||||
val info = withContext(Dispatchers.IO) {
|
||||
AppUpdateGitHub.getReleaseByTag(BuildConfig.VERSION_NAME)
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
val dialog = if (info != null) {
|
||||
UpdateDialog(info, UpdateDialog.Mode.VIEW_LOG)
|
||||
} else {
|
||||
val fallback = String(assets.open("updateLog.md").readBytes())
|
||||
TextDialog(getString(R.string.update_log), fallback, TextDialog.Mode.MD)
|
||||
}
|
||||
|
||||
showDialogFragment(dialog)
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
val fallback = String(assets.open("updateLog.md").readBytes())
|
||||
val dialog = TextDialog(getString(R.string.update_log), fallback, TextDialog.Mode.MD)
|
||||
withContext(Dispatchers.Main) {
|
||||
showDialogFragment(dialog)
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.menu_share_it -> {
|
||||
share(
|
||||
getString(R.string.app_share_description),
|
||||
getString(R.string.app_name)
|
||||
)
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,9 +100,9 @@ class AboutActivity : BaseActivity<ActivityAboutBinding>() {
|
||||
private fun checkUpdate() {
|
||||
waitDialog.show()
|
||||
AppUpdate.gitHubUpdate?.run {
|
||||
check(lifecycleScope)
|
||||
kotlin.check(lifecycleScope)
|
||||
.onSuccess {
|
||||
showDialogFragment(UpdateDialog(it, UpdateDialog.Mode.UPDATE))
|
||||
showDialogFragment(UpdateDialog(it))
|
||||
}.onError {
|
||||
appCtx.toastOnUi("${getString(R.string.check_update)}\n${it.localizedMessage}")
|
||||
}.onFinally {
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
package io.legado.app.ui.about
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.DividerDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
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.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.legado.app.R
|
||||
import io.legado.app.constant.AppConst.appInfo
|
||||
|
||||
|
||||
@Composable
|
||||
fun AboutScreen(
|
||||
onCheckUpdate: () -> Unit = {},
|
||||
onOpenUrl: (String) -> Unit = {},
|
||||
onShowMdFile: (String, String) -> Unit = { _, _ -> },
|
||||
onSaveLog: () -> Unit = {},
|
||||
onCreateHeapDump: () -> Unit = {},
|
||||
onShowCrashLogs: () -> Unit = {},
|
||||
versionName: String = "1.0.0"
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val version = remember { appInfo.versionName }
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.about)) }
|
||||
)
|
||||
}
|
||||
) { padding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(padding)
|
||||
.padding(16.dp)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
Text(text = "版本:$version", style = MaterialTheme.typography.bodyLarge)
|
||||
|
||||
Spacer(Modifier.height(8.dp))
|
||||
Button(onClick = onCheckUpdate) {
|
||||
Text(stringResource(R.string.check_update))
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(8.dp))
|
||||
Button(onClick = { onOpenUrl(context.getString(R.string.github_url)) }) {
|
||||
Text(stringResource(R.string.github_url))
|
||||
}
|
||||
|
||||
Button(onClick = { onOpenUrl(context.getString(R.string.legado_url)) }) {
|
||||
Text(stringResource(R.string.web_service))
|
||||
}
|
||||
|
||||
HorizontalDivider(
|
||||
Modifier.padding(vertical = 8.dp),
|
||||
DividerDefaults.Thickness,
|
||||
DividerDefaults.color
|
||||
)
|
||||
|
||||
Text(text = "文档与政策", style = MaterialTheme.typography.titleMedium)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
TextButton(onClick = { onShowMdFile(context.getString(R.string.privacy_policy), "privacyPolicy.md") }) {
|
||||
Text(stringResource(R.string.privacy_policy))
|
||||
}
|
||||
TextButton(onClick = { onShowMdFile(context.getString(R.string.license), "LICENSE.md") }) {
|
||||
Text(stringResource(R.string.license))
|
||||
}
|
||||
TextButton(onClick = { onShowMdFile(context.getString(R.string.disclaimer), "disclaimer.md") }) {
|
||||
Text(stringResource(R.string.disclaimer))
|
||||
}
|
||||
|
||||
HorizontalDivider(
|
||||
Modifier.padding(vertical = 8.dp),
|
||||
DividerDefaults.Thickness,
|
||||
DividerDefaults.color
|
||||
)
|
||||
|
||||
Text(text = "开发工具", style = MaterialTheme.typography.titleMedium)
|
||||
Spacer(Modifier.height(4.dp))
|
||||
TextButton(onClick = onShowCrashLogs) {
|
||||
Text(stringResource(R.string.crash_log))
|
||||
}
|
||||
TextButton(onClick = onSaveLog) {
|
||||
Text(stringResource(R.string.save_log))
|
||||
}
|
||||
TextButton(onClick = onCreateHeapDump) {
|
||||
Text(stringResource(R.string.create_heap_dump))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -20,7 +20,7 @@ plugins {
|
||||
//id 'com.google.devtools.ksp' version "$kotlin_version-$ksp_version" apply false
|
||||
//id "de.undercouch.download" version "5.5.0" apply false
|
||||
//id "com.google.gms.google-services" version "4.4.0" apply false
|
||||
|
||||
alias libs.plugins.compose.compiler apply false
|
||||
alias libs.plugins.android.application apply false
|
||||
alias libs.plugins.android.library apply false
|
||||
alias libs.plugins.kotlin.android apply false
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
[versions]
|
||||
|
||||
accompanistWebview = "0.36.0"
|
||||
coilCompose = "2.7.0"
|
||||
composeBom = "2025.11.00"
|
||||
coreSplashscreen = "1.2.0-rc01"
|
||||
datastorePreferences = "1.1.7"
|
||||
kotlin = "2.2.20"
|
||||
kotlinxCoroutinesAndroid = "1.8.1"
|
||||
ksp = "2.2.20-2.0.4"
|
||||
agp = "8.13.1"
|
||||
appcompat = "1.7.1"
|
||||
@@ -28,6 +32,7 @@ jsoup = "1.16.2"
|
||||
jsoupxpath = "2.5.3"
|
||||
coroutines = "1.10.2"
|
||||
liveeventbus = "1.8.14"
|
||||
markdownCompose = "0.5.0"
|
||||
markwon = "4.6.2"
|
||||
material = "1.14.0-alpha06"
|
||||
media = "1.7.1"
|
||||
@@ -59,22 +64,31 @@ zxingLite = "3.3.0"
|
||||
|
||||
[libraries]
|
||||
|
||||
accompanist-webview = { module = "com.google.accompanist:accompanist-webview", version.ref = "accompanistWebview" }
|
||||
activity-activity = { module = "androidx.activity:activity", version.ref = "activity" }
|
||||
activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activity" }
|
||||
activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activity" }
|
||||
|
||||
androidx-activity-compose = { module = "androidx.activity:activity-compose" }
|
||||
androidx-annotation = { group = "androidx.annotation", name = "annotation", version = "1.9.1" }
|
||||
#androidx-annotation-experimental = { group = "androidx.annotation", name = "annotation-experimental", version = "1.3.1" }
|
||||
|
||||
androidx-collection = { module = "androidx.collection:collection", version.ref = "collection" }
|
||||
androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "composeBom" }
|
||||
androidx-compose-material3 = { module = "androidx.compose.material3:material3" }
|
||||
androidx-compose-ui = { module = "androidx.compose.ui:ui" }
|
||||
androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" }
|
||||
androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
|
||||
androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" }
|
||||
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
|
||||
androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose" }
|
||||
androidx-palette = { module = "androidx.palette:palette", version.ref = "palette" }
|
||||
androidx-startup-runtime = { module = "androidx.startup:startup-runtime", version.ref = "startupRuntime" }
|
||||
appcompat-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
|
||||
androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayout" }
|
||||
|
||||
|
||||
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coilCompose" }
|
||||
core-core = { module = "androidx.core:core", version.ref = "core" }
|
||||
core-ktx = { module = "androidx.core:core-ktx", version.ref = "core" }
|
||||
|
||||
@@ -93,10 +107,12 @@ androidx-junit = { module = "androidx.test.ext:junit", version = "1.3.0" }
|
||||
|
||||
hutool-crypto = { module = "cn.hutool:hutool-crypto", version.ref = "hutool" }
|
||||
|
||||
kotlinx-coroutines-android-v181 = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesAndroid" }
|
||||
libarchive = { module = "me.zhanghai.android.libarchive:library", version.ref = "libarchive" }
|
||||
lifecycle-common-java8 = { module = "androidx.lifecycle:lifecycle-common-java8", version.ref = "lifecycle" }
|
||||
lifecycle-service = { module = "androidx.lifecycle:lifecycle-service", version.ref = "lifecycle" }
|
||||
|
||||
markdown-compose = { module = "com.mikepenz:markdown-compose", version.ref = "markdownCompose" }
|
||||
media-media = { module = "androidx.media:media", version.ref = "media" }
|
||||
media3-datasource-okhttp = { module = "androidx.media3:media3-datasource-okhttp", version.ref = "media3" }
|
||||
media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3" }
|
||||
@@ -112,6 +128,7 @@ nanohttpd-nanohttpd = { module = "org.nanohttpd:nanohttpd", version.ref = "nanoH
|
||||
nanohttpd-websocket = { module = "org.nanohttpd:nanohttpd-websocket", version.ref = "nanoHttpd" }
|
||||
|
||||
|
||||
navigation-compose = { module = "androidx.navigation:navigation-compose" }
|
||||
preference-preference = { module = "androidx.preference:preference", version.ref = "preference" }
|
||||
preference-ktx = { module = "androidx.preference:preference-ktx", version.ref = "preference" }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user