commit c791fbbd005f1ec1acece35a6434ae3695d58290
parent 61da95f41b3716364a587a156ac8981b0c9ecf4b
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date: Thu, 31 Aug 2023 03:24:33 +0200
fix(ui): social section
- fix logger line separator
Diffstat:
6 files changed, 175 insertions(+), 27 deletions(-)
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/LogManager.kt b/app/src/main/kotlin/me/rhunk/snapenhance/LogManager.kt
@@ -2,6 +2,7 @@ package me.rhunk.snapenhance
import android.content.SharedPreferences
import android.util.Log
+import com.google.gson.GsonBuilder
import java.io.File
import java.io.OutputStream
import java.io.RandomAccessFile
@@ -42,6 +43,21 @@ class LogReader(
private var startLineIndexes = mutableListOf<Long>()
var lineCount = queryLineCount()
+ private fun readLogLine(): LogLine? {
+ val lines = mutableListOf<String>()
+ while (true) {
+ val lastPointer = randomAccessFile.filePointer
+ val line = randomAccessFile.readLine() ?: return null
+ if (lines.size > 0 && line.startsWith("|")) {
+ randomAccessFile.seek(lastPointer)
+ break
+ }
+ lines.add(line)
+ }
+ val line = lines.joinToString("\n").replaceFirst("|", "")
+ return LogLine.fromString(line)
+ }
+
fun incrementLineCount() {
randomAccessFile.seek(randomAccessFile.length())
startLineIndexes.add(randomAccessFile.filePointer)
@@ -54,7 +70,7 @@ class LogReader(
var lastIndex: Long
while (true) {
lastIndex = randomAccessFile.filePointer
- randomAccessFile.readLine() ?: break
+ readLogLine() ?: break
startLineIndexes.add(lastIndex)
lines++
}
@@ -64,7 +80,7 @@ class LogReader(
private fun getLine(index: Int): String? {
if (index <= 0 || index > lineCount) return null
randomAccessFile.seek(startLineIndexes[index])
- return randomAccessFile.readLine()
+ return readLogLine()?.toString()
}
fun getLogLine(index: Int): LogLine? {
@@ -74,7 +90,7 @@ class LogReader(
class LogManager(
- remoteSideContext: RemoteSideContext
+ private val remoteSideContext: RemoteSideContext
) {
companion object {
private const val TAG = "SnapEnhanceManager"
@@ -118,12 +134,10 @@ class LogManager(
}
fun clearLogs() {
- logFile.delete()
+ logFolder.listFiles()?.forEach { it.delete() }
newLogFile()
}
- fun getLogFile() = logFile
-
fun exportLogsToZip(outputStream: OutputStream) {
val zipOutputStream = ZipOutputStream(outputStream)
//add logFolder to zip
@@ -136,8 +150,10 @@ class LogManager(
}
//add device info to zip
- zipOutputStream.putNextEntry(ZipEntry("device_info.txt"))
-
+ zipOutputStream.putNextEntry(ZipEntry("device_info.json"))
+ val gson = GsonBuilder().setPrettyPrinting().create()
+ zipOutputStream.write(gson.toJson(remoteSideContext.installationSummary).toByteArray())
+ zipOutputStream.closeEntry()
zipOutputStream.close()
}
@@ -178,7 +194,7 @@ class LogManager(
fun internalLog(tag: String, logLevel: LogLevel, message: Any?) {
runCatching {
val line = LogLine(logLevel, getCurrentDateTime(), tag, message.toString())
- logFile.appendText("$line\n", Charsets.UTF_8)
+ logFile.appendText("|$line\n", Charsets.UTF_8)
lineAddListener(line)
Log.println(logLevel.priority, tag, message.toString())
}.onFailure {
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/home/HomeSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/home/HomeSection.kt
@@ -237,11 +237,15 @@ class HomeSection : Section() {
})
DropdownMenuItem(onClick = {
- val logFile = context.log.getLogFile()
- activityLauncherHelper.saveFile(logFile.name, "text/plain") { uri ->
+ activityLauncherHelper.saveFile("snapenhance-logs-${System.currentTimeMillis()}.zip", "application/zip") { uri ->
context.androidContext.contentResolver.openOutputStream(Uri.parse(uri))?.use {
- logFile.inputStream().copyTo(it)
- context.longToast("Saved logs to $uri")
+ runCatching {
+ context.log.exportLogsToZip(it)
+ context.longToast("Saved logs to $uri")
+ }.onFailure {
+ context.longToast("Failed to save logs to $uri!")
+ context.log.error("Failed to save logs to $uri!", it)
+ }
}
}
showDropDown = false
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/home/HomeSubSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/home/HomeSubSection.kt
@@ -3,10 +3,13 @@ package me.rhunk.snapenhance.ui.manager.sections.home
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@@ -19,9 +22,12 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardDoubleArrowDown
import androidx.compose.material.icons.filled.KeyboardDoubleArrowUp
import androidx.compose.material.icons.filled.OpenInNew
+import androidx.compose.material.icons.outlined.BugReport
+import androidx.compose.material.icons.outlined.Info
+import androidx.compose.material.icons.outlined.Report
+import androidx.compose.material.icons.outlined.Warning
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.FilledIconButton
-import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
@@ -36,13 +42,19 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalClipboardManager
+import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import me.rhunk.snapenhance.Constants
+import me.rhunk.snapenhance.LogChannels
+import me.rhunk.snapenhance.LogLevel
import me.rhunk.snapenhance.LogReader
import me.rhunk.snapenhance.RemoteSideContext
import me.rhunk.snapenhance.action.EnumAction
@@ -106,6 +118,7 @@ class HomeSubSection(
@Composable
fun LogsSection() {
val coroutineScope = rememberCoroutineScope()
+ val clipboardManager = LocalClipboardManager.current
var lineCount by remember { mutableIntStateOf(0) }
var logReader by remember { mutableStateOf<LogReader?>(null) }
logListState = remember { LazyListState(0) }
@@ -120,12 +133,65 @@ class HomeSubSection(
) {
items(lineCount) { index ->
val line = logReader?.getLogLine(index) ?: return@items
+ var expand by remember { mutableStateOf(false) }
Box(modifier = Modifier
.fillMaxWidth()
.background(
if (index % 2 == 0) MaterialTheme.colorScheme.surface else MaterialTheme.colorScheme.surfaceVariant
- )) {
- Text(text = line.message, modifier = Modifier.padding(9.dp), fontSize = 10.sp)
+ )
+ .pointerInput(Unit) {
+ detectTapGestures(
+ onLongPress = {
+ coroutineScope.launch {
+ clipboardManager.setText(AnnotatedString(line.message))
+ }
+ },
+ onTap = {
+ expand = !expand
+ }
+ )
+ }) {
+
+ Row(
+ modifier = Modifier
+ .horizontalScroll(ScrollState(0))
+ .padding(4.dp)
+ .defaultMinSize(minHeight = 30.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ if (!expand) {
+ Icon(
+ imageVector = when (line.logLevel) {
+ LogLevel.DEBUG -> Icons.Outlined.BugReport
+ LogLevel.ERROR, LogLevel.ASSERT -> Icons.Outlined.Report
+ LogLevel.INFO, LogLevel.VERBOSE -> Icons.Outlined.Info
+ LogLevel.WARN -> Icons.Outlined.Warning
+ },
+ contentDescription = null,
+ )
+
+ Text(
+ text = LogChannels.fromChannel(line.tag)?.shortName ?: line.tag,
+ modifier = Modifier.padding(start = 4.dp),
+ fontWeight = FontWeight.Light,
+ fontSize = 10.sp,
+ )
+
+ Text(
+ text = line.dateTime,
+ modifier = Modifier.padding(start = 4.dp, end = 4.dp),
+ fontSize = 10.sp
+ )
+ }
+
+ Text(
+ text = line.message.trimIndent(),
+ fontSize = 10.sp,
+ maxLines = if (expand) Int.MAX_VALUE else 6,
+ overflow = if (expand) TextOverflow.Visible else TextOverflow.Ellipsis,
+ softWrap = !expand,
+ )
+ }
}
}
}
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/social/ScopeContent.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/social/ScopeContent.kt
@@ -241,10 +241,26 @@ class ScopeContent(
return
}
- Column {
- Text(text = group.name, maxLines = 1)
- Text(text = "participantsCount: ${group.participantsCount}", maxLines = 1)
- Spacer(modifier = Modifier.height(16.dp))
+
+ Column(
+ modifier = Modifier
+ .padding(10.dp)
+ .fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Text(
+ text = group.name,
+ maxLines = 1,
+ fontSize = 20.sp,
+ fontWeight = FontWeight.Bold
+ )
+ Spacer(modifier = Modifier.height(5.dp))
+ Text(
+ text = "Participants: ${group.participantsCount}",
+ maxLines = 1,
+ fontSize = 12.sp,
+ fontWeight = FontWeight.Light
+ )
}
}
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/social/SocialSection.kt b/app/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/social/SocialSection.kt
@@ -2,6 +2,7 @@ package me.rhunk.snapenhance.ui.manager.sections.social
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
@@ -39,6 +40,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -147,8 +149,7 @@ class SocialSection : Section() {
if (listSize == 0) {
item {
- //TODO: i18n
- Text(text = "No ${scope.key.lowercase()}s found")
+ Text(text = "(empty)", modifier = Modifier.fillMaxWidth().padding(10.dp), textAlign = TextAlign.Center)
}
}
@@ -172,9 +173,13 @@ class SocialSection : Section() {
when (scope) {
SocialScope.GROUP -> {
val group = groupList[index]
- Column {
- Text(text = group.name, maxLines = 1)
- Text(text = "participantsCount: ${group.participantsCount}", maxLines = 1)
+ Column(
+ modifier = Modifier
+ .padding(10.dp)
+ .fillMaxSize(),
+ verticalArrangement = Arrangement.Center
+ ) {
+ Text(text = group.name, maxLines = 1, fontWeight = FontWeight.Bold)
}
}
SocialScope.FRIEND -> {
diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/Logger.kt b/core/src/main/kotlin/me/rhunk/snapenhance/Logger.kt
@@ -1,8 +1,11 @@
package me.rhunk.snapenhance
+import android.annotation.SuppressLint
import android.util.Log
import de.robv.android.xposed.XposedBridge
import me.rhunk.snapenhance.core.bridge.BridgeClient
+import me.rhunk.snapenhance.hook.HookStage
+import me.rhunk.snapenhance.hook.hook
enum class LogLevel(
val letter: String,
@@ -24,10 +27,28 @@ enum class LogLevel(
fun fromShortName(shortName: String): LogLevel? {
return values().find { it.shortName == shortName }
}
+
+ fun fromPriority(priority: Int): LogLevel? {
+ return values().find { it.priority == priority }
+ }
+ }
+}
+
+enum class LogChannels(val channel: String, val shortName: String) {
+ CORE("SnapEnhanceCore", "core"),
+ NATIVE("SnapEnhanceNative", "native"),
+ MANAGER("SnapEnhanceManager", "manager"),
+ XPOSED("LSPosed-Bridge", "xposed");
+
+ companion object {
+ fun fromChannel(channel: String): LogChannels? {
+ return values().find { it.channel == channel }
+ }
}
}
+@SuppressLint("PrivateApi")
class Logger(
private val bridgeClient: BridgeClient
) {
@@ -55,11 +76,31 @@ class Logger(
}
}
+ private var invokeOriginalPrintLog: (Int, String, String) -> Unit
+
+ init {
+ val printLnMethod = Log::class.java.getDeclaredMethod("println", Int::class.java, String::class.java, String::class.java)
+ printLnMethod.hook(HookStage.BEFORE) { param ->
+ val priority = param.arg(0) as Int
+ val tag = param.arg(1) as String
+ val message = param.arg(2) as String
+ internalLog(tag, LogLevel.fromPriority(priority) ?: LogLevel.INFO, message)
+ }
+
+ invokeOriginalPrintLog = { priority, tag, message ->
+ XposedBridge.invokeOriginalMethod(
+ printLnMethod,
+ null,
+ arrayOf(priority, tag, message)
+ )
+ }
+ }
+
private fun internalLog(tag: String, logLevel: LogLevel, message: Any?) {
runCatching {
bridgeClient.broadcastLog(tag, logLevel.shortName, message.toString())
}.onFailure {
- Log.println(logLevel.priority, tag, message.toString())
+ invokeOriginalPrintLog(logLevel.priority, tag, message.toString())
}
}
@@ -69,7 +110,7 @@ class Logger(
fun error(message: Any?, throwable: Throwable, tag: String = TAG) {
internalLog(tag, LogLevel.ERROR, message)
- internalLog(tag, LogLevel.ERROR, throwable)
+ internalLog(tag, LogLevel.ERROR, throwable.stackTraceToString())
}
fun info(message: Any?, tag: String = TAG) = internalLog(tag, LogLevel.INFO, message)