|
| 1 | +package com.zaneschepke.logcatter |
| 2 | + |
| 3 | +import androidx.lifecycle.DefaultLifecycleObserver |
| 4 | +import androidx.lifecycle.LifecycleOwner |
| 5 | +import com.zaneschepke.logcatter.model.LogMessage |
| 6 | +import kotlinx.coroutines.CoroutineScope |
| 7 | +import kotlinx.coroutines.Dispatchers |
| 8 | +import kotlinx.coroutines.ExperimentalCoroutinesApi |
| 9 | +import kotlinx.coroutines.Job |
| 10 | +import kotlinx.coroutines.SupervisorJob |
| 11 | +import kotlinx.coroutines.cancel |
| 12 | +import kotlinx.coroutines.channels.BufferOverflow |
| 13 | +import kotlinx.coroutines.flow.Flow |
| 14 | +import kotlinx.coroutines.flow.MutableSharedFlow |
| 15 | +import kotlinx.coroutines.flow.asSharedFlow |
| 16 | +import kotlinx.coroutines.launch |
| 17 | + |
| 18 | +class LogcatManager( |
| 19 | + pid: Int, |
| 20 | + logDir: String, |
| 21 | + maxFileSize: Long, |
| 22 | + maxFolderSize: Long, |
| 23 | +) : LogReader, DefaultLifecycleObserver { |
| 24 | + private val logScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) |
| 25 | + private val fileManager = LogFileManager(logDir, maxFileSize, maxFolderSize) |
| 26 | + private val logcatReader = LogcatStreamReader(pid, fileManager) |
| 27 | + private var logJob: Job? = null |
| 28 | + private var isStarted = false |
| 29 | + |
| 30 | + private val _bufferedLogs = MutableSharedFlow<LogMessage>( |
| 31 | + replay = 10_000, |
| 32 | + onBufferOverflow = BufferOverflow.DROP_OLDEST, |
| 33 | + ) |
| 34 | + private val _liveLogs = MutableSharedFlow<LogMessage>( |
| 35 | + replay = 1, |
| 36 | + onBufferOverflow = BufferOverflow.DROP_OLDEST, |
| 37 | + ) |
| 38 | + |
| 39 | + override val bufferedLogs: Flow<LogMessage> = _bufferedLogs.asSharedFlow() |
| 40 | + override val liveLogs: Flow<LogMessage> = _liveLogs.asSharedFlow() |
| 41 | + |
| 42 | + override fun onCreate(owner: LifecycleOwner) { |
| 43 | + // for auto start |
| 44 | + // start() |
| 45 | + } |
| 46 | + |
| 47 | + override fun onDestroy(owner: LifecycleOwner) { |
| 48 | + stop() |
| 49 | + logScope.cancel() |
| 50 | + } |
| 51 | + |
| 52 | + override fun start() { |
| 53 | + if (isStarted) return |
| 54 | + stop() |
| 55 | + logJob = logScope.launch { |
| 56 | + logcatReader.readLogs().collect { logMessage -> |
| 57 | + _bufferedLogs.emit(logMessage) |
| 58 | + _liveLogs.emit(logMessage) |
| 59 | + } |
| 60 | + } |
| 61 | + isStarted = true |
| 62 | + } |
| 63 | + |
| 64 | + override fun stop() { |
| 65 | + if (!isStarted) return |
| 66 | + logJob?.cancel() |
| 67 | + logcatReader.stop() |
| 68 | + fileManager.close() |
| 69 | + isStarted = false |
| 70 | + } |
| 71 | + |
| 72 | + override fun zipLogFiles(path: String) { |
| 73 | + logScope.launch { |
| 74 | + val wasStarted = isStarted |
| 75 | + stop() |
| 76 | + fileManager.zipLogs(path) |
| 77 | + if (wasStarted) { |
| 78 | + logcatReader.clearLogs() |
| 79 | + start() |
| 80 | + } |
| 81 | + } |
| 82 | + } |
| 83 | + |
| 84 | + @OptIn(ExperimentalCoroutinesApi::class) |
| 85 | + override suspend fun deleteAndClearLogs() { |
| 86 | + val wasStarted = isStarted |
| 87 | + stop() |
| 88 | + _bufferedLogs.resetReplayCache() |
| 89 | + fileManager.deleteAllLogs() |
| 90 | + if (wasStarted) start() |
| 91 | + } |
| 92 | +} |
0 commit comments