summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@spawned.biz>2024-08-25 01:28:08 +0200
committerJoel Klinghed <the_jk@spawned.biz>2024-08-25 01:28:08 +0200
commit157853ea0839fac4e066c012c21a4900aaf4c30f (patch)
tree716215093c68c27e606670a58ad1c9cd56d26341 /libs
parent5b0bd926c6c8a1d3f65acd170b56c582abcee488 (diff)
samba: Add support for live list
Using polling (every 10s) as libsmb2 has no watch/event support. (Unsure if SMB protocol has any support).
Diffstat (limited to 'libs')
-rw-r--r--libs/samba/build.gradle.kts1
-rw-r--r--libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaConnection.kt2
-rw-r--r--libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaDirectory.kt54
-rw-r--r--libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaFile.kt4
-rw-r--r--libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaLink.kt4
-rw-r--r--libs/samba/src/test/java/org/the_jk/cleversync/samba/SambaTreeTest.kt51
6 files changed, 111 insertions, 5 deletions
diff --git a/libs/samba/build.gradle.kts b/libs/samba/build.gradle.kts
index b7816c4..7fab391 100644
--- a/libs/samba/build.gradle.kts
+++ b/libs/samba/build.gradle.kts
@@ -46,6 +46,7 @@ android {
dependencies {
implementation(project(":libs:io"))
+ testImplementation(project(":libs:utils"))
}
listOf("Debug", "Release").forEach { buildType ->
diff --git a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaConnection.kt b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaConnection.kt
index 6eda092..a645804 100644
--- a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaConnection.kt
+++ b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaConnection.kt
@@ -43,6 +43,8 @@ internal class SambaConnection(uri: String, credentials: SambaCredentials) {
fun openFile(path: String, read: NativeSamba.OpenMode): NativeSamba.File? =
if (connected) context.openFile(join(url!!.path(), path), read) else null
+ override fun toString() = description
+
companion object {
fun join(a: String, b: String): String {
if (a.isEmpty() || b.startsWith("/")) return b
diff --git a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaDirectory.kt b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaDirectory.kt
index d9ec6aa..f5230b2 100644
--- a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaDirectory.kt
+++ b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaDirectory.kt
@@ -1,6 +1,10 @@
package org.the_jk.cleversync.io.samba
+import android.os.Handler
+import android.os.Looper
import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.map
import org.the_jk.cleversync.io.Directory
import org.the_jk.cleversync.io.File
import org.the_jk.cleversync.io.ModifiableDirectory
@@ -8,12 +12,48 @@ import org.the_jk.cleversync.io.ModifiableFile
import org.the_jk.cleversync.io.ModifiableLink
import java.io.IOException
import java.time.Instant
+import kotlin.time.Duration.Companion.seconds
internal open class SambaDirectory(
private val conn: SambaConnection,
private val path: String,
override val name: String,
) : ModifiableDirectory {
+ private val modifiableLiveContent = object : MutableLiveData<ModifiableDirectory.Content>() {
+ private val looper = Looper.myLooper()
+ private val handler = if (looper != null) Handler(looper) else null
+ private val updateCallback = Runnable { update() }
+
+ override fun onActive() {
+ super.onActive()
+
+ value = modifiableList()
+ handler?.postDelayed(updateCallback, LIVE_UPDATE_INTERVAL.inWholeMilliseconds)
+ }
+
+ override fun onInactive() {
+ super.onInactive()
+
+ handler?.removeCallbacks(updateCallback)
+ }
+
+ private fun update() {
+ val newValue = modifiableList()
+ if (value != newValue) postValue(newValue)
+ handler?.postDelayed(updateCallback, LIVE_UPDATE_INTERVAL.inWholeMilliseconds)
+ }
+ }
+
+ private val liveContent: LiveData<Directory.Content> by lazy {
+ modifiableLiveContent.map {
+ Directory.Content(
+ it.directories,
+ it.files,
+ it.links,
+ )
+ }
+ }
+
override fun modifiableOpenDir(name: String): ModifiableDirectory? {
val newPath = SambaConnection.join(path, name)
val entry = conn.entry(newPath) ?: return null
@@ -60,9 +100,7 @@ internal open class SambaDirectory(
return ModifiableDirectory.Content(directories, files, links)
}
- override fun modifiableLiveList(): LiveData<ModifiableDirectory.Content> {
- TODO("Not yet implemented")
- }
+ override fun modifiableLiveList() = modifiableLiveContent
override fun createDirectory(name: String): ModifiableDirectory {
val newPath = SambaConnection.join(path, name)
@@ -109,7 +147,13 @@ internal open class SambaDirectory(
Directory.Content(directories, files, links)
}
- override fun liveList(): LiveData<Directory.Content> {
- TODO("Not yet implemented")
+ override fun liveList() = liveContent
+
+ override fun equals(other: Any?) = other is SambaDirectory && other.conn == conn && other.path == path
+ override fun hashCode() = path.hashCode()
+ override fun toString() = "$conn/$path"
+
+ private companion object {
+ private val LIVE_UPDATE_INTERVAL = 10.seconds
}
}
diff --git a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaFile.kt b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaFile.kt
index 9913035..c8bb98d 100644
--- a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaFile.kt
+++ b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaFile.kt
@@ -119,6 +119,10 @@ internal class SambaFile(
return entry.lastModified
}
+ override fun equals(other: Any?) = other is SambaFile && other.conn == conn && other.path == path
+ override fun hashCode() = path.hashCode()
+ override fun toString() = "$conn/$path"
+
private fun useCached(): Boolean {
return Instant.now().isBefore(cacheEndOfLife)
}
diff --git a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaLink.kt b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaLink.kt
index 2d0d080..a9e6f04 100644
--- a/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaLink.kt
+++ b/libs/samba/src/main/java/org/the_jk/cleversync/io/samba/SambaLink.kt
@@ -51,6 +51,10 @@ internal class SambaLink(
}
}
+ override fun equals(other: Any?) = other is SambaLink && other.conn == conn && other.path == path
+ override fun hashCode() = path.hashCode()
+ override fun toString() = "$conn/$path"
+
private fun doResolve(): Pair<String, NativeSamba.DirEntry?> {
var linkPath = path
var entry: NativeSamba.DirEntry? = null
diff --git a/libs/samba/src/test/java/org/the_jk/cleversync/samba/SambaTreeTest.kt b/libs/samba/src/test/java/org/the_jk/cleversync/samba/SambaTreeTest.kt
index d2a2e04..b0ecf03 100644
--- a/libs/samba/src/test/java/org/the_jk/cleversync/samba/SambaTreeTest.kt
+++ b/libs/samba/src/test/java/org/the_jk/cleversync/samba/SambaTreeTest.kt
@@ -11,10 +11,15 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
+import org.robolectric.shadows.ShadowLooper
+import org.the_jk.cleversync.io.Directory
import org.the_jk.cleversync.io.samba.SambaCredentials
+import org.the_jk.cleversync.safeValue
import java.io.File
import java.nio.charset.StandardCharsets
import java.nio.file.Files
+import java.util.concurrent.TimeUnit
+import kotlin.time.Duration.Companion.seconds
@Config(manifest=Config.NONE)
@RunWith(RobolectricTestRunner::class)
@@ -54,6 +59,16 @@ class SambaTreeTest {
}
@Test
+ fun listEmptyRootLive() {
+ SambaTreeFactory.tree(uri, credentials).getOrThrow().use { root ->
+ val content = root.liveList().safeValue()
+ assertThat(content?.directories).isEmpty()
+ assertThat(content?.files).isEmpty()
+ assertThat(content?.links).isEmpty()
+ }
+ }
+
+ @Test
fun listRootWithSymlink() {
File(shareDir, "dir").mkdir()
File(shareDir, "file").writeText("foo")
@@ -148,6 +163,42 @@ class SambaTreeTest {
assertThat(File(shareDir, "file").readText()).isEqualTo("foobar")
}
+ @Test
+ fun createDirectory() {
+ SambaTreeFactory.modifiableTree(uri, credentials).getOrThrow().use { root ->
+ val foo = root.createDirectory("foo")
+ assertThat(foo.name).isEqualTo("foo")
+ val fooContent = foo.list()
+ assertThat(fooContent.directories).isEmpty()
+ assertThat(fooContent.files).isEmpty()
+ assertThat(fooContent.links).isEmpty()
+ val content = root.list()
+ assertThat(content.directories).contains(foo)
+ assertThat(content.files).isEmpty()
+ assertThat(content.links).isEmpty()
+ }
+
+ assertThat(File(shareDir, "foo").isDirectory).isTrue()
+ }
+
+ @Test(timeout = 10000)
+ fun observeCreateDirectory() {
+ SambaTreeFactory.modifiableTree(uri, credentials).getOrThrow().use { root ->
+ val content = root.liveList()
+ var dir: Directory? = null
+ content.observeForever {
+ if (it.directories.size == 1) dir = it.directories[0]
+ }
+ root.createDirectory("foo")
+ while (dir == null) {
+ ShadowLooper.idleMainLooper(10, TimeUnit.SECONDS)
+ }
+ assertThat(dir?.name).isEqualTo("foo")
+ }
+
+ assertThat(File(shareDir, "foo").isDirectory).isTrue()
+ }
+
companion object {
private lateinit var uri: String
private lateinit var credentials: SambaCredentials