Debug info
This commit is contained in:
@@ -1,3 +1,8 @@
|
|||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.TimeZone
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
id("org.jetbrains.kotlin.android")
|
id("org.jetbrains.kotlin.android")
|
||||||
@@ -19,6 +24,10 @@ android {
|
|||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = "1.0"
|
versionName = "1.0"
|
||||||
|
|
||||||
|
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||||
|
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
|
||||||
|
buildConfigField("String", "BUILD_TIME", "\"${getBuildTime()}\"")
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
useSupportLibrary = true
|
useSupportLibrary = true
|
||||||
@@ -79,6 +88,7 @@ dependencies {
|
|||||||
implementation("androidx.compose.material3:material3-window-size-class:1.2.0-rc01")
|
implementation("androidx.compose.material3:material3-window-size-class:1.2.0-rc01")
|
||||||
implementation("androidx.hilt:hilt-navigation-compose:1.1.0")
|
implementation("androidx.hilt:hilt-navigation-compose:1.1.0")
|
||||||
implementation("androidx.navigation:navigation-compose:2.7.6")
|
implementation("androidx.navigation:navigation-compose:2.7.6")
|
||||||
|
implementation("androidx.profileinstaller:profileinstaller:1.3.1")
|
||||||
implementation("androidx.room:room-runtime:2.6.1")
|
implementation("androidx.room:room-runtime:2.6.1")
|
||||||
implementation("androidx.room:room-ktx:2.6.1")
|
implementation("androidx.room:room-ktx:2.6.1")
|
||||||
implementation("androidx.work:work-runtime-ktx:2.9.0")
|
implementation("androidx.work:work-runtime-ktx:2.9.0")
|
||||||
@@ -97,6 +107,7 @@ dependencies {
|
|||||||
implementation("com.squareup.okio:okio:3.7.0")
|
implementation("com.squareup.okio:okio:3.7.0")
|
||||||
implementation("com.tencent:mmkv:1.3.2")
|
implementation("com.tencent:mmkv:1.3.2")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7")
|
implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.7")
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.7.3")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
|
||||||
implementation("io.coil-kt:coil-compose:2.5.0")
|
implementation("io.coil-kt:coil-compose:2.5.0")
|
||||||
testImplementation("junit:junit:4.13.2")
|
testImplementation("junit:junit:4.13.2")
|
||||||
@@ -106,4 +117,31 @@ dependencies {
|
|||||||
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
|
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
|
||||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||||
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Git is needed in your system PATH for these commands to work.
|
||||||
|
// If it's not installed, you can return a random value as a workaround
|
||||||
|
fun Project.getCommitCount(): String {
|
||||||
|
return runCommand("git rev-list --count HEAD")
|
||||||
|
// return "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Project.getGitSha(): String {
|
||||||
|
return runCommand("git rev-parse --short HEAD")
|
||||||
|
// return "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Project.getBuildTime(): String {
|
||||||
|
val df = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'")
|
||||||
|
df.timeZone = TimeZone.getTimeZone("UTC")
|
||||||
|
return df.format(Date())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Project.runCommand(command: String): String {
|
||||||
|
val byteOut = ByteArrayOutputStream()
|
||||||
|
project.exec {
|
||||||
|
commandLine = command.split(" ")
|
||||||
|
standardOutput = byteOut
|
||||||
|
}
|
||||||
|
return String(byteOut.toByteArray()).trim()
|
||||||
}
|
}
|
||||||
@@ -140,7 +140,8 @@ fun Navigation(
|
|||||||
SettingsView(
|
SettingsView(
|
||||||
navigateBack = navigateBack,
|
navigateBack = navigateBack,
|
||||||
navigateToAppearance = { navController.navigate(APPEARANCE_DESTINATION) },
|
navigateToAppearance = { navController.navigate(APPEARANCE_DESTINATION) },
|
||||||
navigateToAbout = { navController.navigate(ABOUT_DESTINATION) }
|
navigateToAbout = { navController.navigate(ABOUT_DESTINATION) },
|
||||||
|
navigateToAdvanced = { navController.navigate(ADVANCED_DESTINATION) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,6 +166,12 @@ fun Navigation(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
composable(ADVANCED_DESTINATION) {
|
||||||
|
AdvancedView(
|
||||||
|
navigateBack = navigateBack,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
composable(
|
composable(
|
||||||
route = LIST_DESTINATION,
|
route = LIST_DESTINATION,
|
||||||
arguments = listOf(
|
arguments = listOf(
|
||||||
|
|||||||
@@ -121,8 +121,13 @@ fun PreferenceItem(
|
|||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.padding(horizontal = 16.dp)
|
.then(
|
||||||
.padding(end = 8.dp)
|
if (icon != null)
|
||||||
|
Modifier
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
.padding(end = 8.dp)
|
||||||
|
else Modifier.padding(horizontal = 8.dp)
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
PreferenceItemTitle(text = title, enabled = enabled)
|
PreferenceItemTitle(text = title, enabled = enabled)
|
||||||
if (!description.isNullOrEmpty()) PreferenceItemDescription(
|
if (!description.isNullOrEmpty()) PreferenceItemDescription(
|
||||||
@@ -465,7 +470,8 @@ fun PreferenceSwitch(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
.padding(horizontal = 16.dp)
|
.padding(horizontal = 16.dp)
|
||||||
) {
|
) {
|
||||||
PreferenceItemTitle(
|
PreferenceItemTitle(
|
||||||
@@ -542,7 +548,7 @@ fun PreferencesHintCard(
|
|||||||
@Preview
|
@Preview
|
||||||
fun PreferenceItemPreview() {
|
fun PreferenceItemPreview() {
|
||||||
Column {
|
Column {
|
||||||
PreferenceItem(title = "title", description = "description", icon = 0)
|
PreferenceItem(title = "title", description = "description")
|
||||||
PreferenceItem(title = "title", description = "description", icon = Icons.Outlined.Update)
|
PreferenceItem(title = "title", description = "description", icon = Icons.Outlined.Update)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.xtimms.tokusho.sections.settings
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Code
|
||||||
import androidx.compose.material.icons.outlined.Info
|
import androidx.compose.material.icons.outlined.Info
|
||||||
import androidx.compose.material.icons.outlined.Palette
|
import androidx.compose.material.icons.outlined.Palette
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -19,6 +20,7 @@ fun SettingsView(
|
|||||||
navigateBack: () -> Unit,
|
navigateBack: () -> Unit,
|
||||||
navigateToAppearance: () -> Unit,
|
navigateToAppearance: () -> Unit,
|
||||||
navigateToAbout: () -> Unit,
|
navigateToAbout: () -> Unit,
|
||||||
|
navigateToAdvanced: () -> Unit
|
||||||
) {
|
) {
|
||||||
ScaffoldWithTopAppBar(
|
ScaffoldWithTopAppBar(
|
||||||
title = stringResource(R.string.settings),
|
title = stringResource(R.string.settings),
|
||||||
@@ -36,6 +38,14 @@ fun SettingsView(
|
|||||||
onClick = navigateToAppearance
|
onClick = navigateToAppearance
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
item {
|
||||||
|
SettingItem(
|
||||||
|
title = stringResource(id = R.string.advanced),
|
||||||
|
description = stringResource(id = R.string.advanced_page),
|
||||||
|
icon = Icons.Outlined.Code,
|
||||||
|
onClick = navigateToAdvanced
|
||||||
|
)
|
||||||
|
}
|
||||||
item {
|
item {
|
||||||
SettingItem(
|
SettingItem(
|
||||||
title = stringResource(id = R.string.about),
|
title = stringResource(id = R.string.about),
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
package org.xtimms.tokusho.sections.settings.advanced
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.ReadOnlyComposable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.produceState
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.profileinstaller.ProfileVerifier
|
||||||
|
import kotlinx.coroutines.guava.await
|
||||||
|
import org.xtimms.tokusho.BuildConfig
|
||||||
|
import org.xtimms.tokusho.R
|
||||||
|
import org.xtimms.tokusho.core.components.PreferenceItem
|
||||||
|
import org.xtimms.tokusho.core.components.PreferenceSubtitle
|
||||||
|
import org.xtimms.tokusho.core.components.ScaffoldWithTopAppBar
|
||||||
|
import org.xtimms.tokusho.utils.WebViewUtil
|
||||||
|
import org.xtimms.tokusho.utils.lang.toDateTimestampString
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
|
import java.util.TimeZone
|
||||||
|
|
||||||
|
const val ADVANCED_DESTINATION = "advanced"
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AdvancedView(
|
||||||
|
navigateBack: () -> Unit,
|
||||||
|
) {
|
||||||
|
|
||||||
|
ScaffoldWithTopAppBar(
|
||||||
|
title = stringResource(R.string.advanced),
|
||||||
|
navigateBack = navigateBack
|
||||||
|
) { padding ->
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(padding)
|
||||||
|
) {
|
||||||
|
item {
|
||||||
|
PreferenceSubtitle(text = stringResource(id = R.string.app_info))
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
PreferenceItem(
|
||||||
|
title = stringResource(id = R.string.app_version),
|
||||||
|
description = getVersionName(false)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
PreferenceItem(
|
||||||
|
title = stringResource(id = R.string.build_time),
|
||||||
|
description = getFormattedBuildTime()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
getProfileVerifierPreference()
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
PreferenceItem(
|
||||||
|
title = stringResource(id = R.string.webview_version),
|
||||||
|
description = getWebViewVersion()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@ReadOnlyComposable
|
||||||
|
private fun getWebViewVersion(): String {
|
||||||
|
return WebViewUtil.getVersion(LocalContext.current)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun getProfileVerifierPreference() {
|
||||||
|
val status by produceState(initialValue = "-") {
|
||||||
|
val result = ProfileVerifier.getCompilationStatusAsync().await().profileInstallResultCode
|
||||||
|
value = when (result) {
|
||||||
|
ProfileVerifier.CompilationStatus.RESULT_CODE_NO_PROFILE -> "No profile installed"
|
||||||
|
ProfileVerifier.CompilationStatus.RESULT_CODE_COMPILED_WITH_PROFILE -> "Compiled"
|
||||||
|
ProfileVerifier.CompilationStatus.RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING ->
|
||||||
|
"Compiled non-matching"
|
||||||
|
|
||||||
|
ProfileVerifier.CompilationStatus.RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ,
|
||||||
|
ProfileVerifier.CompilationStatus.RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE,
|
||||||
|
ProfileVerifier.CompilationStatus.RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST,
|
||||||
|
-> "Error $result"
|
||||||
|
|
||||||
|
ProfileVerifier.CompilationStatus.RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION -> "Not supported"
|
||||||
|
ProfileVerifier.CompilationStatus.RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION -> "Pending compilation"
|
||||||
|
else -> "Unknown code $result"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return PreferenceItem(
|
||||||
|
title = "Profile compilation status",
|
||||||
|
description = status,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getVersionName(withBuildDate: Boolean): String {
|
||||||
|
return when {
|
||||||
|
BuildConfig.DEBUG -> {
|
||||||
|
"Debug ${BuildConfig.COMMIT_SHA}".let {
|
||||||
|
if (withBuildDate) {
|
||||||
|
"$it (${getFormattedBuildTime()})"
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
"Stable ${BuildConfig.VERSION_NAME}".let {
|
||||||
|
if (withBuildDate) {
|
||||||
|
"$it (${getFormattedBuildTime()})"
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun getFormattedBuildTime(): String {
|
||||||
|
return try {
|
||||||
|
val inputDf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'", Locale.US)
|
||||||
|
inputDf.timeZone = TimeZone.getTimeZone("UTC")
|
||||||
|
val buildTime = inputDf.parse(BuildConfig.BUILD_TIME)
|
||||||
|
|
||||||
|
val outputDf = DateFormat.getDateTimeInstance(
|
||||||
|
DateFormat.MEDIUM,
|
||||||
|
DateFormat.SHORT,
|
||||||
|
Locale.getDefault(),
|
||||||
|
)
|
||||||
|
outputDf.timeZone = TimeZone.getDefault()
|
||||||
|
|
||||||
|
buildTime!!.toDateTimestampString(DateFormat.getDateTimeInstance())
|
||||||
|
} catch (e: Exception) {
|
||||||
|
BuildConfig.BUILD_TIME
|
||||||
|
}
|
||||||
|
}
|
||||||
16
app/src/main/java/org/xtimms/tokusho/utils/WebViewUtil.kt
Normal file
16
app/src/main/java/org/xtimms/tokusho/utils/WebViewUtil.kt
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package org.xtimms.tokusho.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.webkit.WebView
|
||||||
|
|
||||||
|
object WebViewUtil {
|
||||||
|
|
||||||
|
fun getVersion(context: Context): String {
|
||||||
|
val webView = WebView.getCurrentWebViewPackage() ?: return "o_O"
|
||||||
|
val pm = context.packageManager
|
||||||
|
val label = webView.applicationInfo.loadLabel(pm)
|
||||||
|
val version = webView.versionName
|
||||||
|
return "$label $version"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
10
app/src/main/java/org/xtimms/tokusho/utils/lang/Date.kt
Normal file
10
app/src/main/java/org/xtimms/tokusho/utils/lang/Date.kt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package org.xtimms.tokusho.utils.lang
|
||||||
|
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
fun Date.toDateTimestampString(dateFormatter: DateFormat): String {
|
||||||
|
val date = dateFormatter.format(this)
|
||||||
|
val time = DateFormat.getTimeInstance(DateFormat.SHORT).format(this)
|
||||||
|
return "$date $time"
|
||||||
|
}
|
||||||
@@ -73,4 +73,10 @@
|
|||||||
<string name="manga_info_expand">More</string>
|
<string name="manga_info_expand">More</string>
|
||||||
<string name="action_copy_to_clipboard">Copy to clipboard</string>
|
<string name="action_copy_to_clipboard">Copy to clipboard</string>
|
||||||
<string name="description_placeholder">No description</string>
|
<string name="description_placeholder">No description</string>
|
||||||
|
<string name="advanced">Advanced</string>
|
||||||
|
<string name="app_info">App info</string>
|
||||||
|
<string name="app_version">App version</string>
|
||||||
|
<string name="build_time">Build time</string>
|
||||||
|
<string name="webview_version">WebView version</string>
|
||||||
|
<string name="advanced_page">Dump crash logs, debug info</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user