mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
Add debug info screens
Nice and freshly converted from compose 😃
This commit is contained in:
parent
0b37080e47
commit
8ae9b09d68
17 changed files with 497 additions and 59 deletions
|
@ -140,9 +140,9 @@ class MangaDetailsAdapter(
|
||||||
fun showFloatingActionMode(view: TextView, content: String? = null, isTag: Boolean = false)
|
fun showFloatingActionMode(view: TextView, content: String? = null, isTag: Boolean = false)
|
||||||
fun showChapterFilter()
|
fun showChapterFilter()
|
||||||
fun favoriteManga(longPress: Boolean)
|
fun favoriteManga(longPress: Boolean)
|
||||||
fun copyToClipboard(content: String, label: Int, useToast: Boolean = false)
|
fun copyContentToClipboard(content: String, label: Int, useToast: Boolean = false)
|
||||||
fun customActionMode(view: TextView): ActionMode.Callback
|
fun customActionMode(view: TextView): ActionMode.Callback
|
||||||
fun copyToClipboard(content: String, label: String?, useToast: Boolean = false)
|
fun copyContentToClipboard(content: String, label: String?, useToast: Boolean = false)
|
||||||
fun zoomImageFromThumb(thumbView: View)
|
fun zoomImageFromThumb(thumbView: View)
|
||||||
fun showTrackingSheet()
|
fun showTrackingSheet()
|
||||||
fun updateScroll()
|
fun updateScroll()
|
||||||
|
|
|
@ -4,7 +4,6 @@ import android.animation.ValueAnimator
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipboardManager
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
@ -109,6 +108,7 @@ import eu.kanade.tachiyomi.util.system.setCustomTitleAndMessage
|
||||||
import eu.kanade.tachiyomi.util.system.timeSpanFromNow
|
import eu.kanade.tachiyomi.util.system.timeSpanFromNow
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import eu.kanade.tachiyomi.util.view.activityBinding
|
import eu.kanade.tachiyomi.util.view.activityBinding
|
||||||
|
import eu.kanade.tachiyomi.util.view.copyToClipboard
|
||||||
import eu.kanade.tachiyomi.util.view.findChild
|
import eu.kanade.tachiyomi.util.view.findChild
|
||||||
import eu.kanade.tachiyomi.util.view.getText
|
import eu.kanade.tachiyomi.util.view.getText
|
||||||
import eu.kanade.tachiyomi.util.view.isControllerVisible
|
import eu.kanade.tachiyomi.util.view.isControllerVisible
|
||||||
|
@ -1639,10 +1639,10 @@ class MangaDetailsController :
|
||||||
* @param content the actual text to copy to the board
|
* @param content the actual text to copy to the board
|
||||||
* @param label Label to show to the user describing the content
|
* @param label Label to show to the user describing the content
|
||||||
*/
|
*/
|
||||||
override fun copyToClipboard(content: String, label: Int, useToast: Boolean) {
|
override fun copyContentToClipboard(content: String, label: Int, useToast: Boolean) {
|
||||||
val view = view ?: return
|
val view = view ?: return
|
||||||
val contentType = if (label != 0) view.context.getString(label) else null
|
val contentType = if (label != 0) view.context.getString(label) else null
|
||||||
copyToClipboard(content, contentType, useToast)
|
copyContentToClipboard(content, contentType, useToast)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1651,22 +1651,8 @@ class MangaDetailsController :
|
||||||
* @param content the actual text to copy to the board
|
* @param content the actual text to copy to the board
|
||||||
* @param label Label to show to the user describing the content
|
* @param label Label to show to the user describing the content
|
||||||
*/
|
*/
|
||||||
override fun copyToClipboard(content: String, label: String?, useToast: Boolean) {
|
override fun copyContentToClipboard(content: String, label: String?, useToast: Boolean) {
|
||||||
if (content.isBlank()) return
|
snack = copyToClipboard(content, label, useToast)
|
||||||
|
|
||||||
val activity = activity ?: return
|
|
||||||
val view = view ?: return
|
|
||||||
|
|
||||||
val clipboard = activity.getSystemService(ClipboardManager::class.java)
|
|
||||||
clipboard.setPrimaryClip(ClipData.newPlainText(label, content))
|
|
||||||
|
|
||||||
label ?: return
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) return
|
|
||||||
if (useToast) {
|
|
||||||
activity.toast(view.context.getString(R.string._copied_to_clipboard, label))
|
|
||||||
} else {
|
|
||||||
snack = view.snack(view.context.getString(R.string._copied_to_clipboard, label))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showTrackingSheet() {
|
override fun showTrackingSheet() {
|
||||||
|
@ -1897,7 +1883,7 @@ class MangaDetailsController :
|
||||||
item: MenuItem?,
|
item: MenuItem?,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
when (item?.itemId) {
|
when (item?.itemId) {
|
||||||
R.id.action_copy -> copyToClipboard(text, null)
|
R.id.action_copy -> copyContentToClipboard(text, null)
|
||||||
R.id.action_source_search -> sourceSearch(text)
|
R.id.action_source_search -> sourceSearch(text)
|
||||||
R.id.action_global_search, R.id.action_local_search -> {
|
R.id.action_global_search, R.id.action_local_search -> {
|
||||||
if (authorText != null) {
|
if (authorText != null) {
|
||||||
|
|
|
@ -139,7 +139,7 @@ class MangaHeaderHolder(
|
||||||
}
|
}
|
||||||
title.setOnLongClickListener {
|
title.setOnLongClickListener {
|
||||||
title.text?.toString()?.toNormalized()?.let {
|
title.text?.toString()?.toNormalized()?.let {
|
||||||
adapter.delegate.copyToClipboard(it, R.string.title)
|
adapter.delegate.copyContentToClipboard(it, R.string.title)
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,7 @@ class MangaHeaderHolder(
|
||||||
}
|
}
|
||||||
mangaAuthor.setOnLongClickListener {
|
mangaAuthor.setOnLongClickListener {
|
||||||
mangaAuthor.text?.toString()?.let {
|
mangaAuthor.text?.toString()?.let {
|
||||||
adapter.delegate.copyToClipboard(it, R.string.author)
|
adapter.delegate.copyContentToClipboard(it, R.string.author)
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -523,7 +523,7 @@ class MangaHeaderHolder(
|
||||||
adapter.delegate.showFloatingActionMode(chip, isTag = true)
|
adapter.delegate.showFloatingActionMode(chip, isTag = true)
|
||||||
}
|
}
|
||||||
chip.setOnLongClickListener {
|
chip.setOnLongClickListener {
|
||||||
adapter.delegate.copyToClipboard(genreText, genreText)
|
adapter.delegate.copyContentToClipboard(genreText, genreText)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
this.addView(chip)
|
this.addView(chip)
|
||||||
|
|
|
@ -241,7 +241,7 @@ class TrackingBottomSheet(private val controller: MangaDetailsController) :
|
||||||
|
|
||||||
override fun onTitleLongClick(position: Int) {
|
override fun onTitleLongClick(position: Int) {
|
||||||
val title = adapter?.getItem(position)?.track?.title ?: return
|
val title = adapter?.getItem(position)?.track?.title ?: return
|
||||||
controller.copyToClipboard(title, R.string.title, true)
|
controller.copyContentToClipboard(title, R.string.title, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startTransition(duration: Long = 100) {
|
private fun startTransition(duration: Long = 100) {
|
||||||
|
|
|
@ -114,7 +114,7 @@ class AboutController : SettingsController() {
|
||||||
preference {
|
preference {
|
||||||
key = "pref_build_time"
|
key = "pref_build_time"
|
||||||
titleRes = R.string.build_time
|
titleRes = R.string.build_time
|
||||||
summary = getFormattedBuildTime()
|
summary = getFormattedBuildTime(dateFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
preferenceCategory {
|
preferenceCategory {
|
||||||
|
@ -234,11 +234,13 @@ class AboutController : SettingsController() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFormattedBuildTime(): String {
|
companion object {
|
||||||
|
fun getFormattedBuildTime(dateFormat: DateFormat): String {
|
||||||
try {
|
try {
|
||||||
val inputDf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.getDefault())
|
val inputDf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.getDefault())
|
||||||
inputDf.timeZone = TimeZone.getTimeZone("UTC")
|
inputDf.timeZone = TimeZone.getTimeZone("UTC")
|
||||||
val buildTime = inputDf.parse(BuildConfig.BUILD_TIME) ?: return BuildConfig.BUILD_TIME
|
val buildTime =
|
||||||
|
inputDf.parse(BuildConfig.BUILD_TIME) ?: return BuildConfig.BUILD_TIME
|
||||||
|
|
||||||
return buildTime.toTimestampString(dateFormat)
|
return buildTime.toTimestampString(dateFormat)
|
||||||
} catch (e: ParseException) {
|
} catch (e: ParseException) {
|
||||||
|
@ -246,3 +248,4 @@ class AboutController : SettingsController() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ import eu.kanade.tachiyomi.network.PREF_DOH_QUAD101
|
||||||
import eu.kanade.tachiyomi.network.PREF_DOH_QUAD9
|
import eu.kanade.tachiyomi.network.PREF_DOH_QUAD9
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.ui.setting.database.ClearDatabaseController
|
import eu.kanade.tachiyomi.ui.setting.database.ClearDatabaseController
|
||||||
|
import eu.kanade.tachiyomi.ui.setting.debug.DebugController
|
||||||
import eu.kanade.tachiyomi.util.CrashLogUtil
|
import eu.kanade.tachiyomi.util.CrashLogUtil
|
||||||
import eu.kanade.tachiyomi.util.system.disableItems
|
import eu.kanade.tachiyomi.util.system.disableItems
|
||||||
import eu.kanade.tachiyomi.util.system.isPackageInstalled
|
import eu.kanade.tachiyomi.util.system.isPackageInstalled
|
||||||
|
@ -107,6 +108,17 @@ class SettingsAdvancedController : SettingsController() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
preference {
|
||||||
|
key = "debug_info"
|
||||||
|
titleRes = R.string.pref_debug_info
|
||||||
|
|
||||||
|
onClick {
|
||||||
|
router.pushController(DebugController().withFadeTransaction())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
preferenceCategory {
|
||||||
|
titleRes = R.string.label_background_activity
|
||||||
val pm = context.getSystemService(Context.POWER_SERVICE) as? PowerManager?
|
val pm = context.getSystemService(Context.POWER_SERVICE) as? PowerManager?
|
||||||
if (pm != null) {
|
if (pm != null) {
|
||||||
preference {
|
preference {
|
||||||
|
@ -138,6 +150,7 @@ class SettingsAdvancedController : SettingsController() {
|
||||||
openInBrowser("https://dontkillmyapp.com/")
|
openInBrowser("https://dontkillmyapp.com/")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isUpdaterEnabled) {
|
if (isUpdaterEnabled) {
|
||||||
switchPreference {
|
switchPreference {
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package eu.kanade.tachiyomi.ui.setting.debug
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuInflater
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.mikepenz.fastadapter.FastAdapter
|
||||||
|
import com.mikepenz.fastadapter.adapters.ItemAdapter
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.data.backup.models.Backup
|
||||||
|
import eu.kanade.tachiyomi.databinding.SubDebugControllerBinding
|
||||||
|
import eu.kanade.tachiyomi.ui.base.controller.BaseController
|
||||||
|
import eu.kanade.tachiyomi.util.view.copyToClipboard
|
||||||
|
import eu.kanade.tachiyomi.util.view.scrollViewWith
|
||||||
|
import kotlinx.serialization.protobuf.schema.ProtoBufSchemaGenerator
|
||||||
|
|
||||||
|
class BackupSchemaController : BaseController<SubDebugControllerBinding>() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val title = "Backup file schema"
|
||||||
|
}
|
||||||
|
|
||||||
|
private val itemAdapter = ItemAdapter<DebugInfoItem>()
|
||||||
|
private val fastAdapter = FastAdapter.with(itemAdapter)
|
||||||
|
private val schema = ProtoBufSchemaGenerator.generateSchemaText(Backup.serializer().descriptor)
|
||||||
|
|
||||||
|
override fun getTitle() = title
|
||||||
|
override fun createBinding(inflater: LayoutInflater) =
|
||||||
|
SubDebugControllerBinding.inflate(inflater)
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View) {
|
||||||
|
super.onViewCreated(view)
|
||||||
|
scrollViewWith(binding.recycler, padBottom = true)
|
||||||
|
fastAdapter.setHasStableIds(true)
|
||||||
|
binding.recycler.layoutManager = LinearLayoutManager(view.context)
|
||||||
|
binding.recycler.adapter = fastAdapter
|
||||||
|
itemAdapter.add(DebugInfoItem(schema, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
inflater.inflate(R.menu.sub_debug_info, menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
when (item.itemId) {
|
||||||
|
R.id.action_copy -> copyToClipboard(schema, "Backup file schema", true)
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
package eu.kanade.tachiyomi.ui.setting.debug
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.preference.PreferenceScreen
|
||||||
|
import androidx.webkit.WebViewCompat
|
||||||
|
import eu.kanade.tachiyomi.BuildConfig
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.ui.more.AboutController
|
||||||
|
import eu.kanade.tachiyomi.ui.setting.SettingsController
|
||||||
|
import eu.kanade.tachiyomi.ui.setting.onClick
|
||||||
|
import eu.kanade.tachiyomi.ui.setting.preference
|
||||||
|
import eu.kanade.tachiyomi.ui.setting.preferenceCategory
|
||||||
|
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||||
|
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
||||||
|
import java.text.DateFormat
|
||||||
|
|
||||||
|
class DebugController : SettingsController() {
|
||||||
|
|
||||||
|
override fun getTitle() = resources?.getString(R.string.pref_debug_info)
|
||||||
|
|
||||||
|
private val dateFormat: DateFormat by lazy {
|
||||||
|
preferences.dateFormat()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
||||||
|
preference {
|
||||||
|
title = WorkerInfoController.title
|
||||||
|
onClick {
|
||||||
|
router.pushController(WorkerInfoController().withFadeTransaction())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
preference {
|
||||||
|
title = BackupSchemaController.title
|
||||||
|
onClick {
|
||||||
|
router.pushController(BackupSchemaController().withFadeTransaction())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
preferenceCategory {
|
||||||
|
title = "App Info"
|
||||||
|
preference {
|
||||||
|
key = "pref_version"
|
||||||
|
title = "Version"
|
||||||
|
summary = if (BuildConfig.DEBUG) {
|
||||||
|
"r" + BuildConfig.COMMIT_COUNT
|
||||||
|
} else {
|
||||||
|
BuildConfig.VERSION_NAME
|
||||||
|
}
|
||||||
|
}
|
||||||
|
preference {
|
||||||
|
key = "pref_build_time"
|
||||||
|
title = "Build Time"
|
||||||
|
summary = AboutController.getFormattedBuildTime(dateFormat)
|
||||||
|
}
|
||||||
|
preference {
|
||||||
|
key = "pref_webview_version"
|
||||||
|
title = "WebView version"
|
||||||
|
summary = getWebViewVersion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
preferenceCategory {
|
||||||
|
title = "Device info"
|
||||||
|
preference {
|
||||||
|
title = "Model"
|
||||||
|
summary = "${Build.MANUFACTURER} ${Build.MODEL} (${Build.DEVICE})"
|
||||||
|
}
|
||||||
|
if (DeviceUtil.oneUiVersion != null) {
|
||||||
|
preference {
|
||||||
|
title = "OneUI version"
|
||||||
|
summary = "${DeviceUtil.oneUiVersion}"
|
||||||
|
}
|
||||||
|
} else if (DeviceUtil.miuiMajorVersion != null) {
|
||||||
|
preference {
|
||||||
|
title = "MIUI version"
|
||||||
|
summary = "${DeviceUtil.miuiMajorVersion}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val androidVersion = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
Build.VERSION.RELEASE_OR_PREVIEW_DISPLAY
|
||||||
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
Build.VERSION.RELEASE_OR_CODENAME
|
||||||
|
} else {
|
||||||
|
Build.VERSION.RELEASE
|
||||||
|
}
|
||||||
|
preference {
|
||||||
|
title = "Android version"
|
||||||
|
summary = "$androidVersion (${Build.DISPLAY})"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getWebViewVersion(): String {
|
||||||
|
val activity = activity ?: return "Unknown"
|
||||||
|
val webView =
|
||||||
|
WebViewCompat.getCurrentWebViewPackage(activity) ?: return "how did you get here?"
|
||||||
|
val label = webView.applicationInfo.loadLabel(activity.packageManager)
|
||||||
|
val version = webView.versionName
|
||||||
|
return "$label $version"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package eu.kanade.tachiyomi.ui.setting.debug
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import com.mikepenz.fastadapter.FastAdapter
|
||||||
|
import com.mikepenz.fastadapter.items.AbstractItem
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.databinding.DebugInfoItemBinding
|
||||||
|
|
||||||
|
class DebugInfoItem(val text: String, val header: Boolean) : AbstractItem<FastAdapter.ViewHolder<DebugInfoItem>>() {
|
||||||
|
|
||||||
|
/** defines the type defining this item. must be unique. preferably an id */
|
||||||
|
override val type: Int = R.id.debug_title
|
||||||
|
|
||||||
|
/** defines the layout which will be used for this item in the list */
|
||||||
|
override val layoutRes: Int = R.layout.debug_info_item
|
||||||
|
|
||||||
|
override var identifier = text.hashCode().toLong()
|
||||||
|
|
||||||
|
override fun getViewHolder(v: View): FastAdapter.ViewHolder<DebugInfoItem> {
|
||||||
|
return ViewHolder(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewHolder(view: View) : FastAdapter.ViewHolder<DebugInfoItem>(view) {
|
||||||
|
|
||||||
|
val binding = DebugInfoItemBinding.bind(view)
|
||||||
|
|
||||||
|
override fun bindView(item: DebugInfoItem, payloads: List<Any>) {
|
||||||
|
binding.debugTitle.isVisible = item.header
|
||||||
|
binding.debugSummary.isVisible = !item.header
|
||||||
|
if (item.header) {
|
||||||
|
binding.debugTitle.text = item.text
|
||||||
|
} else {
|
||||||
|
binding.debugSummary.text = item.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unbindView(item: DebugInfoItem) {
|
||||||
|
binding.debugTitle.text = ""
|
||||||
|
binding.debugSummary.text = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package eu.kanade.tachiyomi.ui.setting.debug
|
||||||
|
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuInflater
|
||||||
|
import android.view.MenuItem
|
||||||
|
import android.view.View
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.mikepenz.fastadapter.FastAdapter
|
||||||
|
import com.mikepenz.fastadapter.adapters.ItemAdapter
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.databinding.SubDebugControllerBinding
|
||||||
|
import eu.kanade.tachiyomi.ui.base.controller.BaseCoroutineController
|
||||||
|
import eu.kanade.tachiyomi.util.system.launchUI
|
||||||
|
import eu.kanade.tachiyomi.util.view.copyToClipboard
|
||||||
|
import eu.kanade.tachiyomi.util.view.scrollViewWith
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.flow.merge
|
||||||
|
|
||||||
|
class WorkerInfoController : BaseCoroutineController<SubDebugControllerBinding, WorkerInfoPresenter>() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val title = "Worker info"
|
||||||
|
}
|
||||||
|
|
||||||
|
override var presenter = WorkerInfoPresenter()
|
||||||
|
|
||||||
|
private val itemAdapter = ItemAdapter<DebugInfoItem>()
|
||||||
|
private val fastAdapter = FastAdapter.with(itemAdapter)
|
||||||
|
|
||||||
|
override fun getTitle() = title
|
||||||
|
override fun createBinding(inflater: LayoutInflater) =
|
||||||
|
SubDebugControllerBinding.inflate(inflater)
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View) {
|
||||||
|
super.onViewCreated(view)
|
||||||
|
scrollViewWith(binding.recycler, padBottom = true)
|
||||||
|
|
||||||
|
fastAdapter.setHasStableIds(true)
|
||||||
|
binding.recycler.layoutManager = LinearLayoutManager(view.context)
|
||||||
|
binding.recycler.adapter = fastAdapter
|
||||||
|
binding.recycler.itemAnimator = null
|
||||||
|
viewScope.launchUI {
|
||||||
|
merge(presenter.enqueued, presenter.finished, presenter.running).collectLatest {
|
||||||
|
itemAdapter.clear()
|
||||||
|
itemAdapter.add(DebugInfoItem("Enqueued", true))
|
||||||
|
itemAdapter.add(DebugInfoItem(presenter.enqueued.value, false))
|
||||||
|
itemAdapter.add(DebugInfoItem("Finished", true))
|
||||||
|
itemAdapter.add(DebugInfoItem(presenter.finished.value, false))
|
||||||
|
itemAdapter.add(DebugInfoItem("Running", true))
|
||||||
|
itemAdapter.add(DebugInfoItem(presenter.running.value, false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
inflater.inflate(R.menu.sub_debug_info, menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
when (item.itemId) {
|
||||||
|
R.id.action_copy -> copyToClipboard(
|
||||||
|
"${presenter.enqueued.value}\n${presenter.finished.value}\n${presenter.running.value}",
|
||||||
|
"Backup file schema",
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package eu.kanade.tachiyomi.ui.setting.debug
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.asFlow
|
||||||
|
import androidx.work.WorkInfo
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import androidx.work.WorkQuery
|
||||||
|
import eu.kanade.tachiyomi.ui.base.presenter.BaseCoroutinePresenter
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
|
class WorkerInfoPresenter : BaseCoroutinePresenter<WorkerInfoController>() {
|
||||||
|
private val workManager by lazy { WorkManager.getInstance(Injekt.get<Application>()) }
|
||||||
|
|
||||||
|
val finished by lazy {
|
||||||
|
workManager
|
||||||
|
.getWorkInfosLiveData(
|
||||||
|
WorkQuery.fromStates(
|
||||||
|
WorkInfo.State.SUCCEEDED,
|
||||||
|
WorkInfo.State.FAILED,
|
||||||
|
WorkInfo.State.CANCELLED,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.asFlow()
|
||||||
|
.map(::constructString)
|
||||||
|
.stateIn(presenterScope, SharingStarted.WhileSubscribed(), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
val running by lazy {
|
||||||
|
workManager
|
||||||
|
.getWorkInfosLiveData(WorkQuery.fromStates(WorkInfo.State.RUNNING))
|
||||||
|
.asFlow()
|
||||||
|
.map(::constructString)
|
||||||
|
.stateIn(presenterScope, SharingStarted.WhileSubscribed(), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
val enqueued by lazy {
|
||||||
|
workManager
|
||||||
|
.getWorkInfosLiveData(WorkQuery.fromStates(WorkInfo.State.ENQUEUED))
|
||||||
|
.asFlow()
|
||||||
|
.map(::constructString)
|
||||||
|
.stateIn(presenterScope, SharingStarted.WhileSubscribed(), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun constructString(list: List<WorkInfo>) = buildString {
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
appendLine("-")
|
||||||
|
} else {
|
||||||
|
val newList = list.toList()
|
||||||
|
newList.forEach { workInfo ->
|
||||||
|
appendLine("Id: ${workInfo.id}")
|
||||||
|
appendLine("Tags:")
|
||||||
|
workInfo.tags.forEach {
|
||||||
|
appendLine(" - $it")
|
||||||
|
}
|
||||||
|
appendLine("State: ${workInfo.state}")
|
||||||
|
appendLine()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,20 @@ object DeviceUtil {
|
||||||
getSystemProperty("ro.miui.ui.version.name")?.isNotEmpty() ?: false
|
getSystemProperty("ro.miui.ui.version.name")?.isNotEmpty() ?: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the MIUI major version code from a string like "V12.5.3.0.QFGMIXM".
|
||||||
|
*
|
||||||
|
* @return MIUI major version code (e.g., 13) or null if can't be parsed.
|
||||||
|
*/
|
||||||
|
val miuiMajorVersion by lazy {
|
||||||
|
if (!isMiui) return@lazy null
|
||||||
|
|
||||||
|
Build.VERSION.INCREMENTAL
|
||||||
|
.substringBefore('.')
|
||||||
|
.trimStart('V')
|
||||||
|
.toIntOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("PrivateApi")
|
@SuppressLint("PrivateApi")
|
||||||
fun isMiuiOptimizationDisabled(): Boolean {
|
fun isMiuiOptimizationDisabled(): Boolean {
|
||||||
val sysProp = getSystemProperty("persist.sys.miui_optimization")
|
val sysProp = getSystemProperty("persist.sys.miui_optimization")
|
||||||
|
@ -30,6 +44,20 @@ object DeviceUtil {
|
||||||
Build.MANUFACTURER.equals("samsung", ignoreCase = true)
|
Build.MANUFACTURER.equals("samsung", ignoreCase = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val oneUiVersion by lazy {
|
||||||
|
try {
|
||||||
|
val semPlatformIntField = Build.VERSION::class.java.getDeclaredField("SEM_PLATFORM_INT")
|
||||||
|
val version = semPlatformIntField.getInt(null) - 90000
|
||||||
|
if (version < 0) {
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
((version / 10000).toString() + "." + version % 10000 / 100).toDouble()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val invalidDefaultBrowsers = listOf(
|
val invalidDefaultBrowsers = listOf(
|
||||||
"android",
|
"android",
|
||||||
"com.huawei.android.internal.app",
|
"com.huawei.android.internal.app",
|
||||||
|
|
|
@ -4,6 +4,8 @@ import android.Manifest
|
||||||
import android.animation.Animator
|
import android.animation.Animator
|
||||||
import android.animation.ValueAnimator
|
import android.animation.ValueAnimator
|
||||||
import android.app.ActivityManager
|
import android.app.ActivityManager
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
@ -50,6 +52,7 @@ import com.bluelinelabs.conductor.ControllerChangeType
|
||||||
import com.bluelinelabs.conductor.Router
|
import com.bluelinelabs.conductor.Router
|
||||||
import com.bluelinelabs.conductor.RouterTransaction
|
import com.bluelinelabs.conductor.RouterTransaction
|
||||||
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler
|
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
|
import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
@ -824,6 +827,25 @@ fun Controller.openInBrowser(url: String) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Controller.copyToClipboard(content: String, label: String?, useToast: Boolean): Snackbar? {
|
||||||
|
if (content.isBlank()) return null
|
||||||
|
|
||||||
|
val activity = activity ?: return null
|
||||||
|
val view = view ?: return null
|
||||||
|
|
||||||
|
val clipboard = activity.getSystemService(ClipboardManager::class.java)
|
||||||
|
clipboard.setPrimaryClip(ClipData.newPlainText(label, content))
|
||||||
|
|
||||||
|
label ?: return null
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) return null
|
||||||
|
return if (useToast) {
|
||||||
|
activity.toast(view.context.getString(R.string._copied_to_clipboard, label))
|
||||||
|
null
|
||||||
|
} else {
|
||||||
|
view.snack(view.context.getString(R.string._copied_to_clipboard, label))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val Controller.activityBinding: MainActivityBinding?
|
val Controller.activityBinding: MainActivityBinding?
|
||||||
get() = (activity as? MainActivity)?.binding
|
get() = (activity as? MainActivity)?.binding
|
||||||
|
|
||||||
|
|
34
app/src/main/res/layout/debug_info_item.xml
Normal file
34
app/src/main/res/layout/debug_info_item.xml
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/debug_title"
|
||||||
|
style="?textAppearanceTitleMedium"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/card_view_group"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
tools:visibility="invisible"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:text="Enqueued" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/debug_summary"
|
||||||
|
android:typeface="monospace"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/card_view_group"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:text="Enqueued" />
|
||||||
|
</FrameLayout>
|
12
app/src/main/res/layout/sub_debug_controller.xml
Normal file
12
app/src/main/res/layout/sub_debug_controller.xml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/frame_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recycler"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false"/>
|
||||||
|
</FrameLayout>
|
9
app/src/main/res/menu/sub_debug_info.xml
Normal file
9
app/src/main/res/menu/sub_debug_info.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_copy"
|
||||||
|
android:icon="@drawable/ic_content_copy_24dp"
|
||||||
|
android:title="@string/copy_value"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
</menu>
|
|
@ -877,6 +877,8 @@
|
||||||
not in your library \nCurrently using: %1$s</string>
|
not in your library \nCurrently using: %1$s</string>
|
||||||
<string name="most_entries">Most entries</string>
|
<string name="most_entries">Most entries</string>
|
||||||
<string name="select_uninstalled_sources">Select uninstalled sources</string>
|
<string name="select_uninstalled_sources">Select uninstalled sources</string>
|
||||||
|
<string name="pref_debug_info">Debug info</string>
|
||||||
|
<string name="label_background_activity">Background activity</string>
|
||||||
|
|
||||||
<!-- Browse Settings -->
|
<!-- Browse Settings -->
|
||||||
<string name="pref_global_search">Global search</string>
|
<string name="pref_global_search">Global search</string>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue