HapeLee vor 11 Monaten
Ursprung
Commit
158b472bef

+ 44 - 1
app/build.gradle

@@ -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
+
 }

+ 28 - 86
app/src/main/java/io/legado/app/ui/about/AboutActivity.kt

@@ -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)
-    }
-
-    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)
+        return true
     }
 
-    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 {

+ 107 - 0
app/src/main/java/io/legado/app/ui/about/AboutScreen.kt

@@ -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
build.gradle

@@ -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

+ 17 - 0
gradle/libs.versions.toml

@@ -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" }