diff options
Diffstat (limited to 'app/src/main/java/org/the_jk')
16 files changed, 5 insertions, 562 deletions
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() - } - } -} |
