Skip to content

Commit e81cca1

Browse files
committed
fix: crash logs writer, profile path, LSPosed pause sync, UI polish
- Fix 1: InputBlockerServiceManager.reportCrash() now accepts Throwable and writes detailed crash logs (timestamp + stack trace) to /data/local/tmp/inputblocker/crash_logs/ for CrashLogActivity to read. - Fix 2: ProfileListActivity uses InputBlockerServiceManager.getConfigDir() instead of hardcoded /data/adb/modules/inputblocker/config/profiles. - Fix 3: togglePause() writes/removes paused=1 in config file so LSPosed hook module respects pause state. InputBlockerXposed reads paused flag and skips blocking when set. - Fix 4+5: CrashLogActivity + ProfileListActivity rewritten with MaterialCardView, MaterialButton, proper theme color integration.
1 parent 56238a0 commit e81cca1

5 files changed

Lines changed: 114 additions & 76 deletions

File tree

android-app/app/src/main/java/com/inputblocker/app/CrashLogActivity.kt

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package com.inputblocker.app
22

33
import android.os.Bundle
4-
import android.widget.Button
54
import android.widget.LinearLayout
65
import android.widget.ScrollView
76
import android.widget.TextView
87
import android.widget.Toast
98
import androidx.appcompat.app.AppCompatActivity
9+
import androidx.core.content.ContextCompat
10+
import com.google.android.material.button.MaterialButton
11+
import com.google.android.material.card.MaterialCardView
1012
import java.io.File
1113

1214
class CrashLogActivity : AppCompatActivity() {
@@ -17,27 +19,25 @@ class CrashLogActivity : AppCompatActivity() {
1719
}
1820

1921
private lateinit var logContainer: LinearLayout
20-
private lateinit var btnClear: Button
21-
private lateinit var btnRefresh: Button
22+
private lateinit var btnClear: MaterialButton
23+
private lateinit var btnRefresh: MaterialButton
2224

2325
override fun onCreate(savedInstanceState: Bundle?) {
2426
super.onCreate(savedInstanceState)
2527

28+
val currentTheme = getSharedPreferences("InputBlockerPrefs", MODE_PRIVATE)
29+
.getInt("theme", ThemeManager.THEME_SYSTEM)
30+
val colors = ThemeManager.getThemeColors(this@CrashLogActivity, currentTheme)
31+
2632
val root = LinearLayout(this).apply {
2733
orientation = LinearLayout.VERTICAL
2834
setPadding(32, 32, 32, 32)
29-
val currentTheme = getSharedPreferences("InputBlockerPrefs", MODE_PRIVATE)
30-
.getInt("theme", ThemeManager.THEME_SYSTEM)
31-
val colors = ThemeManager.getThemeColors(this@CrashLogActivity, currentTheme)
3235
setBackgroundColor(colors.background)
3336
}
3437

3538
val title = TextView(this).apply {
3639
text = "Crash Log Viewer"
3740
textSize = 24f
38-
val currentTheme = getSharedPreferences("InputBlockerPrefs", MODE_PRIVATE)
39-
.getInt("theme", ThemeManager.THEME_SYSTEM)
40-
val colors = ThemeManager.getThemeColors(this@CrashLogActivity, currentTheme)
4141
setTextColor(colors.textPrimary)
4242
setPadding(0, 0, 0, 32)
4343
}
@@ -48,15 +48,16 @@ class CrashLogActivity : AppCompatActivity() {
4848
setPadding(0, 0, 0, 16)
4949
}
5050

51-
btnRefresh = Button(this).apply {
51+
btnRefresh = MaterialButton(this).apply {
5252
text = "Refresh"
5353
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
5454
setOnClickListener { refreshLog() }
5555
}
5656

57-
btnClear = Button(this).apply {
57+
btnClear = MaterialButton(this).apply {
5858
text = "Clear All"
5959
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
60+
setTextColor(android.graphics.Color.RED)
6061
setOnClickListener {
6162
try {
6263
val crashDir = File(CRASH_LOG_DIR)
@@ -110,10 +111,25 @@ class CrashLogActivity : AppCompatActivity() {
110111
?.sortedByDescending { it.lastModified() }
111112
?: emptyList()
112113

114+
val currentTheme = getSharedPreferences("InputBlockerPrefs", MODE_PRIVATE)
115+
.getInt("theme", ThemeManager.THEME_SYSTEM)
116+
val colors = ThemeManager.getThemeColors(this@CrashLogActivity, currentTheme)
117+
113118
for (file in crashFiles) {
114-
val card = LinearLayout(this).apply {
119+
val card = MaterialCardView(this).apply {
120+
layoutParams = LinearLayout.LayoutParams(
121+
LinearLayout.LayoutParams.MATCH_PARENT,
122+
LinearLayout.LayoutParams.WRAP_CONTENT
123+
).also { it.setMargins(0, 0, 0, 12) }
124+
radius = 16f
125+
setContentPadding(16, 12, 16, 12)
126+
setCardBackgroundColor(colors.surface)
127+
strokeColor = colors.border
128+
strokeWidth = 1
129+
}
130+
131+
val innerLayout = LinearLayout(this).apply {
115132
orientation = LinearLayout.VERTICAL
116-
setPadding(0, 0, 0, 12)
117133
}
118134

119135
val headerText = "${file.name}${java.text.SimpleDateFormat(
@@ -124,12 +140,8 @@ class CrashLogActivity : AppCompatActivity() {
124140
text = headerText
125141
textSize = 13f
126142
setTypeface(null, android.graphics.Typeface.BOLD)
127-
val colors = ThemeManager.getThemeColors(
128-
this@CrashLogActivity,
129-
getSharedPreferences("InputBlockerPrefs", MODE_PRIVATE)
130-
.getInt("theme", ThemeManager.THEME_SYSTEM)
131-
)
132143
setTextColor(android.graphics.Color.parseColor("#FF5252"))
144+
setPadding(0, 0, 0, 8)
133145
}
134146

135147
val content = try {
@@ -141,30 +153,17 @@ class CrashLogActivity : AppCompatActivity() {
141153
val body = TextView(this).apply {
142154
text = content
143155
textSize = 11f
144-
setPadding(16, 8, 16, 8)
156+
setPadding(12, 8, 12, 8)
145157
setBackgroundColor(android.graphics.Color.parseColor("#1A000000"))
146-
val colors = ThemeManager.getThemeColors(
147-
this@CrashLogActivity,
148-
getSharedPreferences("InputBlockerPrefs", MODE_PRIVATE)
149-
.getInt("theme", ThemeManager.THEME_SYSTEM)
150-
)
151158
setTextColor(colors.textSecondary)
152159
setLineSpacing(4f, 1f)
153160
setTypeface(android.graphics.Typeface.MONOSPACE)
154161
}
155162

156-
card.addView(header)
157-
card.addView(body)
163+
innerLayout.addView(header)
164+
innerLayout.addView(body)
165+
card.addView(innerLayout)
158166
logContainer.addView(card)
159-
160-
// Separator
161-
val sep = TextView(this).apply {
162-
text = "──────────────────────────────────"
163-
textSize = 10f
164-
setTextColor(android.graphics.Color.parseColor("#40FFFFFF"))
165-
setPadding(0, 8, 0, 8)
166-
}
167-
logContainer.addView(sep)
168167
}
169168
} catch (e: Exception) {
170169
Toast.makeText(this, "Error reading crash logs: ${e.message}", Toast.LENGTH_SHORT).show()

android-app/app/src/main/java/com/inputblocker/app/InputBlockerServiceManager.kt

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,37 @@ object InputBlockerServiceManager {
152152
}
153153
}
154154

155-
fun reportCrash() {
155+
fun reportCrash(error: Throwable? = null) {
156156
try {
157157
runRootCommand("mkdir -p /data/local/tmp/inputblocker && touch ${CRASH_FLAG}")
158158
// Increment consecutive crash counter
159159
val current = getCrashCount()
160160
runRootCommand("echo '${current + 1}' > $CRASH_COUNTER_FILE")
161+
162+
// Write detailed crash log
163+
val timestamp = java.text.SimpleDateFormat(
164+
"yyyy-MM-dd_HH-mm-ss", java.util.Locale.getDefault()
165+
).format(java.util.Date())
166+
val crashLogDir = "/data/local/tmp/inputblocker/crash_logs"
167+
val crashLogFile = "$crashLogDir/crash_$timestamp.log"
168+
val detail = buildString {
169+
appendLine("=== InputBlocker Crash Report ===")
170+
appendLine("Time: $timestamp")
171+
if (error != null) {
172+
appendLine("Error: ${error.message}")
173+
appendLine("Stack:")
174+
for (ste in error.stackTrace) {
175+
appendLine(" at $ste")
176+
}
177+
}
178+
appendLine("Crash Count: ${current + 1}")
179+
}
180+
runRootCommand("mkdir -p $crashLogDir")
181+
// Write via temp file + root cp (echo can mangle special chars)
182+
val tempFile = java.io.File("/data/local/tmp/inputblocker/crash_payload.tmp")
183+
tempFile.writeText(detail)
184+
runRootCommand("cp ${tempFile.absolutePath} $crashLogFile && chmod 644 $crashLogFile && rm -f ${tempFile.absolutePath}")
185+
161186
Log.e(TAG, "Crash detected! Consecutive crash count: ${current + 1}")
162187
} catch (e: Exception) {
163188
Log.e(TAG, "Failed to report crash", e)

android-app/app/src/main/java/com/inputblocker/app/InputBlockerXposed.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class InputBlockerXposed : IXposedHookZygoteInit {
2020
private const val TAG = "InputBlocker-Hook"
2121
@Volatile private var cachedRegions = ArrayList<Region>()
2222
@Volatile private var cachedEnabled = true
23+
@Volatile private var cachedPaused = false
2324
@Volatile private var cachedLsposedMode = true
2425
@Volatile private var testModeActive = false
2526
private var lastLoadTime = 0L
@@ -74,7 +75,7 @@ class InputBlockerXposed : IXposedHookZygoteInit {
7475
val now = System.currentTimeMillis()
7576
updateConfigIfNeeded(now)
7677

77-
if (!cachedEnabled) return
78+
if (!cachedEnabled || cachedPaused) return
7879
if (!cachedLsposedMode) return // LSPosed mode disabled — OverlayService handles blocking
7980

8081
// Use index access for performance; dispatchMotionLocked(IBinder, MotionEvent, ...)
@@ -243,13 +244,15 @@ class InputBlockerXposed : IXposedHookZygoteInit {
243244

244245
val newRegions = ArrayList<Region>()
245246
var newEnabled = true
247+
var newPaused = false
246248
var newLsposedMode = true
247249

248250
BufferedReader(FileReader(file)).use { reader ->
249251
reader.lineSequence().forEach { line ->
250252
val trimmed = line.trim()
251253
when {
252254
trimmed.startsWith("enabled=") -> newEnabled = trimmed.substring(8) == "1"
255+
trimmed.startsWith("paused=") -> newPaused = trimmed.substring(7) == "1"
253256
trimmed.startsWith("lsposed_mode=") -> newLsposedMode = trimmed.substring(13) == "1"
254257
trimmed.isNotEmpty() && !trimmed.startsWith("#") ->
255258
Region.fromString(trimmed)?.let { newRegions.add(it) }
@@ -258,6 +261,7 @@ class InputBlockerXposed : IXposedHookZygoteInit {
258261
}
259262
cachedRegions = newRegions
260263
cachedEnabled = newEnabled
264+
cachedPaused = newPaused
261265
cachedLsposedMode = newLsposedMode
262266
lastLoadTime = now
263267
} catch (e: Exception) {

android-app/app/src/main/java/com/inputblocker/app/MainActivity.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ class MainActivity : AppCompatActivity() {
122122
// Senior Engineering: Fail-safe crash tracking
123123
val oldHandler = Thread.getDefaultUncaughtExceptionHandler()
124124
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
125-
InputBlockerServiceManager.reportCrash()
125+
InputBlockerServiceManager.reportCrash(throwable)
126126
oldHandler?.uncaughtException(thread, throwable)
127127
}
128128

@@ -1135,6 +1135,15 @@ class MainActivity : AppCompatActivity() {
11351135
val action = if (isPaused) "com.inputblocker.PAUSE" else "com.inputblocker.RESUME"
11361136
sendBroadcast(Intent(action))
11371137

1138+
// Sync paused state to config so LSPosed hook module also respects it
1139+
try {
1140+
val configPath = InputBlockerServiceManager.getConfigFile(this, "default")
1141+
InputBlockerServiceManager.runRootCommand("sed -i '/^paused=/d' $configPath")
1142+
if (isPaused) {
1143+
InputBlockerServiceManager.runRootCommand("echo 'paused=1' >> $configPath")
1144+
}
1145+
} catch (_: Exception) { }
1146+
11381147
btnActionPause.text = if (isPaused) "RESUME" else "PAUSE"
11391148
updateUI()
11401149
}

0 commit comments

Comments
 (0)