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:
Mapp/src/main/kotlin/me/rhunk/snapenhance/LogManager.kt | 34+++++++++++++++++++++++++---------
Mapp/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/home/HomeSection.kt | 12++++++++----
Mapp/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/home/HomeSubSection.kt | 72+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mapp/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/social/ScopeContent.kt | 24++++++++++++++++++++----
Mapp/src/main/kotlin/me/rhunk/snapenhance/ui/manager/sections/social/SocialSection.kt | 15++++++++++-----
Mcore/src/main/kotlin/me/rhunk/snapenhance/Logger.kt | 45+++++++++++++++++++++++++++++++++++++++++++--
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)