Native Kotlin Android app (single Activity + WorkManager worker) that copies photos newer than a stored cursor to //192.168.123.2/photo/Home as decade/year/month, deduping by filename and NAS file size. - SMB2/3 via smbj; transparently reconnects when the server drops an idle session (previously surfaced as "DiskShare has already been closed") - MediaStore cursor store, incremental uploads, size-aware dedupe - "Phone files" / "NAS files" browser: grid, thumbnails, full-screen preview - Release signing read from keystore.properties (never committed)
64 lines
2.0 KiB
Kotlin
64 lines
2.0 KiB
Kotlin
package com.bm.andpictures
|
|
|
|
import java.io.InputStream
|
|
|
|
/**
|
|
* Browses an SMB share starting at [rootKey]. Keys are share-relative paths
|
|
* using backslashes (matching [SmbUploader]); an empty key means the share
|
|
* root.
|
|
*/
|
|
class NasFileSource(
|
|
private val uploader: SmbUploader,
|
|
private val hostAndShare: String,
|
|
override val rootKey: String,
|
|
) : FileSource {
|
|
|
|
override val title: String get() = "NAS"
|
|
|
|
override fun list(dirKey: String): List<TileEntry> {
|
|
val entries = uploader.listEntries(dirKey)
|
|
val dirs = entries.asSequence()
|
|
.filter { it.isDirectory }
|
|
.sortedBy { it.name.lowercase() }
|
|
.map { TileEntry(it.name, joinKey(dirKey, it.name), isDirectory = true, sizeBytes = 0L, isImage = false) }
|
|
.toList()
|
|
val files = entries.asSequence()
|
|
.filter { !it.isDirectory }
|
|
.sortedBy { it.name.lowercase() }
|
|
.map {
|
|
TileEntry(
|
|
it.name,
|
|
joinKey(dirKey, it.name),
|
|
isDirectory = false,
|
|
sizeBytes = it.sizeBytes,
|
|
isImage = isImageName(it.name),
|
|
)
|
|
}
|
|
.toList()
|
|
return dirs + files
|
|
}
|
|
|
|
override fun parentOf(dirKey: String): String? {
|
|
val norm = dirKey.trim('\\', '/')
|
|
if (norm.isEmpty() || norm == rootKey.trim('\\', '/')) return null
|
|
val parent = norm.substringBeforeLast('\\', "")
|
|
return parent
|
|
}
|
|
|
|
override fun displayName(dirKey: String): String {
|
|
val norm = dirKey.trim('\\', '/').replace('\\', '/')
|
|
return if (norm.isEmpty()) "//$hostAndShare" else "//$hostAndShare/$norm"
|
|
}
|
|
|
|
override fun openStream(key: String): InputStream = uploader.openRead(key)
|
|
|
|
override fun close() {
|
|
uploader.close()
|
|
}
|
|
|
|
private fun joinKey(dir: String, name: String): String {
|
|
val d = dir.trim('\\', '/')
|
|
return if (d.isEmpty()) name else "$d\\$name"
|
|
}
|
|
}
|