更新关于界面

搜索书籍界面切换至SearchBar与SearchView
This commit is contained in:
HapeLee committed 2025-08-06 17:52:07 +08:00
1 parent 3db111e87d
commit ccaf8973fe
18 files changed
+798 -490

No files matched your search

@@ -1,53 +1,97 @@
package io.legato.kazusa.ui.about
import android.os.Bundle
import android.text.Spannable
import android.text.SpannableString
import android.util.TypedValue
import android.view.Menu
import android.view.MenuItem
import io.legato.kazusa.R
import io.legato.kazusa.base.BaseActivity
import io.legato.kazusa.databinding.ActivityAboutBinding
//import io.legado.app.lib.theme.accentColor
//import io.legado.app.lib.theme.filletBackground
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.annotation.StringRes
import androidx.core.net.toUri
import androidx.lifecycle.lifecycleScope
import io.legato.kazusa.R
import io.legato.kazusa.base.BaseActivity
import io.legato.kazusa.constant.AppLog
import io.legato.kazusa.databinding.ActivityAboutBinding
import io.legato.kazusa.help.CrashHandler
import io.legato.kazusa.help.config.AppConfig
import io.legato.kazusa.help.coroutine.Coroutine
import io.legato.kazusa.help.update.AppUpdate
import io.legato.kazusa.ui.widget.dialog.TextDialog
import io.legato.kazusa.ui.widget.dialog.WaitDialog
import io.legato.kazusa.utils.FileDoc
import io.legato.kazusa.utils.compress.ZipUtils
import io.legato.kazusa.utils.createFileIfNotExist
import io.legato.kazusa.utils.createFolderIfNotExist
import io.legato.kazusa.utils.delete
import io.legato.kazusa.utils.externalCache
import io.legato.kazusa.utils.find
import io.legato.kazusa.utils.list
import io.legato.kazusa.utils.openInputStream
import io.legato.kazusa.utils.openOutputStream
import io.legato.kazusa.utils.openUrl
import io.legato.kazusa.utils.share
import io.legato.kazusa.utils.showDialogFragment
import io.legato.kazusa.utils.toastOnUi
import io.legato.kazusa.utils.viewbindingdelegate.viewBinding
import kotlinx.coroutines.delay
import splitties.init.appCtx
import java.io.File
class AboutActivity : BaseActivity<ActivityAboutBinding>() {
override val binding by viewBinding(ActivityAboutBinding::inflate)
private val waitDialog by lazy {
WaitDialog(this).setText(R.string.checking_update)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setSupportActionBar(binding.topBar)
val fTag = "aboutFragment"
var aboutFragment = supportFragmentManager.findFragmentByTag(fTag)
if (aboutFragment == null) aboutFragment = AboutFragment()
supportFragmentManager.beginTransaction()
.replace(R.id.fl_fragment, aboutFragment, fTag)
.commit()
binding.tvAppSummary.post {
kotlin.runCatching {
val typedValue = TypedValue()
val context = binding.root.context
context.theme.resolveAttribute(
androidx.appcompat.R.attr.colorPrimary,
typedValue,
true
)
val accentColor = typedValue.data
val spannableString = SpannableString(binding.tvAppSummary.text)
val gzh = getString(R.string.legado_gzh)
val start = spannableString.indexOf(gzh)
spannableString.setSpan(
accentColor, start, start + gzh.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
binding.tvAppSummary.text = spannableString
}
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 {
showMdFile(getString(R.string.update_log), "updateLog.md")
}
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()
}
}
@@ -58,7 +102,6 @@ class AboutActivity : BaseActivity<ActivityAboutBinding>() {
override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_scoring -> openUrl("market://details?id=$packageName")
R.id.menu_share_it -> share(
getString(R.string.app_share_description),
getString(R.string.app_name)
@@ -67,4 +110,118 @@ class AboutActivity : BaseActivity<ActivityAboutBinding>() {
return super.onCompatOptionsItemSelected(item)
}
private fun showMdFile(title: String, fileName: String) {
val mdText = String(this.assets.open(fileName).readBytes())
showDialogFragment(TextDialog(title, mdText, TextDialog.Mode.MD))
}
private fun checkUpdate() {
waitDialog.show()
AppUpdate.gitHubUpdate?.run {
check(lifecycleScope)
.onSuccess {
showDialogFragment(UpdateDialog(it))
}.onError {
appCtx.toastOnUi("${getString(R.string.check_update)}\n${it.localizedMessage}")
}.onFinally {
waitDialog.dismiss()
}
}
}
@Suppress("SameParameterValue")
private fun openUrl(@StringRes addressID: Int) {
this.openUrl(getString(addressID))
}
private fun saveLog() {
Coroutine.async {
val backupPath = AppConfig.backupPath ?: let {
appCtx.toastOnUi("未设置备份目录")
return@async
}
if (!AppConfig.recordLog) {
appCtx.toastOnUi("未开启日志记录,请去其他设置里打开记录日志")
delay(3000)
}
val doc = FileDoc.fromUri(backupPath.toUri(), true)
copyLogs(doc)
copyHeapDump(doc)
appCtx.toastOnUi("已保存至备份目录")
}.onError {
AppLog.put("保存日志出错\n${it.localizedMessage}", it, true)
}
}
private fun createHeapDump() {
Coroutine.async {
val backupPath = AppConfig.backupPath ?: let {
appCtx.toastOnUi("未设置备份目录")
return@async
}
if (!AppConfig.recordHeapDump) {
appCtx.toastOnUi("未开启堆转储记录,请去其他设置里打开记录堆转储")
delay(3000)
}
appCtx.toastOnUi("开始创建堆转储")
System.gc()
CrashHandler.doHeapDump(true)
val doc = FileDoc.fromUri(backupPath.toUri(), true)
if (!copyHeapDump(doc)) {
appCtx.toastOnUi("未找到堆转储文件")
} else {
appCtx.toastOnUi("已保存至备份目录")
}
}.onError {
AppLog.put("保存堆转储失败\n${it.localizedMessage}", it)
}
}
private fun copyLogs(doc: FileDoc) {
val cacheDir = appCtx.externalCache
val logFiles = File(cacheDir, "logs")
val crashFiles = File(cacheDir, "crash")
val logcatFile = File(cacheDir, "logcat.txt")
dumpLogcat(logcatFile)
val zipFile = File(cacheDir, "logs.zip")
ZipUtils.zipFiles(arrayListOf(logFiles, crashFiles, logcatFile), zipFile)
doc.find("logs.zip")?.delete()
zipFile.inputStream().use { input ->
doc.createFileIfNotExist("logs.zip").openOutputStream().getOrNull()
?.use {
input.copyTo(it)
}
}
zipFile.delete()
}
private fun copyHeapDump(doc: FileDoc): Boolean {
val heapFile = FileDoc.fromFile(File(appCtx.externalCache, "heapDump")).list()
?.firstOrNull() ?: return false
doc.find("heapDump")?.delete()
val heapDumpDoc = doc.createFolderIfNotExist("heapDump")
heapFile.openInputStream().getOrNull()?.use { input ->
heapDumpDoc.createFileIfNotExist(heapFile.name).openOutputStream().getOrNull()
?.use {
input.copyTo(it)
}
}
return true
}
private fun dumpLogcat(file: File) {
try {
val process = Runtime.getRuntime().exec("logcat -d")
file.outputStream().use {
process.inputStream.copyTo(it)
}
} catch (e: Exception) {
AppLog.put("保存Logcat失败\n$e", e)
}
}
}
@@ -1,213 +0,0 @@
package io.legato.kazusa.ui.about
import android.os.Bundle
import android.view.View
import androidx.annotation.StringRes
import androidx.lifecycle.lifecycleScope
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import io.legato.kazusa.R
import io.legato.kazusa.constant.AppConst.appInfo
import io.legato.kazusa.constant.AppLog
import io.legato.kazusa.help.CrashHandler
import io.legato.kazusa.help.config.AppConfig
import io.legato.kazusa.help.coroutine.Coroutine
import io.legato.kazusa.help.update.AppUpdate
import io.legato.kazusa.ui.widget.dialog.TextDialog
import io.legato.kazusa.ui.widget.dialog.WaitDialog
import io.legato.kazusa.utils.FileDoc
import io.legato.kazusa.utils.compress.ZipUtils
import io.legato.kazusa.utils.createFileIfNotExist
import io.legato.kazusa.utils.createFolderIfNotExist
import io.legato.kazusa.utils.delete
import io.legato.kazusa.utils.externalCache
import io.legato.kazusa.utils.find
import io.legato.kazusa.utils.list
import io.legato.kazusa.utils.openInputStream
import io.legato.kazusa.utils.openOutputStream
import io.legato.kazusa.utils.openUrl
import io.legato.kazusa.utils.sendMail
import io.legato.kazusa.utils.sendToClip
import io.legato.kazusa.utils.showDialogFragment
import io.legato.kazusa.utils.toastOnUi
import kotlinx.coroutines.delay
import splitties.init.appCtx
import java.io.File
import androidx.core.net.toUri
class AboutFragment : PreferenceFragmentCompat() {
private val waitDialog by lazy {
WaitDialog(requireContext())
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.about)
findPreference<Preference>("update_log")?.summary =
"${getString(R.string.version)} ${appInfo.versionName}"
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
listView.overScrollMode = View.OVER_SCROLL_NEVER
}
override fun onPreferenceTreeClick(preference: Preference): Boolean {
when (preference.key) {
"contributors" -> openUrl(R.string.contributors_url)
"update_log" -> showMdFile(getString(R.string.update_log), "updateLog.md")
"check_update" -> checkUpdate()
"mail" -> requireContext().sendMail(getString(R.string.email))
"license" -> showMdFile(getString(R.string.license), "LICENSE.md")
"disclaimer" -> showMdFile(getString(R.string.disclaimer), "disclaimer.md")
"privacyPolicy" -> showMdFile(getString(R.string.privacy_policy), "privacyPolicy.md")
"gzGzh" -> requireContext().sendToClip(getString(R.string.legado_gzh))
"crashLog" -> showDialogFragment<CrashLogsDialog>()
"saveLog" -> saveLog()
"createHeapDump" -> createHeapDump()
}
return super.onPreferenceTreeClick(preference)
}
@Suppress("SameParameterValue")
private fun openUrl(@StringRes addressID: Int) {
requireContext().openUrl(getString(addressID))
}
/**
* 显示md文件
*/
private fun showMdFile(title: String, fileName: String) {
val mdText = String(requireContext().assets.open(fileName).readBytes())
showDialogFragment(TextDialog(title, mdText, TextDialog.Mode.MD))
}
/**
* 检测更新
*/
private fun checkUpdate() {
waitDialog.show()
AppUpdate.gitHubUpdate?.run {
check(lifecycleScope)
.onSuccess {
showDialogFragment(
UpdateDialog(it)
)
}.onError {
appCtx.toastOnUi("${getString(R.string.check_update)}\n${it.localizedMessage}")
}.onFinally {
waitDialog.dismiss()
}
}
}
// /**
// * 加入qq群
// */
// private fun joinQQGroup(key: String): Boolean {
// val intent = Intent()
// intent.data =
// "mqqopensdkapi://bizAgent/qm/qr?url=http%3A%2F%2Fqm.qq.com%2Fcgi-bin%2Fqm%2Fqr%3Ffrom%3Dapp%26p%3Dandroid%26k%3D$key".toUri()
// // 此Flag可根据具体产品需要自定义,如设置,则在加群界面按返回,返回手Q主界面,不设置,按返回会返回到呼起产品界面
// // intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
// kotlin.runCatching {
// startActivity(intent)
// return true
// }.onFailure {
// toastOnUi("添加失败,请手动添加")
// }
// return false
// }
private fun saveLog() {
Coroutine.async {
val backupPath = AppConfig.backupPath ?: let {
appCtx.toastOnUi("未设置备份目录")
return@async
}
if (!AppConfig.recordLog) {
appCtx.toastOnUi("未开启日志记录,请去其他设置里打开记录日志")
delay(3000)
}
val doc = FileDoc.fromUri(backupPath.toUri(), true)
copyLogs(doc)
copyHeapDump(doc)
appCtx.toastOnUi("已保存至备份目录")
}.onError {
AppLog.put("保存日志出错\n${it.localizedMessage}", it, true)
}
}
private fun createHeapDump() {
Coroutine.async {
val backupPath = AppConfig.backupPath ?: let {
appCtx.toastOnUi("未设置备份目录")
return@async
}
if (!AppConfig.recordHeapDump) {
appCtx.toastOnUi("未开启堆转储记录,请去其他设置里打开记录堆转储")
delay(3000)
}
appCtx.toastOnUi("开始创建堆转储")
System.gc()
CrashHandler.doHeapDump(true)
val doc = FileDoc.fromUri(backupPath.toUri(), true)
if (!copyHeapDump(doc)) {
appCtx.toastOnUi("未找到堆转储文件")
} else {
appCtx.toastOnUi("已保存至备份目录")
}
}.onError {
AppLog.put("保存堆转储失败\n${it.localizedMessage}", it)
}
}
private fun copyLogs(doc: FileDoc) {
val cacheDir = appCtx.externalCache
val logFiles = File(cacheDir, "logs")
val crashFiles = File(cacheDir, "crash")
val logcatFile = File(cacheDir, "logcat.txt")
dumpLogcat(logcatFile)
val zipFile = File(cacheDir, "logs.zip")
ZipUtils.zipFiles(arrayListOf(logFiles, crashFiles, logcatFile), zipFile)
doc.find("logs.zip")?.delete()
zipFile.inputStream().use { input ->
doc.createFileIfNotExist("logs.zip").openOutputStream().getOrNull()
?.use {
input.copyTo(it)
}
}
zipFile.delete()
}
private fun copyHeapDump(doc: FileDoc): Boolean {
val heapFile = FileDoc.fromFile(File(appCtx.externalCache, "heapDump")).list()
?.firstOrNull() ?: return false
doc.find("heapDump")?.delete()
val heapDumpDoc = doc.createFolderIfNotExist("heapDump")
heapFile.openInputStream().getOrNull()?.use { input ->
heapDumpDoc.createFileIfNotExist(heapFile.name).openOutputStream().getOrNull()
?.use {
input.copyTo(it)
}
}
return true
}
private fun dumpLogcat(file: File) {
try {
val process = Runtime.getRuntime().exec("logcat -d")
file.outputStream().use {
process.inputStream.copyTo(it)
}
} catch (e: Exception) {
AppLog.put("保存Logcat失败\n$e", e)
}
}
}