Skip to content

Commit 23fc684

Browse files
committed
Merge branch 'worktree-explorer'
2 parents 13c1175 + 3ba9e6b commit 23fc684

9 files changed

Lines changed: 163 additions & 131 deletions

File tree

app-workspace-explorer/src/main/java/eu/darken/butler/explorer/core/engine/DeviceLocationLoader.kt

Lines changed: 17 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@ import androidx.compose.material.icons.twotone.Code
66
import androidx.compose.material.icons.twotone.FolderShared
77
import androidx.compose.material.icons.twotone.SdCard
88
import androidx.compose.material.icons.twotone.Storage
9-
import eu.darken.butler.common.ca.caString
109
import eu.darken.butler.common.ca.toCaString
1110
import eu.darken.butler.common.debug.logging.Logging.Priority.*
1211
import eu.darken.butler.common.debug.logging.log
1312
import eu.darken.butler.common.debug.logging.logTag
14-
import eu.darken.butler.common.files.APath
1513
import eu.darken.butler.common.files.GatewaySwitch
1614
import eu.darken.butler.common.files.LocalPath
1715
import eu.darken.butler.common.files.extensions.getFileSystemInfo
@@ -25,7 +23,6 @@ import eu.darken.butler.workspace.core.permissions.PermissionState
2523
import kotlinx.coroutines.currentCoroutineContext
2624
import kotlinx.coroutines.ensureActive
2725
import kotlinx.coroutines.flow.Flow
28-
import kotlin.coroutines.coroutineContext
2926
import kotlinx.coroutines.flow.first
3027
import kotlinx.coroutines.flow.flow
3128
import javax.inject.Inject
@@ -41,36 +38,6 @@ class DeviceLocationLoader @Inject constructor(
4138

4239
private val tag = logTag("Explorer", "DeviceLocationLoader")
4340

44-
private class LoaderContext(
45-
private val permissionState: PermissionState,
46-
private val emit: suspend (ExplorerLocation.Device) -> Unit,
47-
) {
48-
private var currentState = ExplorerLocation.Device(
49-
permissionState = permissionState,
50-
progress = Progress.Data(
51-
primary = R.string.explorer_loader_progress_device_loading.toCaString(),
52-
),
53-
)
54-
val state: ExplorerLocation.Device get() = currentState
55-
56-
suspend fun updateState(transform: ExplorerLocation.Device.() -> ExplorerLocation.Device) {
57-
currentState = currentState.transform()
58-
emit(currentState)
59-
}
60-
61-
suspend fun updateProgressMsg(@StringRes msg: Int) = updateState {
62-
copy(
63-
progress = currentState.progress!!.copy(
64-
secondary = msg.toCaString(),
65-
),
66-
)
67-
}
68-
69-
suspend fun emitState() {
70-
emit(currentState)
71-
}
72-
}
73-
7441
private suspend fun checkLocationPermissions(): PermissionState {
7542
log(tag) { "checkLocationPermissions(): Checking permissions for Device" }
7643

@@ -85,22 +52,29 @@ class DeviceLocationLoader @Inject constructor(
8552
log(tag, INFO) { "loadDevice(): Loading device location with multi-stage loading" }
8653

8754
val permissionState = checkLocationPermissions()
88-
val context = LoaderContext(permissionState, ::emit)
55+
val context = LocationLoaderContext(
56+
initialState = ExplorerLocation.Device(
57+
permissionState = permissionState,
58+
progress = Progress.Data(
59+
primary = R.string.explorer_loader_progress_device_loading.toCaString(),
60+
),
61+
),
62+
emit = ::emit
63+
)
8964
context.emitState()
9065

91-
context.loadQuickList()
66+
gatewaySwitch.useRes {
67+
context.loadQuickList()
68+
context.updateState { copy(progress = null) }
69+
log(tag, INFO) { "loadDevice(): Stage 1 complete with ${context.state.items?.size} storage locations" }
9270

93-
// Clear progress after Stage 1 - flow can complete, navigation can proceed
94-
context.updateState { copy(progress = null) }
95-
log(tag, INFO) { "loadDevice(): Stage 1 complete with ${context.state.items?.size} storage locations" }
96-
97-
// Stage 2: Load filesystem info (can be cancelled without blocking)
98-
context.loadFilesystemInfo()
71+
context.loadFilesystemInfo()
72+
}
9973

10074
log(tag, INFO) { "loadDevice(): Stage 2 complete with filesystem info" }
10175
}
10276

103-
private suspend fun LoaderContext.loadQuickList() {
77+
private suspend fun LocationLoaderContext<ExplorerLocation.Device>.loadQuickList() {
10478
log(tag) { "loadQuickList(): Loading storage list without filesystem info" }
10579
updateProgressMsg(R.string.explorer_loader_progress_device_locations)
10680

@@ -182,7 +156,7 @@ class DeviceLocationLoader @Inject constructor(
182156
null
183157
}
184158

185-
private suspend fun LoaderContext.loadFilesystemInfo() {
159+
private suspend fun LocationLoaderContext<ExplorerLocation.Device>.loadFilesystemInfo() {
186160
log(tag) { "loadFilesystemInfo(): Loading filesystem info sequentially with incremental updates" }
187161

188162
val currentItems = state.items ?: return

app-workspace-explorer/src/main/java/eu/darken/butler/explorer/core/engine/DirectoryLocationLoader.kt

Lines changed: 32 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import eu.darken.butler.common.storage.StorageEnvironment
2424
import eu.darken.butler.explorer.R
2525
import eu.darken.butler.workspace.core.Workspace
2626
import eu.darken.butler.workspace.core.permissions.PathPermissionCheck
27+
import kotlin.coroutines.coroutineContext
28+
import kotlinx.coroutines.ensureActive
2729
import kotlinx.coroutines.flow.Flow
2830
import kotlinx.coroutines.flow.flatMapLatest
2931
import kotlinx.coroutines.flow.flow
@@ -38,50 +40,28 @@ class DirectoryLocationLoader @AssistedInject constructor(
3840

3941
private val tag = logTag("Explorer", "Workspace", workspaceId.shortTag, "DirectoryLoader")
4042

41-
private class LoaderContext(
42-
private val path: APath<*>,
43-
private val permissionState: eu.darken.butler.workspace.core.permissions.PermissionState,
44-
private val emit: suspend (ExplorerLocation.Directory) -> Unit,
45-
) {
46-
private var currentState = ExplorerLocation.Directory(
47-
path = path,
48-
permissionState = permissionState,
49-
progress = Progress.Data(
50-
primary = caString {
51-
it.getString(
52-
R.string.explorer_loader_progress_directory_loading,
53-
path.userReadablePath.get(it)
54-
)
55-
},
56-
)
57-
)
58-
val state: ExplorerLocation.Directory get() = currentState
59-
60-
suspend fun updateState(transform: ExplorerLocation.Directory.() -> ExplorerLocation.Directory) {
61-
currentState = currentState.transform()
62-
emit(currentState)
63-
}
64-
65-
suspend fun updateProgressMsg(@StringRes msg: Int) = updateState {
66-
copy(
67-
progress = currentState.progress?.copy(
68-
secondary = msg.toCaString()
69-
)
70-
)
71-
}
72-
73-
suspend fun emitState() {
74-
emit(currentState)
75-
}
76-
77-
val targetPath: APath<*> get() = currentState.path
78-
}
43+
private val LocationLoaderContext<ExplorerLocation.Directory>.targetPath: APath<*>
44+
get() = state.path
7945

8046
fun loadDirectory(path: APath<*>): Flow<ExplorerLocation> {
8147
return pathPermissionCheck.monitor(path).flatMapLatest { permissionState ->
8248
flow {
8349
log(tag, INFO) { "loadDirectory(): Loading directory with permission state: $permissionState" }
84-
val context = LoaderContext(path, permissionState, ::emit)
50+
val context = LocationLoaderContext(
51+
initialState = ExplorerLocation.Directory(
52+
path = path,
53+
permissionState = permissionState,
54+
progress = Progress.Data(
55+
primary = caString {
56+
it.getString(
57+
R.string.explorer_loader_progress_directory_loading,
58+
path.userReadablePath.get(it)
59+
)
60+
},
61+
)
62+
),
63+
emit = ::emit
64+
)
8565
context.emitState()
8666

8767
context.updateProgressMsg(R.string.explorer_loader_progress_directory_permissions)
@@ -91,20 +71,19 @@ class DirectoryLocationLoader @AssistedInject constructor(
9171
return@flow
9272
}
9373

94-
context.loadFileSystemInfo()
95-
96-
context.loadPeek()
97-
98-
context.loadContent()
99-
100-
context.loadContentExtended()
74+
gatewaySwitch.useRes {
75+
context.loadFileSystemInfo()
76+
context.loadPeek()
77+
context.loadContent()
78+
context.loadContentExtended()
79+
}
10180

10281
context.updateState { copy(progress = null) }
10382
}
10483
}
10584
}
10685

107-
private suspend fun LoaderContext.loadFileSystemInfo() {
86+
private suspend fun LocationLoaderContext<ExplorerLocation.Directory>.loadFileSystemInfo() {
10887
log(tag) { "loadFileSystemInfo(): Loading file system info for $targetPath" }
10988

11089
updateProgressMsg(R.string.explorer_loader_progress_directory_filesystem)
@@ -120,7 +99,7 @@ class DirectoryLocationLoader @AssistedInject constructor(
12099
}
121100
}
122101

123-
private suspend fun LoaderContext.loadPeek() {
102+
private suspend fun LocationLoaderContext<ExplorerLocation.Directory>.loadPeek() {
124103
log(tag) { "loadPeek(): Loading peek for $targetPath" }
125104
updateProgressMsg(R.string.explorer_loader_progress_directory_content)
126105

@@ -139,7 +118,7 @@ class DirectoryLocationLoader @AssistedInject constructor(
139118
}
140119
}
141120

142-
private suspend fun LoaderContext.loadContent() {
121+
private suspend fun LocationLoaderContext<ExplorerLocation.Directory>.loadContent() {
143122
log(tag) { "loadContent(): Loading content: $targetPath" }
144123
updateProgressMsg(R.string.explorer_loader_progress_directory_content_details)
145124

@@ -193,7 +172,7 @@ class DirectoryLocationLoader @AssistedInject constructor(
193172
}
194173
}
195174

196-
private suspend fun LoaderContext.loadContentExtended() {
175+
private suspend fun LocationLoaderContext<ExplorerLocation.Directory>.loadContentExtended() {
197176
log(tag) { "loadContentExtended(): Loading content extended: $targetPath" }
198177
updateProgressMsg(R.string.explorer_loader_progress_directory_content_extended)
199178

@@ -214,6 +193,9 @@ class DirectoryLocationLoader @AssistedInject constructor(
214193
// Count children for directories
215194
val childCounts = mutableMapOf<String, Int>()
216195
extendedLookups.values.forEach { lookup ->
196+
// Check if cancelled before processing next item
197+
coroutineContext.ensureActive()
198+
217199
if (lookup.fileType == FileType.DIRECTORY) {
218200
try {
219201
val children = gatewaySwitch.listFiles(lookup.lookedUp)

app-workspace-explorer/src/main/java/eu/darken/butler/explorer/core/engine/ExplorerLocation.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import eu.darken.butler.explorer.core.ExplorerNavigation
66
import eu.darken.butler.workspace.core.permissions.PermissionState
77

88
sealed interface ExplorerLocation {
9+
val locationId: String
910
val items: List<ExplorerItem>?
1011
val info: LocationInfo?
1112
val permissionState: PermissionState
@@ -21,6 +22,9 @@ sealed interface ExplorerLocation {
2122
override val permissionState: PermissionState = PermissionState(),
2223
override val progress: Progress.Data? = Progress.Data(),
2324
) : ExplorerLocation {
25+
26+
override val locationId: String get() = "location://home"
27+
2428
data class Info(
2529
val shortcutCount: Int,
2630
val totalDeviceStorage: Long? = null,
@@ -34,6 +38,9 @@ sealed interface ExplorerLocation {
3438
override val permissionState: PermissionState = PermissionState(),
3539
override val progress: Progress.Data? = Progress.Data(),
3640
) : ExplorerLocation {
41+
42+
override val locationId: String get() = "location://device"
43+
3744
data class Info(
3845
val locationCount: Int,
3946
val totalCapacity: Long? = null,
@@ -49,6 +56,9 @@ sealed interface ExplorerLocation {
4956
val path: APath<*>,
5057
val parent: ExplorerNavigation.Target? = null,
5158
) : ExplorerLocation {
59+
60+
override val locationId: String get() = "location://directory/${path.path}"
61+
5262
data class Info(
5363
val fileCount: Int? = null,
5464
val directoryCount: Int? = null,

app-workspace-explorer/src/main/java/eu/darken/butler/explorer/core/engine/ExplorerLocationExtensions.kt

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

app-workspace-explorer/src/main/java/eu/darken/butler/explorer/core/engine/HomeLocationLoader.kt

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import androidx.compose.material.icons.Icons
77
import androidx.compose.material.icons.twotone.PhoneAndroid
88
import dagger.hilt.android.qualifiers.ApplicationContext
99
import eu.darken.butler.common.ca.toCaString
10+
import eu.darken.butler.common.debug.logging.Logging.Priority.*
1011
import eu.darken.butler.common.debug.logging.log
1112
import eu.darken.butler.common.debug.logging.logTag
13+
import eu.darken.butler.common.progress.Progress
1214
import eu.darken.butler.explorer.R
1315
import eu.darken.butler.explorer.core.ExplorerNavigation
1416
import eu.darken.butler.workspace.core.permissions.PermissionState
@@ -35,10 +37,19 @@ class HomeLocationLoader @Inject constructor(
3537
}
3638

3739
fun loadHome(): Flow<ExplorerLocation> = flow {
38-
log(tag) { "loadHome(): Loading home location" }
40+
log(tag, INFO) { "loadHome(): Loading home location" }
3941

40-
var result = ExplorerLocation.Home()
41-
emit(result)
42+
val permissionState = checkLocationPermissions()
43+
val context = LocationLoaderContext(
44+
initialState = ExplorerLocation.Home(
45+
permissionState = permissionState,
46+
progress = Progress.Data(
47+
primary = R.string.explorer_loader_progress_home_loading.toCaString(),
48+
),
49+
),
50+
emit = ::emit
51+
)
52+
context.emitState()
4253

4354
val shortcuts = listOf(
4455
ExplorerItem.Shortcut(
@@ -53,7 +64,7 @@ class HomeLocationLoader @Inject constructor(
5364
val stat = try {
5465
StatFs(Environment.getDataDirectory().path)
5566
} catch (e: Exception) {
56-
log(tag) { "loadHome(): Failed to get storage info: ${e.message}" }
67+
log(tag, WARN) { "loadHome(): Failed to get storage info: ${e.message}" }
5768
null
5869
}
5970

@@ -63,15 +74,14 @@ class HomeLocationLoader @Inject constructor(
6374
usedStorage = stat?.let { it.totalBytes - it.availableBytes },
6475
)
6576

66-
log(tag) { "loadHome(): Created home with ${shortcuts.size} shortcuts" }
67-
68-
result = ExplorerLocation.Home(
69-
items = shortcuts,
70-
info = info,
71-
permissionState = checkLocationPermissions(),
72-
progress = null,
73-
)
74-
emit(result)
77+
log(tag, INFO) { "loadHome(): Created home with ${shortcuts.size} shortcuts" }
7578

79+
context.updateState {
80+
copy(
81+
items = shortcuts,
82+
info = info,
83+
progress = null,
84+
)
85+
}
7686
}
7787
}

0 commit comments

Comments
 (0)