summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/.gitignore1
-rw-r--r--app/build.gradle.kts34
-rw-r--r--app/src/main/java/org/the_jk/cleversync/LiveDataUtils.kt14
-rw-r--r--app/src/main/java/org/the_jk/cleversync/StringUtils.kt32
-rw-r--r--app/src/main/java/org/the_jk/cleversync/io/Directory.kt20
-rw-r--r--app/src/main/java/org/the_jk/cleversync/io/File.kt12
-rw-r--r--app/src/main/java/org/the_jk/cleversync/io/Link.kt13
-rw-r--r--app/src/main/java/org/the_jk/cleversync/io/ModifiableDirectory.kt28
-rw-r--r--app/src/main/java/org/the_jk/cleversync/io/ModifiableFile.kt7
-rw-r--r--app/src/main/java/org/the_jk/cleversync/io/ModifiableLink.kt15
-rw-r--r--app/src/main/java/org/the_jk/cleversync/io/ModifiableTree.kt3
-rw-r--r--app/src/main/java/org/the_jk/cleversync/io/Tree.kt7
-rw-r--r--app/src/main/java/org/the_jk/cleversync/io/TreeFactory.kt8
-rw-r--r--app/src/main/java/org/the_jk/cleversync/io/impl/PathDirectory.kt188
-rw-r--r--app/src/main/java/org/the_jk/cleversync/io/impl/PathFile.kt66
-rw-r--r--app/src/main/java/org/the_jk/cleversync/io/impl/PathLink.kt62
-rw-r--r--app/src/main/java/org/the_jk/cleversync/io/impl/PathTree.kt10
-rw-r--r--app/src/main/java/org/the_jk/cleversync/io/impl/PathWatcher.kt82
-rw-r--r--app/src/test/java/org/the_jk/cleversync/StringUtilsTest.kt40
-rw-r--r--app/src/test/java/org/the_jk/cleversync/io/LocalTreeTest.kt323
20 files changed, 9 insertions, 956 deletions
diff --git a/app/.gitignore b/app/.gitignore
deleted file mode 100644
index 42afabf..0000000
--- a/app/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build \ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index d432519..ea29544 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,21 +1,14 @@
plugins {
alias(libs.plugins.android.application)
- alias(libs.plugins.arturbosch.detekt)
- alias(libs.plugins.jetbrains.kotlin.android)
}
android {
namespace = "org.the_jk.cleversync"
- compileSdk = 34
defaultConfig {
applicationId = "org.the_jk.cleversync"
- minSdk = 29
- targetSdk = 34
versionCode = 1
versionName = "0.1"
-
- testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -27,41 +20,22 @@ android {
)
}
}
- compileOptions {
- sourceCompatibility = JavaVersion.VERSION_11
- targetCompatibility = JavaVersion.VERSION_11
- }
- kotlinOptions {
- jvmTarget = "11"
- }
buildFeatures {
viewBinding = true
}
}
dependencies {
+ implementation(project(":libs:local"))
+ implementation(project(":libs:io"))
+ implementation(project(":libs:samba"))
+ implementation(project(":libs:utils"))
implementation(libs.androidx.appcompat)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.core.ktx)
- implementation(libs.androidx.livedata)
- implementation(libs.androidx.livedata.ktx)
implementation(libs.androidx.navigation.fragment.ktx)
implementation(libs.androidx.navigation.ui.ktx)
implementation(libs.material)
- testImplementation(libs.junit)
- testImplementation(libs.robolectric)
- testImplementation(libs.truth)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
}
-
-detekt {
- config.setFrom(rootProject.file("detekt.yaml"))
-}
-
-tasks.withType<io.gitlab.arturbosch.detekt.Detekt>().configureEach {
- this.jvmTarget = "11"
-}
-tasks.withType<io.gitlab.arturbosch.detekt.DetektCreateBaselineTask>().configureEach {
- this.jvmTarget = "11"
-}
diff --git a/app/src/main/java/org/the_jk/cleversync/LiveDataUtils.kt b/app/src/main/java/org/the_jk/cleversync/LiveDataUtils.kt
deleted file mode 100644
index 7f6ab1f..0000000
--- a/app/src/main/java/org/the_jk/cleversync/LiveDataUtils.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.the_jk.cleversync
-
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.Observer
-
-fun <T> LiveData<T>.safeValue(): T? {
- if (this.hasActiveObservers())
- return value
- var ret: T? = null
- val observer = Observer<T> { value -> ret = value }
- this.observeForever(observer)
- this.removeObserver(observer)
- return ret
-}
diff --git a/app/src/main/java/org/the_jk/cleversync/StringUtils.kt b/app/src/main/java/org/the_jk/cleversync/StringUtils.kt
deleted file mode 100644
index 6adea24..0000000
--- a/app/src/main/java/org/the_jk/cleversync/StringUtils.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.the_jk.cleversync
-
-object StringUtils {
- fun split(input: String, delimiter: Char, keepEmpty: Boolean = true, limit: Int = 0): List<String> {
- return buildList {
- var offset = 0
- var count = 0
- while (true) {
- val next = input.indexOf(delimiter, offset)
- if (next == -1) {
- if (keepEmpty || offset < input.length) {
- if (limit > 0 && count == limit) {
- add("${removeLast()}${delimiter}${input.substring(offset)}")
- break
- }
- add(input.substring(offset))
- }
- break
- }
- if (keepEmpty || offset < next) {
- if (limit > 0 && count == limit) {
- add("${removeLast()}${delimiter}${input.substring(offset)}")
- break
- }
- add(input.substring(offset, next))
- count++
- }
- offset = next + 1
- }
- }
- }
-}
diff --git a/app/src/main/java/org/the_jk/cleversync/io/Directory.kt b/app/src/main/java/org/the_jk/cleversync/io/Directory.kt
deleted file mode 100644
index e653059..0000000
--- a/app/src/main/java/org/the_jk/cleversync/io/Directory.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.the_jk.cleversync.io
-
-import androidx.lifecycle.LiveData
-
-interface Directory {
- val name: String
-
- fun openDir(name: String): Directory?
- fun openFile(name: String): File?
- fun openLink(name: String): Link?
-
- fun list(): Content
- fun liveList(): LiveData<Content>
-
- data class Content(
- val directories: List<Directory>,
- val files: List<File>,
- val links: List<Link>,
- )
-}
diff --git a/app/src/main/java/org/the_jk/cleversync/io/File.kt b/app/src/main/java/org/the_jk/cleversync/io/File.kt
deleted file mode 100644
index 17f142a..0000000
--- a/app/src/main/java/org/the_jk/cleversync/io/File.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package org.the_jk.cleversync.io
-
-import java.io.InputStream
-import java.time.Instant
-
-interface File {
- val name: String
- val size: ULong
- val lastModified: Instant
-
- fun read(): InputStream
-}
diff --git a/app/src/main/java/org/the_jk/cleversync/io/Link.kt b/app/src/main/java/org/the_jk/cleversync/io/Link.kt
deleted file mode 100644
index c05f29e..0000000
--- a/app/src/main/java/org/the_jk/cleversync/io/Link.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.the_jk.cleversync.io
-
-interface Link {
- val name: String
-
- fun resolve(): LinkTarget
-
- sealed class LinkTarget
-
- data class DirectoryTarget(val directory: Directory): LinkTarget()
- data class FileTarget(val file: File): LinkTarget()
- data object NoTarget: LinkTarget()
-}
diff --git a/app/src/main/java/org/the_jk/cleversync/io/ModifiableDirectory.kt b/app/src/main/java/org/the_jk/cleversync/io/ModifiableDirectory.kt
deleted file mode 100644
index 8bddc2c..0000000
--- a/app/src/main/java/org/the_jk/cleversync/io/ModifiableDirectory.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.the_jk.cleversync.io
-
-import androidx.lifecycle.LiveData
-
-interface ModifiableDirectory : Directory {
- fun modifiableOpenDir(name: String): ModifiableDirectory?
- fun modifiableOpenFile(name: String): ModifiableFile?
- fun modifiableOpenLink(name: String): ModifiableLink?
-
- fun modifiableList(): Content
- fun modifiableLiveList(): LiveData<Content>
-
- fun createDirectory(name: String): ModifiableDirectory
- fun createFile(name: String): ModifiableFile
- fun createLink(name: String, target: Directory): ModifiableLink
- fun createLink(name: String, target: File): ModifiableLink
- fun createLink(name: String, target: String): ModifiableLink
-
- fun removeDirectory(name: String): Boolean
- fun removeFile(name: String): Boolean
- fun removeLink(name: String): Boolean
-
- data class Content(
- val directories: List<ModifiableDirectory>,
- val files: List<ModifiableFile>,
- val links: List<ModifiableLink>,
- )
-}
diff --git a/app/src/main/java/org/the_jk/cleversync/io/ModifiableFile.kt b/app/src/main/java/org/the_jk/cleversync/io/ModifiableFile.kt
deleted file mode 100644
index 8675dae..0000000
--- a/app/src/main/java/org/the_jk/cleversync/io/ModifiableFile.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.the_jk.cleversync.io
-
-import java.io.OutputStream
-
-interface ModifiableFile : File {
- fun write(): OutputStream
-}
diff --git a/app/src/main/java/org/the_jk/cleversync/io/ModifiableLink.kt b/app/src/main/java/org/the_jk/cleversync/io/ModifiableLink.kt
deleted file mode 100644
index 7dd565b..0000000
--- a/app/src/main/java/org/the_jk/cleversync/io/ModifiableLink.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.the_jk.cleversync.io
-
-interface ModifiableLink : Link {
- fun modifiableResolve(): ModifiableLinkTarget
-
- fun target(directory: Directory)
- fun target(file: File)
- fun target(name: String)
-
- sealed class ModifiableLinkTarget
-
- data class ModifiableDirectoryTarget(val directory: ModifiableDirectory): ModifiableLinkTarget()
- data class ModifiableFileTarget(val file: ModifiableFile): ModifiableLinkTarget()
- data object NoTarget: ModifiableLinkTarget()
-}
diff --git a/app/src/main/java/org/the_jk/cleversync/io/ModifiableTree.kt b/app/src/main/java/org/the_jk/cleversync/io/ModifiableTree.kt
deleted file mode 100644
index 383360d..0000000
--- a/app/src/main/java/org/the_jk/cleversync/io/ModifiableTree.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package org.the_jk.cleversync.io
-
-interface ModifiableTree : Tree, ModifiableDirectory
diff --git a/app/src/main/java/org/the_jk/cleversync/io/Tree.kt b/app/src/main/java/org/the_jk/cleversync/io/Tree.kt
deleted file mode 100644
index b6f2d54..0000000
--- a/app/src/main/java/org/the_jk/cleversync/io/Tree.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.the_jk.cleversync.io
-
-import android.content.res.Resources
-
-interface Tree : Directory {
- fun description(resources: Resources): CharSequence
-}
diff --git a/app/src/main/java/org/the_jk/cleversync/io/TreeFactory.kt b/app/src/main/java/org/the_jk/cleversync/io/TreeFactory.kt
index d7c22f5..6df2c93 100644
--- a/app/src/main/java/org/the_jk/cleversync/io/TreeFactory.kt
+++ b/app/src/main/java/org/the_jk/cleversync/io/TreeFactory.kt
@@ -1,12 +1,14 @@
package org.the_jk.cleversync.io
-import org.the_jk.cleversync.io.impl.PathTree
+import org.the_jk.cleversync.local.LocalTreeFactory
import java.nio.file.Path
object TreeFactory {
fun localModifiableTree(root: Path): ModifiableTree {
- return PathTree(root)
+ return LocalTreeFactory.modifiableTree(root)
}
- fun localTree(root: Path): Tree = localModifiableTree(root)
+ fun localTree(root: Path): Tree {
+ return LocalTreeFactory.tree(root)
+ }
}
diff --git a/app/src/main/java/org/the_jk/cleversync/io/impl/PathDirectory.kt b/app/src/main/java/org/the_jk/cleversync/io/impl/PathDirectory.kt
deleted file mode 100644
index 55bed6a..0000000
--- a/app/src/main/java/org/the_jk/cleversync/io/impl/PathDirectory.kt
+++ /dev/null
@@ -1,188 +0,0 @@
-package org.the_jk.cleversync.io.impl
-
-import androidx.annotation.AnyThread
-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
-import org.the_jk.cleversync.io.ModifiableFile
-import org.the_jk.cleversync.io.ModifiableLink
-import java.nio.file.LinkOption
-import java.nio.file.NoSuchFileException
-import java.nio.file.Path
-import kotlin.io.path.ExperimentalPathApi
-import kotlin.io.path.createDirectory
-import kotlin.io.path.createSymbolicLinkPointingTo
-import kotlin.io.path.deleteIfExists
-import kotlin.io.path.deleteRecursively
-import kotlin.io.path.isDirectory
-import kotlin.io.path.isRegularFile
-import kotlin.io.path.isSymbolicLink
-import kotlin.io.path.listDirectoryEntries
-import kotlin.io.path.name
-import kotlin.io.path.readSymbolicLink
-
-@OptIn(ExperimentalPathApi::class)
-internal open class PathDirectory(
- internal val path: Path,
- private val pathWatcher: PathWatcher,
-) : ModifiableDirectory {
- private val watcher: DirectoryWatcher by lazy {
- DirectoryWatcher()
- }
-
- private val modifiableLiveContent: LiveData<ModifiableDirectory.Content> by lazy {
- watcher.content
- }
-
- 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 path = path.resolve(name)
- if (path.isDirectory(LinkOption.NOFOLLOW_LINKS)) return PathDirectory(path, pathWatcher)
- if (path.isSymbolicLink()) {
- val target = path.readSymbolicLink()
- if (target.isDirectory()) return PathDirectory(target.toRealPath(), pathWatcher)
- }
- return null
- }
-
- override fun modifiableOpenFile(name: String): ModifiableFile? {
- val path = path.resolve(name)
- if (path.isRegularFile(LinkOption.NOFOLLOW_LINKS)) return PathFile(path)
- if (path.isSymbolicLink()) {
- val target = path.readSymbolicLink()
- if (target.isRegularFile()) return PathFile(target.toRealPath())
- }
- return null
- }
-
- override fun modifiableOpenLink(name: String): ModifiableLink? {
- val path = path.resolve(name)
- return if (path.isSymbolicLink()) PathLink(path, pathWatcher) else null
- }
-
- override fun modifiableList() = makeContent(path.listDirectoryEntries())
- override fun modifiableLiveList() = modifiableLiveContent
-
- override fun createDirectory(name: String): ModifiableDirectory {
- val path = path.resolve(name)
- return PathDirectory(path.createDirectory(), pathWatcher)
- }
-
- override fun createFile(name: String): ModifiableFile {
- val path = path.resolve(name)
- return PathFile(path)
- }
-
- override fun createLink(name: String, target: Directory): ModifiableLink {
- val path = path.resolve(name)
- return PathLink(path.createSymbolicLinkPointingTo((target as PathDirectory).path), pathWatcher)
- }
-
- override fun createLink(name: String, target: File): ModifiableLink {
- val path = path.resolve(name)
- return PathLink(path.createSymbolicLinkPointingTo((target as PathFile).path), pathWatcher)
- }
-
- override fun createLink(name: String, target: String): ModifiableLink {
- val targetPath = path.resolve(target)
- val path = path.resolve(name)
- return PathLink(path.createSymbolicLinkPointingTo(targetPath), pathWatcher)
- }
-
- override fun removeDirectory(name: String): Boolean {
- val path = path.resolve(name)
- return if (path.isDirectory(LinkOption.NOFOLLOW_LINKS)) {
- path.deleteRecursively()
- true
- } else false
- }
-
- override fun removeFile(name: String): Boolean {
- val path = path.resolve(name)
- return path.isRegularFile(LinkOption.NOFOLLOW_LINKS) && path.deleteIfExists()
- }
-
- override fun removeLink(name: String): Boolean {
- val path = path.resolve(name)
- return path.isSymbolicLink() && path.deleteIfExists()
- }
-
- override val name: String
- get() = path.name
-
- override fun list(): Directory.Content {
- val modifiable = modifiableList()
- return Directory.Content(modifiable.directories, modifiable.files, modifiable.links)
- }
-
- override fun liveList() = liveContent
-
- override fun openDir(name: String) = modifiableOpenDir(name)
- override fun openFile(name: String) = modifiableOpenFile(name)
- override fun openLink(name: String) = modifiableOpenLink(name)
-
- override fun equals(other: Any?) = other is PathDirectory && other.path == path
- override fun hashCode() = path.hashCode()
- override fun toString() = path.toString()
-
- private inner class DirectoryWatcher : PathWatcher.Delegate {
- val content: LiveData<ModifiableDirectory.Content>
- get() = _content
-
- @AnyThread
- override fun update(added: List<Path>, removed: List<Path>) {
- try {
- _content.postValue(makeContent(path.listDirectoryEntries()))
- } catch (ignored: NoSuchFileException) {
- }
- }
-
- private val _content = object : MutableLiveData<ModifiableDirectory.Content>() {
- override fun onActive() {
- setup()
- }
-
- override fun onInactive() {
- clear()
- }
- }
-
- private fun setup() {
- val entries = path.listDirectoryEntries()
- pathWatcher.add(path, this)
- _content.value = makeContent(entries)
- }
-
- private fun clear() {
- pathWatcher.remove(path)
- }
- }
-
- private fun makeContent(entries: List<Path>): ModifiableDirectory.Content {
- val directories = mutableListOf<ModifiableDirectory>()
- val files = mutableListOf<ModifiableFile>()
- val links = mutableListOf<ModifiableLink>()
- entries.forEach {
- if (it.isDirectory(LinkOption.NOFOLLOW_LINKS)) {
- directories.add(PathDirectory(it, pathWatcher))
- } else if (it.isRegularFile(LinkOption.NOFOLLOW_LINKS)) {
- files.add(PathFile(it))
- } else if (it.isSymbolicLink()) {
- links.add(PathLink(it, pathWatcher))
- }
- }
- return ModifiableDirectory.Content(directories, files, links)
- }
-}
diff --git a/app/src/main/java/org/the_jk/cleversync/io/impl/PathFile.kt b/app/src/main/java/org/the_jk/cleversync/io/impl/PathFile.kt
deleted file mode 100644
index 9a8d160..0000000
--- a/app/src/main/java/org/the_jk/cleversync/io/impl/PathFile.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-package org.the_jk.cleversync.io.impl
-
-import org.the_jk.cleversync.io.ModifiableFile
-import java.io.InputStream
-import java.io.OutputStream
-import java.nio.file.Files
-import java.nio.file.LinkOption
-import java.nio.file.Path
-import java.nio.file.StandardCopyOption
-import java.nio.file.StandardOpenOption
-import java.time.Instant
-import kotlin.io.path.exists
-import kotlin.io.path.fileSize
-import kotlin.io.path.getLastModifiedTime
-import kotlin.io.path.inputStream
-import kotlin.io.path.name
-import kotlin.io.path.outputStream
-
-internal class PathFile(internal val path: Path) : ModifiableFile {
- override fun write(): OutputStream {
- // If file doesn't exist, write to it directly.
- if (!path.exists(LinkOption.NOFOLLOW_LINKS))
- return path.outputStream(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)
-
- // Otherwise, write to temp file, only overwriting when done.
- val tmp = path.parent.resolve(".#" + path.name)
- val os = tmp.outputStream(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)
- return object : OutputStream() {
- override fun write(value: Int) {
- os.write(value)
- }
-
- override fun write(b: ByteArray) {
- os.write(b)
- }
-
- override fun write(b: ByteArray, off: Int, len: Int) {
- os.write(b, off, len)
- }
-
- override fun flush() {
- os.flush()
- }
-
- override fun close() {
- os.close()
- Files.move(tmp, path, StandardCopyOption.ATOMIC_MOVE)
- }
- }
- }
-
- override val name: String
- get() = path.name
- override val size: ULong
- get() = path.fileSize().toULong()
- override val lastModified: Instant
- get() = path.getLastModifiedTime().toInstant()
-
- override fun read(): InputStream {
- return path.inputStream(StandardOpenOption.READ)
- }
-
- override fun equals(other: Any?) = other is PathFile && other.path == path
- override fun hashCode() = path.hashCode()
- override fun toString() = path.toString()
-}
diff --git a/app/src/main/java/org/the_jk/cleversync/io/impl/PathLink.kt b/app/src/main/java/org/the_jk/cleversync/io/impl/PathLink.kt
deleted file mode 100644
index 9ae8b51..0000000
--- a/app/src/main/java/org/the_jk/cleversync/io/impl/PathLink.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-package org.the_jk.cleversync.io.impl
-
-import org.the_jk.cleversync.io.Directory
-import org.the_jk.cleversync.io.File
-import org.the_jk.cleversync.io.Link
-import org.the_jk.cleversync.io.ModifiableLink
-import java.nio.file.Path
-import kotlin.io.path.createSymbolicLinkPointingTo
-import kotlin.io.path.deleteIfExists
-import kotlin.io.path.isDirectory
-import kotlin.io.path.isRegularFile
-import kotlin.io.path.name
-import kotlin.io.path.readSymbolicLink
-
-internal class PathLink(
- private val path: Path,
- private val pathWatcher: PathWatcher,
-) : ModifiableLink {
- override fun modifiableResolve(): ModifiableLink.ModifiableLinkTarget {
- val target = path.readSymbolicLink()
- return if (target.isDirectory()) {
- ModifiableLink.ModifiableDirectoryTarget(PathDirectory(target.toRealPath(), pathWatcher))
- } else if (target.isRegularFile()) {
- ModifiableLink.ModifiableFileTarget(PathFile(target.toRealPath()))
- } else {
- ModifiableLink.NoTarget
- }
- }
-
- override fun target(directory: Directory) {
- path.deleteIfExists()
- path.createSymbolicLinkPointingTo((directory as PathDirectory).path)
- }
-
- override fun target(file: File) {
- path.deleteIfExists()
- path.createSymbolicLinkPointingTo((file as PathFile).path)
- }
-
- override fun target(name: String) {
- path.deleteIfExists()
- path.createSymbolicLinkPointingTo(path.parent.resolve(name))
- }
-
- override val name: String
- get() = path.name
-
- override fun equals(other: Any?) = other is PathLink && other.path == path
- override fun hashCode() = path.hashCode()
- override fun toString() = path.toString()
-
- override fun resolve(): Link.LinkTarget {
- val target = path.readSymbolicLink()
- return if (target.isDirectory()) {
- Link.DirectoryTarget(PathDirectory(target.toRealPath(), pathWatcher))
- } else if (target.isRegularFile()) {
- Link.FileTarget(PathFile(target.toRealPath()))
- } else {
- Link.NoTarget
- }
- }
-}
diff --git a/app/src/main/java/org/the_jk/cleversync/io/impl/PathTree.kt b/app/src/main/java/org/the_jk/cleversync/io/impl/PathTree.kt
deleted file mode 100644
index a8a74c5..0000000
--- a/app/src/main/java/org/the_jk/cleversync/io/impl/PathTree.kt
+++ /dev/null
@@ -1,10 +0,0 @@
-package org.the_jk.cleversync.io.impl
-
-import android.content.res.Resources
-import org.the_jk.cleversync.R
-import org.the_jk.cleversync.io.ModifiableTree
-import java.nio.file.Path
-
-internal class PathTree(root: Path) : PathDirectory(root, PathWatcher()), ModifiableTree {
- override fun description(resources: Resources) = resources.getString(R.string.local_directory)
-}
diff --git a/app/src/main/java/org/the_jk/cleversync/io/impl/PathWatcher.kt b/app/src/main/java/org/the_jk/cleversync/io/impl/PathWatcher.kt
deleted file mode 100644
index 945019a..0000000
--- a/app/src/main/java/org/the_jk/cleversync/io/impl/PathWatcher.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-package org.the_jk.cleversync.io.impl
-
-import androidx.annotation.GuardedBy
-import androidx.annotation.WorkerThread
-import java.nio.file.ClosedWatchServiceException
-import java.nio.file.FileSystems
-import java.nio.file.Path
-import java.nio.file.StandardWatchEventKinds
-import java.nio.file.WatchKey
-import java.nio.file.WatchService
-import java.util.concurrent.Executors
-
-internal class PathWatcher {
- private val executor = Executors.newSingleThreadExecutor()
- private var service: WatchService? = null
- private val keys = mutableMapOf<Path, WatchKey>()
- private val lock = Object()
- @GuardedBy("lock")
- private val delegates = mutableMapOf<WatchKey, Delegate>()
-
- fun add(path: Path, delegate: Delegate) {
- if (keys.isEmpty()) {
- val service = FileSystems.getDefault().newWatchService()
- executor.execute{ runner(service) }
- this.service = service
- }
- val key = path.register(service!!, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE)
- delegates[key] = delegate
- keys[path] = key
- }
-
- fun remove(path: Path) {
- val key = keys.remove(path) ?: return
- key.cancel()
- synchronized(lock) {
- delegates.remove(key)
- }
- if (keys.isEmpty()) {
- service?.close()
- service = null
- }
- }
-
- interface Delegate {
- fun update(added: List<Path>, removed: List<Path>)
- }
-
- @WorkerThread
- private fun runner(service: WatchService) {
- while (true) {
- val key: WatchKey
- try {
- key = service.take()
- } catch (ignored: InterruptedException) {
- return
- } catch (ignored: ClosedWatchServiceException) {
- return
- }
-
- val added = mutableListOf<Path>()
- val removed = mutableListOf<Path>()
- var overflow = false
-
- for (event in key.pollEvents()) {
- when (event.kind()) {
- StandardWatchEventKinds.OVERFLOW -> overflow = true
- StandardWatchEventKinds.ENTRY_CREATE -> added.add(event.context() as Path)
- StandardWatchEventKinds.ENTRY_DELETE -> removed.add(event.context() as Path)
- }
- }
-
- if (overflow || added.isNotEmpty() || removed.isNotEmpty()) {
- val delegate = synchronized(lock) {
- delegates[key]
- }
- delegate?.update(added, removed)
- }
-
- key.reset()
- }
- }
-}
diff --git a/app/src/test/java/org/the_jk/cleversync/StringUtilsTest.kt b/app/src/test/java/org/the_jk/cleversync/StringUtilsTest.kt
deleted file mode 100644
index 6a36156..0000000
--- a/app/src/test/java/org/the_jk/cleversync/StringUtilsTest.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-package org.the_jk.cleversync
-
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-
-class StringUtilsTest {
- @Test
- fun splitEmpty() {
- assertThat(StringUtils.split("", '.', keepEmpty = true)).containsExactly("")
- assertThat(StringUtils.split("", '.', keepEmpty = false)).isEmpty()
- }
-
- @Test
- fun splitSanity() {
- assertThat(StringUtils.split("a.bb.a", '.')).containsExactly("a", "bb", "a").inOrder()
- assertThat(StringUtils.split(".a.bb.a", '.', keepEmpty = true)).containsExactly("", "a", "bb", "a").inOrder()
- assertThat(StringUtils.split(".a.bb.a", '.', keepEmpty = false)).containsExactly("a", "bb", "a").inOrder()
- assertThat(StringUtils.split(".a.bb.a.", '.', keepEmpty = true))
- .containsExactly("", "a", "bb", "a", "").inOrder()
- assertThat(StringUtils.split(".a.bb.a.", '.', keepEmpty = false)).containsExactly("a", "bb", "a").inOrder()
- }
-
- @Test
- fun splitDouble() {
- assertThat(StringUtils.split("foo..bar", '.', keepEmpty = true)).containsExactly("foo", "", "bar").inOrder()
- assertThat(StringUtils.split("foo..bar", '.', keepEmpty = false)).containsExactly("foo", "bar").inOrder()
- }
-
- @Test
- fun splitLimit() {
- assertThat(StringUtils.split("a.bb.a", '.', limit = 1)).containsExactly("a.bb.a")
- assertThat(StringUtils.split("a.bb.a", '.', limit = 2)).containsExactly("a", "bb.a").inOrder()
- assertThat(StringUtils.split("a.bb.a", '.', limit = 3)).containsExactly("a", "bb", "a").inOrder()
- assertThat(StringUtils.split("a.bb.a.", '.', limit = 3, keepEmpty = true))
- .containsExactly("a", "bb", "a.").inOrder()
- assertThat(StringUtils.split("a.bb.a.", '.', limit = 3, keepEmpty = false))
- .containsExactly("a", "bb", "a").inOrder()
- assertThat(StringUtils.split("a.bb.a", '.', limit = 1000)).containsExactly("a", "bb", "a").inOrder()
- }
-}
diff --git a/app/src/test/java/org/the_jk/cleversync/io/LocalTreeTest.kt b/app/src/test/java/org/the_jk/cleversync/io/LocalTreeTest.kt
deleted file mode 100644
index 7b83c98..0000000
--- a/app/src/test/java/org/the_jk/cleversync/io/LocalTreeTest.kt
+++ /dev/null
@@ -1,323 +0,0 @@
-package org.the_jk.cleversync.io
-
-import com.google.common.truth.Truth.assertThat
-import org.junit.Assert
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.rules.TemporaryFolder
-import org.junit.runner.RunWith
-import org.robolectric.RobolectricTestRunner
-import org.robolectric.annotation.Config
-import org.robolectric.shadows.ShadowLooper
-import org.the_jk.cleversync.safeValue
-
-@Config(manifest=Config.NONE)
-@RunWith(RobolectricTestRunner::class)
-class LocalTreeTest {
- @get:Rule
- val folder = TemporaryFolder()
-
- private lateinit var tree: ModifiableTree
-
- @Before
- fun setUp() {
- tree = TreeFactory.localModifiableTree(folder.root.toPath())
- }
-
- @Test
- fun empty() {
- val content = tree.list()
- assertThat(content.directories).isEmpty()
- assertThat(content.files).isEmpty()
- assertThat(content.links).isEmpty()
- }
-
- @Test
- fun emptyLive() {
- val content = tree.liveList().safeValue()
- assertThat(content?.directories).isEmpty()
- assertThat(content?.files).isEmpty()
- assertThat(content?.links).isEmpty()
- }
-
- @Test
- fun createDirectory() {
- val foo = tree.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 = tree.list()
- assertThat(content.directories).contains(foo)
- assertThat(content.files).isEmpty()
- assertThat(content.links).isEmpty()
- }
-
- @Test
- fun observeCreateDirectory() {
- val content = tree.liveList()
- var dir: Directory? = null
- content.observeForever {
- if (it.directories.size == 1) dir = it.directories[0]
- }
- tree.createDirectory("foo")
- while (dir == null) {
- ShadowLooper.idleMainLooper()
- }
- assertThat(dir?.name).isEqualTo("foo")
- }
-
- @Test
- fun createFile() {
- val foo = tree.createFile("foo")
- // Files are not created until you write to them.
- assertThat(tree.list().files).isEmpty()
- foo.write().use { os ->
- os.write(byteArrayOf(1, 2, 3, 4))
- }
- assertThat(tree.list().files).contains(foo)
- assertThat(foo.size).isEqualTo(4.toULong())
- foo.read().use {
- assertThat(it.readBytes()).isEqualTo(byteArrayOf(1, 2, 3, 4))
- }
- }
-
- @Test
- fun overwriteFile() {
- val foo = tree.createFile("foo")
- foo.write().use { os ->
- os.write(byteArrayOf(1, 2, 3, 4))
- }
- foo.write().use { os ->
- os.write(127)
- os.write(byteArrayOf(1))
- os.write(byteArrayOf(2), 0, 0)
- assertThat(foo.size).isEqualTo(4.toULong())
- }
- assertThat(foo.size).isEqualTo(2.toULong())
- assertThat(tree.list().files).hasSize(1)
- foo.read().use {
- assertThat(it.readBytes()).isEqualTo(byteArrayOf(127, 1))
- }
- }
-
- @Test
- fun removeDir() {
- tree.createDirectory("foo")
- tree.removeDirectory("foo")
- assertThat(tree.list().directories).isEmpty()
- }
-
- @Test
- fun removeDirLive() {
- tree.createDirectory("foo")
- val content = tree.liveList()
- var done = false
- content.observeForever {
- if (it.directories.isEmpty()) done = true
- }
- tree.removeDirectory("foo")
- while (!done) {
- ShadowLooper.idleMainLooper()
- }
- }
-
- @Test
- fun createLink() {
- val dir = tree.createDirectory("dir")
- val file = tree.createFile("file")
- val link = tree.createLink("link", dir.name)
- var target = link.resolve()
- when (target) {
- is Link.DirectoryTarget -> assertThat(target.directory).isEqualTo(
- dir
- )
- is Link.FileTarget -> Assert.fail()
- is Link.NoTarget -> Assert.fail()
- }
- assertThat(tree.openDir("link")).isEqualTo(dir)
-
- link.target(file)
- target = link.resolve()
- when (target) {
- is Link.DirectoryTarget -> Assert.fail()
- is Link.FileTarget -> Assert.fail()
- is Link.NoTarget -> Unit
- }
- file.write().use { it.write(1) }
- target = link.resolve()
- when (target) {
- is Link.DirectoryTarget -> Assert.fail()
- is Link.FileTarget -> assertThat(target.file).isEqualTo(file)
- is Link.NoTarget -> Assert.fail()
- }
-
- assertThat(tree.openFile("link")).isEqualTo(file)
- }
-
- @Test
- fun createLinkSubdir() {
- val foo = tree.createDirectory("foo")
- val bar = foo.createDirectory("bar")
- val link1 = tree.createLink("link1", "foo/bar")
- val link2 = tree.createLink("link2", bar)
- assertThat(link1.resolve()).isEqualTo(link2.resolve())
- assertThat((link1.resolve() as Link.DirectoryTarget).directory).isEqualTo(bar)
- val link3 = foo.createLink("link3", "../link1")
- assertThat((link3.resolve() as Link.DirectoryTarget).directory).isEqualTo(bar)
- }
-
- @Test
- fun createLiveLink() {
- val content = tree.liveList()
- var link: Link? = null
- content.observeForever {
- if (it.links.size == 1) link = it.links[0]
- }
- val dir = tree.createDirectory("dir")
- tree.createLink("link", "dir")
- while (link == null) {
- ShadowLooper.idleMainLooper()
- }
- assertThat((link?.resolve() as Link.DirectoryTarget).directory).isEqualTo(dir)
- }
-
- @Test
- fun sameDir() {
- val dir1 = tree.createDirectory("dir")
- val dir2 = tree.openDir("dir")
- assertThat(dir1).isEqualTo(dir2)
- assertThat(dir1.hashCode()).isEqualTo(dir2.hashCode())
- assertThat(dir1.toString()).isEqualTo(dir2.toString())
- }
-
- @Test
- fun sameFile() {
- val file1 = tree.createFile("file")
- file1.write().use { it.write(127) }
- val file2 = tree.openFile("file")
- assertThat(file1).isEqualTo(file2)
- assertThat(file1.hashCode()).isEqualTo(file2.hashCode())
- assertThat(file1.toString()).isEqualTo(file2.toString())
- }
-
- @Test
- fun sameLink() {
- val link1 = tree.createLink("link", "foo")
- val link2 = tree.openLink("link")
- assertThat(link1).isEqualTo(link2)
- assertThat(link1.hashCode()).isEqualTo(link2.hashCode())
- assertThat(link1.toString()).isEqualTo(link2.toString())
- }
-
- @Test
- fun removeDirWithContent() {
- val foo = tree.createDirectory("foo")
- foo.createDirectory("dir")
- foo.createFile("file").write().use { it.write(byteArrayOf(1, 2, 3, 4)) }
- foo.createLink("link", "file")
- assertThat(tree.list().directories).hasSize(1)
- assertThat(tree.removeDirectory("foo")).isTrue()
- assertThat(tree.list().directories).isEmpty()
- }
-
- @Test
- fun removeWrongType() {
- tree.createDirectory("dir")
- assertThat(tree.removeFile("dir")).isFalse()
- assertThat(tree.removeLink("dir")).isFalse()
- tree.createFile("file").write().use { it.write(byteArrayOf(1, 2, 3, 4)) }
- assertThat(tree.removeDirectory("file")).isFalse()
- assertThat(tree.removeLink("file")).isFalse()
- tree.createLink("link", "doesn't exist")
- assertThat(tree.removeDirectory("link")).isFalse()
- assertThat(tree.removeFile("link")).isFalse()
- val content = tree.list()
- assertThat(content.directories).hasSize(1)
- assertThat(content.files).hasSize(1)
- assertThat(content.links).hasSize(1)
- }
-
- @Test
- fun removeFile() {
- tree.createFile("file").write().use { it.write(byteArrayOf(1, 2, 3, 4)) }
- assertThat(tree.list().files).hasSize(1)
- tree.removeFile("file")
- assertThat(tree.list().files).isEmpty()
- }
-
- @Test
- fun removeLink() {
- val dir = tree.createDirectory("dir")
- val file = tree.createFile("file")
- file.write().use { it.write(127) }
- tree.createLink("link1", dir)
- tree.createLink("link2", file)
- assertThat(tree.list().links).hasSize(2)
- tree.removeLink("link1")
- tree.removeLink("link2")
- assertThat(tree.list().links).isEmpty()
- }
-
- @Test
- fun changeLink() {
- val dir = tree.createDirectory("dir")
- val file = tree.createFile("file")
- file.write().use { it.write(127) }
- val link = tree.createLink("link", "doesn't exist")
- assertThat(link.resolve() is Link.NoTarget).isTrue()
- link.target(file)
- assertThat((link.resolve() as Link.FileTarget).file).isEqualTo(file)
- link.target(dir)
- assertThat((link.resolve() as Link.DirectoryTarget).directory).isEqualTo(dir)
- link.target("bad")
- assertThat(link.resolve() is Link.NoTarget).isTrue()
- }
-
- @Test
- fun changeModifiableLink() {
- val dir = tree.createDirectory("dir")
- val file = tree.createFile("file")
- file.write().use { it.write(127) }
- val link = tree.createLink("link", "doesn't exist")
- assertThat(link.modifiableResolve() is ModifiableLink.NoTarget).isTrue()
- link.target(file)
- assertThat((link.modifiableResolve() as ModifiableLink.ModifiableFileTarget).file).isEqualTo(file)
- link.target(dir)
- assertThat((link.modifiableResolve() as ModifiableLink.ModifiableDirectoryTarget).directory).isEqualTo(dir)
- link.target("bad")
- assertThat(link.modifiableResolve() is ModifiableLink.NoTarget).isTrue()
- }
-
- @Test
- fun recursiveLink() {
- val link = tree.createLink("link", "link")
- assertThat(link.resolve() is Link.NoTarget).isTrue()
- }
-
- @Test
- fun names() {
- assertThat(tree.createDirectory("dir").name).isEqualTo("dir")
- assertThat(tree.createFile("file").name).isEqualTo("file")
- assertThat(tree.createLink("link", "file").name).isEqualTo("link")
- }
-
- @Test
- fun openNonExistent() {
- assertThat(tree.openDir("dir")).isNull()
- assertThat(tree.openFile("file")).isNull()
- assertThat(tree.openLink("link")).isNull()
- }
-
- @Test
- fun lastModified() {
- val file = tree.createFile("foo")
- file.write().use { it.write(1) }
- val old = file.lastModified
- file.write().use { it.write(2); it.flush() }
- val new = file.lastModified
- assertThat(old.isBefore(new) || old == new).isTrue()
- }
-}