fix(ui): social section

- fix logger line separator
This commit is contained in:
rhunk 2023-08-31 03:24:33 +02:00
parent 61da95f41b
commit c791fbbd00
6 changed files with 175 additions and 27 deletions

View File

@ -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 {

View File

@ -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

View File

@ -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,
)
}
}
}
}

View File

@ -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
)
}
}
}

View File

@ -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 -> {

View File

@ -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)