Skip to content

Commit 5e1c999

Browse files
committed
Refactor: Streamline Recorder UI and add discard functionality
This commit simplifies the `RecorderScreen` by removing the `LogFileAdapter` and integrating its logic directly into the Composable. It also introduces a "Discard" option, allowing users to delete the recorded session and its associated files. The `RecorderViewModel` now handles the session path and zip path more robustly, and the UI reflects the working state more accurately. The `onCancelClick` callback in `RecorderScreenHost` has been updated to trigger the discard action in the ViewModel.
1 parent 1c07e8a commit 5e1c999

5 files changed

Lines changed: 33 additions & 103 deletions

File tree

app/src/main/java/eu/darken/butler/common/debug/recorder/core/Recorder.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package eu.darken.butler.common.debug.recorder.core
22

33
import eu.darken.butler.common.debug.logging.FileLogger
44
import eu.darken.butler.common.debug.logging.Logging
5-
import eu.darken.butler.common.debug.logging.Logging.Priority.*
5+
import eu.darken.butler.common.debug.logging.Logging.Priority.INFO
66
import eu.darken.butler.common.debug.logging.log
77
import eu.darken.butler.common.debug.logging.logTag
88
import kotlinx.coroutines.sync.Mutex
@@ -38,7 +38,7 @@ class Recorder @Inject constructor() {
3838
}
3939

4040
companion object {
41-
internal val TAG = logTag("Debug", "Log", "eu.darken.butler.common.debug.recording.core.Recorder")
41+
internal val TAG = logTag("Debug", "Log", "Recorder")
4242
}
4343

4444
}

app/src/main/java/eu/darken/butler/common/debug/recorder/ui/LogFileAdapter.kt

Lines changed: 0 additions & 72 deletions
This file was deleted.

app/src/main/java/eu/darken/butler/common/debug/recorder/ui/RecorderActivity.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ class RecorderActivity : Activity2() {
4747
ErrorEventHandler(vm)
4848
RecorderScreenHost(
4949
viewModel = vm,
50-
onCancelClick = { finish() }
5150
)
5251
}
5352
}

app/src/main/java/eu/darken/butler/common/debug/recorder/ui/RecorderScreen.kt

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ import androidx.compose.foundation.shape.RoundedCornerShape
2727
import androidx.compose.foundation.text.selection.SelectionContainer
2828
import androidx.compose.material.icons.Icons
2929
import androidx.compose.material.icons.automirrored.filled.InsertDriveFile
30-
import androidx.compose.material.icons.twotone.BugReport
3130
import androidx.compose.material.icons.filled.Folder
3231
import androidx.compose.material.icons.outlined.Info
32+
import androidx.compose.material.icons.twotone.BugReport
3333
import androidx.compose.material3.ButtonDefaults
3434
import androidx.compose.material3.CardDefaults
3535
import androidx.compose.material3.CircularProgressIndicator
@@ -67,7 +67,6 @@ import java.io.File
6767
@Composable
6868
fun RecorderScreenHost(
6969
viewModel: RecorderViewModel,
70-
onCancelClick: () -> Unit
7170
) {
7271
val state by viewModel.state.collectAsState(null)
7372
val context = LocalContext.current
@@ -81,7 +80,7 @@ fun RecorderScreenHost(
8180
state?.let { currentState ->
8281
RecorderScreen(
8382
state = currentState,
84-
onCancelClick = onCancelClick,
83+
onCancelClick = { viewModel.discard() },
8584
onShareClick = { viewModel.share() },
8685
onPrivacyPolicyClick = { viewModel.goPrivacyPolicy() }
8786
)
@@ -119,7 +118,7 @@ private fun RecorderScreen(
119118
shadowElevation = 8.dp
120119
) {
121120
ActionButtons(
122-
loading = state.loading,
121+
loading = state.isWorking,
123122
onCancelClick = onCancelClick,
124123
onShareClick = onShareClick,
125124
modifier = Modifier.padding(16.dp)
@@ -161,7 +160,7 @@ private fun RecorderScreen(
161160

162161
item {
163162
AnimatedVisibility(
164-
visible = !state.loading,
163+
visible = !state.isWorking,
165164
enter = fadeIn(animationSpec = tween(300)),
166165
exit = fadeOut(animationSpec = tween(300))
167166
) {
@@ -310,7 +309,7 @@ private fun WarningCard(onPrivacyPolicyClick: () -> Unit) {
310309
}
311310

312311
@Composable
313-
private fun SessionInfoCard(logDir: File) {
312+
private fun SessionInfoCard(logDir: File?) {
314313
ElevatedCard(
315314
modifier = Modifier.fillMaxWidth(),
316315
colors = CardDefaults.elevatedCardColors(
@@ -351,7 +350,7 @@ private fun SessionInfoCard(logDir: File) {
351350
shape = RoundedCornerShape(8.dp)
352351
) {
353352
Text(
354-
text = "${logDir.path}/",
353+
text = logDir?.let { "${it.path}/" } ?: "?",
355354
style = MaterialTheme.typography.bodySmall.copy(
356355
fontFamily = androidx.compose.ui.text.font.FontFamily.Monospace
357356
),

app/src/main/java/eu/darken/butler/common/debug/recorder/ui/RecorderViewModel.kt

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import eu.darken.butler.common.compression.Zipper
1515
import eu.darken.butler.common.coroutine.DispatcherProvider
1616
import eu.darken.butler.common.debug.logging.log
1717
import eu.darken.butler.common.debug.logging.logTag
18+
import eu.darken.butler.common.files.core.local.deleteAll
1819
import eu.darken.butler.common.flow.DynamicStateFlow
1920
import eu.darken.butler.common.flow.SingleEventFlow
2021
import eu.darken.butler.common.navigation.NavigationController
@@ -27,31 +28,29 @@ import javax.inject.Inject
2728
class RecorderViewModel @Inject constructor(
2829
navCtrl: NavigationController,
2930
dispatchers: DispatcherProvider,
30-
savedStateHandle: SavedStateHandle,
31+
handle: SavedStateHandle,
3132
@param:ApplicationContext private val context: Context,
3233
private val webpageTool: WebpageTool,
33-
) : ViewModel4(dispatchers, logTag("Debug", "Recorder","Screen","VM"), navCtrl) {
34+
) : ViewModel4(dispatchers, logTag("Debug", "Recorder", "Screen", "VM"), navCtrl) {
3435

35-
private val recordedPath: File
36+
private val sessionPath = handle.get<String>(RecorderActivity.RECORD_PATH)?.let { File(it) }
37+
private val zipPath = sessionPath?.let { File(it.parentFile, "${it.name}.zip") }
3638

37-
private val stater: DynamicStateFlow<State>
38-
val state: Flow<State>
39+
private val stater = DynamicStateFlow(TAG, vmScope) {
40+
State(logDir = sessionPath)
41+
}
42+
43+
val state: Flow<State> = stater.flow
3944

4045
val shareEvent = SingleEventFlow<Intent>()
4146

4247
init {
43-
val path = savedStateHandle.get<String>(RecorderActivity.RECORD_PATH)
44-
?: throw IllegalStateException("No path provided")
45-
recordedPath = File(path)
48+
launch {
49+
if (sessionPath == null) throw IllegalStateException("No recorded path found")
4650

47-
stater = DynamicStateFlow(TAG, vmScope) {
48-
State(logDir = recordedPath)
49-
}
50-
state = stater.flow
51+
log(TAG) { "Getting log files in dir: $sessionPath" }
52+
val logFiles = sessionPath.listFiles() ?: throw IllegalStateException("No log files found")
5153

52-
launch {
53-
log(TAG) { "Getting log files in dir: $recordedPath" }
54-
val logFiles = recordedPath.listFiles() ?: emptyArray()
5554
log(TAG) { "Found ${logFiles.size} logfiles: $logFiles" }
5655
var entries = logFiles.map { LogFileItem(path = it) }
5756
stater.updateBlocking { copy(logEntries = entries) }
@@ -61,15 +60,15 @@ class RecorderViewModel @Inject constructor(
6160
stater.updateBlocking { copy(logEntries = entries) }
6261

6362
log(TAG) { "Compressing log files..." }
64-
val zipFile = File(recordedPath.parentFile, "${recordedPath.name}.zip")
63+
val zipFile = zipPath ?: throw IllegalStateException("No zip path found")
6564
log(TAG) { "Writing zip file to $zipFile" }
6665
Zipper().zip(
6766
entries.map { it.path.path },
6867
zipFile.path
6968
)
7069
val zippedSize = zipFile.length()
7170
log(TAG) { "Zip file created ${zippedSize}B at $zipFile" }
72-
stater.updateBlocking { copy(compressedFile = zipFile, compressedSize = zippedSize) }
71+
stater.updateBlocking { copy(compressedFile = zipFile, compressedSize = zippedSize, isWorking = false) }
7372
}
7473
}
7574

@@ -105,14 +104,19 @@ class RecorderViewModel @Inject constructor(
105104
webpageTool.open(ButlerLinks.PRIVACY_POLICY)
106105
}
107106

107+
fun discard() = launch {
108+
stater.updateBlocking { copy(isWorking = true) }
109+
sessionPath?.deleteAll()
110+
navUp()
111+
}
112+
108113
data class State(
109-
val logDir: File,
114+
val logDir: File?,
110115
val logEntries: List<LogFileItem> = emptyList(),
111116
val compressedFile: File? = null,
112-
val compressedSize: Long? = null
117+
val compressedSize: Long? = null,
118+
val isWorking: Boolean = true,
113119
) {
114-
val loading: Boolean
115-
get() = compressedSize == null
116120

117121
fun getFormattedCompressedSize(context: Context): String? {
118122
return compressedSize?.let { Formatter.formatShortFileSize(context, it) }

0 commit comments

Comments
 (0)