Skip to content

Commit 2080ce9

Browse files
committed
1 parent 88935d9 commit 2080ce9

File tree

5 files changed

+195
-0
lines changed

5 files changed

+195
-0
lines changed

app/src/main/AndroidManifest.xml

+5
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,10 @@
7979
<activity
8080
android:name=".setup.SetupActivity"
8181
android:exported="false" />
82+
<activity
83+
android:name=".settings.LogActivity"
84+
android:exported="false"
85+
android:launchMode="singleTask"
86+
android:parentActivityName=".settings.PrefMainActivity" />
8287
</application>
8388
</manifest>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/**
2+
* Adapted from [fcitx5-android/LogActivity.kt](https://github.com/fcitx5-android/fcitx5-android/blob/e44c1c7/app/src/main/java/org/fcitx/fcitx5/android/ui/main/LogActivity.kt)
3+
*/
4+
package com.osfans.trime.settings
5+
6+
import android.os.Bundle
7+
import android.view.View
8+
import androidx.activity.result.ActivityResultLauncher
9+
import androidx.activity.result.contract.ActivityResultContracts
10+
import androidx.appcompat.app.AlertDialog
11+
import androidx.appcompat.app.AppCompatActivity
12+
import androidx.lifecycle.lifecycleScope
13+
import cat.ereza.customactivityoncrash.CustomActivityOnCrash
14+
import com.osfans.trime.BuildConfig
15+
import com.osfans.trime.R
16+
import com.osfans.trime.TrimeApplication
17+
import com.osfans.trime.databinding.ActivityLogBinding
18+
import com.osfans.trime.settings.components.LogView
19+
import com.osfans.trime.util.DeviceInfo
20+
import com.osfans.trime.util.Logcat
21+
import com.osfans.trime.util.bindOnNotNull
22+
import com.osfans.trime.util.iso8601UTCDateTime
23+
import com.osfans.trime.util.toast
24+
import kotlinx.coroutines.Dispatchers
25+
import kotlinx.coroutines.NonCancellable
26+
import kotlinx.coroutines.launch
27+
import java.io.OutputStreamWriter
28+
29+
class LogActivity : AppCompatActivity() {
30+
31+
private lateinit var launcher: ActivityResultLauncher<String>
32+
private lateinit var logView: LogView
33+
34+
private fun registerLauncher() {
35+
launcher = registerForActivityResult(ActivityResultContracts.CreateDocument()) { uri ->
36+
lifecycleScope.launch(NonCancellable + Dispatchers.IO) {
37+
runCatching {
38+
if (uri != null)
39+
contentResolver.openOutputStream(uri)?.let { OutputStreamWriter(it) }
40+
else null
41+
}.bindOnNotNull { x ->
42+
x.use {
43+
logView
44+
.currentLog
45+
.let { log ->
46+
runCatching {
47+
it.write("--------- Build Info\n")
48+
it.write("${BuildConfig.BUILD_INFO}\n")
49+
it.write("--------- Device Info\n")
50+
it.write(DeviceInfo.get(this@LogActivity))
51+
it.write(log.toString())
52+
}
53+
}
54+
}
55+
}?.toast()
56+
}
57+
}
58+
}
59+
60+
override fun onCreate(savedInstanceState: Bundle?) {
61+
super.onCreate(savedInstanceState)
62+
val binding = ActivityLogBinding.inflate(layoutInflater)
63+
setContentView(binding.root)
64+
with(binding) {
65+
setSupportActionBar(toolbar.toolbar)
66+
this@LogActivity.logView = logView
67+
logView.setLogcat(
68+
if (CustomActivityOnCrash.getConfigFromIntent(intent) == null) {
69+
supportActionBar!!.apply {
70+
setDisplayHomeAsUpEnabled(true)
71+
setTitle(R.string.real_time_logs)
72+
}
73+
Logcat()
74+
} else {
75+
supportActionBar!!.setTitle(R.string.crash_logs)
76+
clearButton.visibility = View.GONE
77+
AlertDialog.Builder(this@LogActivity)
78+
.setTitle(R.string.app_crash)
79+
.setMessage(R.string.app_crash_message)
80+
.setPositiveButton(android.R.string.ok) { _, _ -> }
81+
.show()
82+
Logcat(TrimeApplication.getLastPid())
83+
}
84+
)
85+
clearButton.setOnClickListener {
86+
logView.clear()
87+
}
88+
exportButton.setOnClickListener {
89+
launcher.launch("$packageName-${iso8601UTCDateTime()}.txt")
90+
}
91+
}
92+
registerLauncher()
93+
}
94+
}

app/src/main/java/com/osfans/trime/util/Utils.kt

+39
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,54 @@
11
package com.osfans.trime.util
22

33
import android.content.Context
4+
import com.blankj.utilcode.util.ToastUtils
5+
import com.osfans.trime.R
46
import com.osfans.trime.TrimeApplication
7+
import kotlinx.coroutines.Dispatchers
8+
import kotlinx.coroutines.withContext
59
import java.text.SimpleDateFormat
610
import java.util.Date
11+
import java.util.Locale
12+
import java.util.TimeZone
13+
import kotlin.contracts.ExperimentalContracts
14+
import kotlin.contracts.InvocationKind
15+
import kotlin.contracts.contract
716

817
val appContext: Context get() = TrimeApplication.getInstance().applicationContext
918

19+
@OptIn(ExperimentalContracts::class)
20+
inline fun <T : Any, U> Result<T?>.bindOnNotNull(block: (T) -> Result<U>): Result<U>? {
21+
contract {
22+
callsInPlace(block, InvocationKind.AT_MOST_ONCE)
23+
}
24+
return when {
25+
isSuccess && getOrThrow() != null -> block(getOrThrow()!!)
26+
isSuccess && getOrThrow() == null -> null
27+
else -> Result.failure(exceptionOrNull()!!)
28+
}
29+
}
30+
31+
suspend fun <T> Result<T>.toast() = withContext(Dispatchers.Main.immediate) {
32+
onSuccess {
33+
ToastUtils.showShort(R.string.setup__done)
34+
}
35+
onFailure {
36+
ToastUtils.showShort(it.message)
37+
}
38+
}
39+
1040
fun formatDateTime(timeMillis: Long? = null): String =
1141
SimpleDateFormat.getDateTimeInstance().format(timeMillis?.let { Date(it) } ?: Date())
1242

43+
private val iso8601DateFormat by lazy {
44+
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US).apply {
45+
timeZone = TimeZone.getTimeZone("UTC")
46+
}
47+
}
48+
49+
fun iso8601UTCDateTime(timeMillis: Long? = null): String =
50+
iso8601DateFormat.format(timeMillis?.let { Date(it) } ?: Date())
51+
1352
@Suppress("NOTHING_TO_INLINE")
1453
inline fun CharSequence.startsWithAsciiChar(): Boolean {
1554
val firstCodePoint = this.toString().codePointAt(0)
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
xmlns:tools="http://schemas.android.com/tools"
5+
android:layout_width="match_parent"
6+
android:layout_height="match_parent"
7+
android:orientation="vertical"
8+
tools:context=".settings.LogActivity">
9+
10+
<include
11+
android:id="@+id/toolbar"
12+
layout="@layout/toolbar"
13+
app:layout_constraintEnd_toEndOf="parent"
14+
app:layout_constraintStart_toStartOf="parent"
15+
app:layout_constraintTop_toTopOf="parent"/>
16+
17+
<com.osfans.trime.settings.components.LogView
18+
android:id="@+id/logView"
19+
android:layout_width="match_parent"
20+
android:layout_height="0dp"
21+
app:layout_constraintBottom_toTopOf="@id/logButtons"
22+
app:layout_constraintEnd_toEndOf="parent"
23+
app:layout_constraintStart_toStartOf="parent"
24+
app:layout_constraintTop_toBottomOf="@id/toolbar" />
25+
26+
<LinearLayout
27+
android:id="@+id/logButtons"
28+
android:layout_width="match_parent"
29+
android:layout_height="wrap_content"
30+
android:gravity="center_horizontal"
31+
android:orientation="horizontal"
32+
app:layout_constraintBottom_toBottomOf="parent"
33+
app:layout_constraintEnd_toEndOf="parent"
34+
app:layout_constraintStart_toStartOf="parent">
35+
36+
<Button
37+
android:id="@+id/clearButton"
38+
style="?android:attr/buttonBarButtonStyle"
39+
android:layout_width="wrap_content"
40+
android:layout_height="wrap_content"
41+
android:text="@string/clear" />
42+
43+
<Button
44+
android:id="@+id/exportButton"
45+
style="?android:attr/buttonBarButtonStyle"
46+
android:layout_width="wrap_content"
47+
android:layout_height="wrap_content"
48+
android:text="@string/export" />
49+
</LinearLayout>
50+
51+
</androidx.constraintlayout.widget.ConstraintLayout>

app/src/main/res/values/strings.xml

+6
Original file line numberDiff line numberDiff line change
@@ -228,4 +228,10 @@
228228
<item name="100">100</item>
229229
</string-array>
230230
<string name="exception_logcat_created">Logcat process is already created</string>
231+
<string name="app_crash">Application crashed</string>
232+
<string name="app_crash_message">Sorry, but we bring you some logs to investigate.</string>
233+
<string name="real_time_logs">Real-time logs</string>
234+
<string name="crash_logs">Crash logs</string>
235+
<string name="clear">Clear</string>
236+
<string name="export">Export</string>
231237
</resources>

0 commit comments

Comments
 (0)