feat: view message logger
This commit is contained in:
@@ -55,6 +55,7 @@ class Routes(
|
||||
const val CONFIG_EXPORT_SUMMARY_ROUTE = "config_export_summary/?exportSensitiveData={exportSensitiveData}"
|
||||
const val FRIEND_TRACKER_CONFIG_EXPORT_ROUTE = "friend_tracker_config_export/?rule_id={rule_id}"
|
||||
const val FRIEND_TRACKER_CONFIG_IMPORT_ROUTE = "friend_tracker_config_import"
|
||||
const val VIEW_LOGGER_HISTORY_ROUTE = "view_logger_history/{uri}"
|
||||
}
|
||||
|
||||
lateinit var navController: NavController
|
||||
@@ -77,6 +78,7 @@ class Routes(
|
||||
val settings = route(RouteInfo("home_settings"), HomeSettings()).parent(home)
|
||||
val homeLogs = route(RouteInfo("home_logs"), HomeLogs()).parent(home)
|
||||
val loggerHistory = route(RouteInfo("logger_history"), LoggerHistoryRoot()).parent(home)
|
||||
val viewLoggerHistory = route(RouteInfo(VIEW_LOGGER_HISTORY_ROUTE), LoggerHistoryRoot()).parent(home)
|
||||
val friendTracker = route(RouteInfo("friend_tracker", icon = Icons.Default.PersonSearch), FriendTrackerManagerRoot()).parent(home)
|
||||
val editRule = route(RouteInfo("edit_rule/?rule_id={rule_id}", hasOwnTopBar = true), EditRule())
|
||||
val friendTrackerConfigExport = route(RouteInfo(FRIEND_TRACKER_CONFIG_EXPORT_ROUTE), me.rhunk.snapenhance.ui.manager.pages.tracker.FriendTrackerConfigExportScreen())
|
||||
|
||||
@@ -22,6 +22,7 @@ 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
|
||||
import androidx.core.net.toUri
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import com.google.gson.JsonParser
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -46,6 +47,7 @@ import me.rhunk.snapenhance.core.features.impl.downloader.decoder.MessageDecoder
|
||||
import me.rhunk.snapenhance.download.DownloadProcessor
|
||||
import me.rhunk.snapenhance.storage.findFriend
|
||||
import me.rhunk.snapenhance.ui.manager.Routes
|
||||
import java.net.URLDecoder
|
||||
import java.text.DateFormat
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.math.absoluteValue
|
||||
@@ -216,9 +218,14 @@ class LoggerHistoryRoot : Routes.Route() {
|
||||
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
override val content: @Composable (NavBackStackEntry) -> Unit = {
|
||||
override val content: @Composable (NavBackStackEntry) -> Unit = { navBackStackEntry ->
|
||||
LaunchedEffect(Unit) {
|
||||
loggerWrapper = LoggerWrapper(context.androidContext)
|
||||
val uri = navBackStackEntry.arguments?.getString("uri")?.let {
|
||||
runCatching {
|
||||
URLDecoder.decode(it, "UTF-8").toUri()
|
||||
}.getOrNull()
|
||||
}
|
||||
loggerWrapper = LoggerWrapper(context.androidContext, uri)
|
||||
}
|
||||
|
||||
val conversationInfoCache = remember { ConcurrentHashMap<String, String?>() }
|
||||
|
||||
@@ -46,6 +46,9 @@ import me.rhunk.snapenhance.ui.util.ActivityLauncherHelper
|
||||
import me.rhunk.snapenhance.ui.util.AlertDialogs
|
||||
import me.rhunk.snapenhance.ui.util.openFile
|
||||
import me.rhunk.snapenhance.ui.util.saveFile
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.net.URLEncoder
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class HomeSettings : Routes.Route() {
|
||||
@@ -417,6 +420,26 @@ class HomeSettings : Routes.Route() {
|
||||
}) {
|
||||
Text(text = translation["export_button"])
|
||||
}
|
||||
Button(onClick = {
|
||||
runCatching {
|
||||
activityLauncherHelper.openFile("application/octet-stream") { uri ->
|
||||
val tempFile = File(context.androidContext.cacheDir, "view_message_logger.db")
|
||||
context.androidContext.contentResolver.openInputStream(uri.toUri())?.use { inputStream ->
|
||||
FileOutputStream(tempFile).use { outputStream ->
|
||||
inputStream.copyTo(outputStream)
|
||||
}
|
||||
}
|
||||
routes.viewLoggerHistory.navigate {
|
||||
put("uri", URLEncoder.encode(tempFile.toUri().toString(), "UTF-8"))
|
||||
}
|
||||
}
|
||||
}.onFailure {
|
||||
context.log.error("Failed to open file", it)
|
||||
context.longToast("Failed to open file! ${it.localizedMessage}")
|
||||
}
|
||||
}) {
|
||||
Text(text = translation["view_button"])
|
||||
}
|
||||
Button(onClick = {
|
||||
runCatching {
|
||||
context.messageLogger.purgeAll()
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
"success_toast": "Done!",
|
||||
"message_logger_summary": "{messageCount} messages\n{storyCount} stories",
|
||||
"export_button": "Export",
|
||||
"view_button": "View",
|
||||
"clear_button": "Clear",
|
||||
"backup_button": "Backup",
|
||||
"restore_button": "Restore",
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.rhunk.snapenhance.common.bridge.wrapper
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.net.Uri
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonObject
|
||||
import kotlinx.coroutines.*
|
||||
@@ -70,9 +71,13 @@ data class TrackerLog(
|
||||
}
|
||||
|
||||
class LoggerWrapper(
|
||||
val databaseFile: File
|
||||
val databaseFile: File,
|
||||
private val readOnly: Boolean = false
|
||||
): LoggerInterface.Stub() {
|
||||
constructor(context: Context): this(File(context.getDatabasePath(InternalFileHandleType.MESSAGE_LOGGER.fileName).absolutePath))
|
||||
constructor(context: Context, uri: Uri? = null): this(
|
||||
uri?.path?.let { File(it) } ?: File(context.getDatabasePath(InternalFileHandleType.MESSAGE_LOGGER.fileName).absolutePath),
|
||||
uri != null
|
||||
)
|
||||
|
||||
private var _database: SQLiteDatabase? = null
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@@ -82,49 +87,52 @@ class LoggerWrapper(
|
||||
private val database get() = synchronized(this) {
|
||||
_database?.takeIf { it.isOpen } ?: run {
|
||||
_database?.close()
|
||||
val openedDatabase = SQLiteDatabase.openDatabase(databaseFile.absolutePath, null, SQLiteDatabase.CREATE_IF_NECESSARY or SQLiteDatabase.OPEN_READWRITE)
|
||||
SQLiteDatabaseHelper.createTablesFromSchema(openedDatabase, mapOf(
|
||||
"messages" to listOf(
|
||||
"id INTEGER PRIMARY KEY",
|
||||
"message_id BIGINT",
|
||||
"conversation_id VARCHAR",
|
||||
"user_id CHAR(36)",
|
||||
"username VARCHAR",
|
||||
"send_timestamp BIGINT",
|
||||
"added_timestamp BIGINT",
|
||||
"group_title VARCHAR",
|
||||
"message_data BLOB"
|
||||
),
|
||||
"chat_edits" to listOf(
|
||||
"id INTEGER PRIMARY KEY",
|
||||
"edit_number INTEGER",
|
||||
"added_timestamp BIGINT",
|
||||
"conversation_id VARCHAR",
|
||||
"message_id BIGINT",
|
||||
"message_text BLOB"
|
||||
),
|
||||
"stories" to listOf(
|
||||
"id INTEGER PRIMARY KEY",
|
||||
"added_timestamp BIGINT",
|
||||
"user_id VARCHAR",
|
||||
"posted_timestamp BIGINT",
|
||||
"created_timestamp BIGINT",
|
||||
"url VARCHAR",
|
||||
"encryption_key BLOB",
|
||||
"encryption_iv BLOB"
|
||||
),
|
||||
"tracker_events" to listOf(
|
||||
"id INTEGER PRIMARY KEY",
|
||||
"timestamp BIGINT",
|
||||
"conversation_id CHAR(36)",
|
||||
"conversation_title VARCHAR",
|
||||
"is_group BOOLEAN",
|
||||
"username VARCHAR",
|
||||
"user_id VARCHAR",
|
||||
"event_type VARCHAR",
|
||||
"data VARCHAR"
|
||||
)
|
||||
))
|
||||
val dbFlags = if (readOnly) SQLiteDatabase.OPEN_READONLY else SQLiteDatabase.CREATE_IF_NECESSARY or SQLiteDatabase.OPEN_READWRITE
|
||||
val openedDatabase = SQLiteDatabase.openDatabase(databaseFile.absolutePath, null, dbFlags)
|
||||
if (!readOnly) {
|
||||
SQLiteDatabaseHelper.createTablesFromSchema(openedDatabase, mapOf(
|
||||
"messages" to listOf(
|
||||
"id INTEGER PRIMARY KEY",
|
||||
"message_id BIGINT",
|
||||
"conversation_id VARCHAR",
|
||||
"user_id CHAR(36)",
|
||||
"username VARCHAR",
|
||||
"send_timestamp BIGINT",
|
||||
"added_timestamp BIGINT",
|
||||
"group_title VARCHAR",
|
||||
"message_data BLOB"
|
||||
),
|
||||
"chat_edits" to listOf(
|
||||
"id INTEGER PRIMARY KEY",
|
||||
"edit_number INTEGER",
|
||||
"added_timestamp BIGINT",
|
||||
"conversation_id VARCHAR",
|
||||
"message_id BIGINT",
|
||||
"message_text BLOB"
|
||||
),
|
||||
"stories" to listOf(
|
||||
"id INTEGER PRIMARY KEY",
|
||||
"added_timestamp BIGINT",
|
||||
"user_id VARCHAR",
|
||||
"posted_timestamp BIGINT",
|
||||
"created_timestamp BIGINT",
|
||||
"url VARCHAR",
|
||||
"encryption_key BLOB",
|
||||
"encryption_iv BLOB"
|
||||
),
|
||||
"tracker_events" to listOf(
|
||||
"id INTEGER PRIMARY KEY",
|
||||
"timestamp BIGINT",
|
||||
"conversation_id CHAR(36)",
|
||||
"conversation_title VARCHAR",
|
||||
"is_group BOOLEAN",
|
||||
"username VARCHAR",
|
||||
"user_id VARCHAR",
|
||||
"event_type VARCHAR",
|
||||
"data VARCHAR"
|
||||
)
|
||||
))
|
||||
}
|
||||
_database = openedDatabase
|
||||
openedDatabase
|
||||
}
|
||||
|
||||
@@ -161,4 +161,4 @@ val syncTasks = cargoTargets.mapIndexed { index, target ->
|
||||
|
||||
tasks.named("preBuild").configure {
|
||||
syncTasks.forEach { dependsOn(it) }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user