2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-08-30 13:47:41 +00:00

feat(sftp): notify MediaStore on file changes

This commit adds functionality to notify the MediaStore when files are modified via SFTP. This ensures that
 changes made through SFTP are reflected in the Android media library.

Specifically, the MediaStore is notified after file creation, deletion, copying, renaming, and link creation. Additionally, it is notified after closing a file that was opened for writing. This ensures that the MediaStore is kept up-to-date
 with any changes made to files through SFTP.
This commit is contained in:
ShellWen Chen 2024-08-05 18:44:49 +08:00
parent 167e2c7176
commit df0f2d651c
No known key found for this signature in database
GPG Key ID: 51AA58935AFE6617
3 changed files with 78 additions and 8 deletions

View File

@ -7,6 +7,7 @@
package org.kde.kdeconnect.Plugins.SftpPlugin
import android.content.Context
import android.net.Uri
import android.os.Build
import android.util.Log
import org.apache.sshd.common.file.nativefs.NativeFileSystemFactory
@ -26,6 +27,7 @@ import org.apache.sshd.sftp.server.SftpFileSystemAccessor
import org.apache.sshd.sftp.server.SftpSubsystemFactory
import org.apache.sshd.sftp.server.SftpSubsystemProxy
import org.kde.kdeconnect.Device
import org.kde.kdeconnect.Helpers.MediaStoreHelper
import org.kde.kdeconnect.Helpers.RandomHelper
import org.kde.kdeconnect.Helpers.SecurityHelpers.RsaHelper
import org.kde.kdeconnect.Helpers.SecurityHelpers.constantTimeCompare
@ -33,10 +35,13 @@ import org.kde.kdeconnect.Plugins.SftpPlugin.saf.SafFileSystemFactory
import org.kde.kdeconnect.Plugins.SftpPlugin.saf.SafPath
import org.slf4j.impl.HandroidLoggerAdapter
import java.io.IOException
import java.nio.channels.Channel
import java.nio.channels.SeekableByteChannel
import java.nio.charset.StandardCharsets
import java.nio.file.CopyOption
import java.nio.file.OpenOption
import java.nio.file.Path
import java.nio.file.StandardOpenOption
import java.nio.file.attribute.FileAttribute
import java.security.GeneralSecurityException
import java.security.KeyPair
@ -103,6 +108,17 @@ internal class SimpleSftpServer {
sshd.subsystemFactories =
listOf<SubsystemFactory>(SftpSubsystemFactory.Builder().apply {
withFileSystemAccessor(object : SftpFileSystemAccessor {
fun notifyMediaStore(path: Path) {
kotlin.runCatching {
val uri = Uri.parse(path.toUri().toString())
MediaStoreHelper.indexFile(context, uri)
uri
}.fold(
onSuccess = { Log.i(TAG, "Notified media store: $path, $it") },
onFailure = { Log.w(TAG, "Failed to notify media store: $path", it) }
)
}
override fun openFile(
subsystem: SftpSubsystemProxy?,
fileHandle: FileHandle?,
@ -116,6 +132,61 @@ internal class SimpleSftpServer {
}
return super.openFile(subsystem, fileHandle, file, handle, options, *attrs)
}
override fun removeFile(
subsystem: SftpSubsystemProxy?,
path: Path?,
isDirectory: Boolean
) {
super.removeFile(subsystem, path, isDirectory)
path?.let { notifyMediaStore(it) }
}
override fun copyFile(
subsystem: SftpSubsystemProxy?,
src: Path?,
dst: Path?,
opts: MutableCollection<CopyOption>?
) {
super.copyFile(subsystem, src, dst, opts)
dst?.let { notifyMediaStore(it) }
}
override fun renameFile(
subsystem: SftpSubsystemProxy?,
oldPath: Path?,
newPath: Path?,
opts: MutableCollection<CopyOption>?
) {
super.renameFile(subsystem, oldPath, newPath, opts)
oldPath?.let { notifyMediaStore(it) }
newPath?.let { notifyMediaStore(it) }
}
override fun createLink(
subsystem: SftpSubsystemProxy?,
link: Path?,
existing: Path?,
symLink: Boolean
) {
super.createLink(subsystem, link, existing, symLink)
link?.let { notifyMediaStore(it) }
existing?.let { notifyMediaStore(it) }
}
override fun closeFile(
subsystem: SftpSubsystemProxy?,
fileHandle: FileHandle?,
file: Path?,
handle: String?,
channel: Channel?,
options: MutableSet<out OpenOption>?
) {
super.closeFile(subsystem, fileHandle, file, handle, channel, options)
if (options?.contains(StandardOpenOption.WRITE) == true) {
file?.let { notifyMediaStore(it) }
}
}
})
}.build())
@ -189,8 +260,9 @@ internal class SimpleSftpServer {
}
companion object {
private val PORT_RANGE = 1739..1764
private const val TAG = "SimpleSftpServer"
private val PORT_RANGE = 1739..1764
const val USER: String = "kdeconnect"
init {

View File

@ -98,7 +98,6 @@ class SafFileSystemProvider(
val docFile = parent.createFile(Files.probeContentType(path), path.names.last())
?: throw IOException("Failed to create $path")
val uri = docFile.uri
MediaStoreHelper.indexFile(context, uri)
path.safUri = uri
return uri
}
@ -226,7 +225,6 @@ class SafFileSystemProvider(
if (!docFile.delete()) {
throw IOException("Failed to delete $path")
}
MediaStoreHelper.indexFile(context, docFile.uri)
}
override fun copy(source: Path, target: Path, vararg options: CopyOption) {
@ -272,8 +270,6 @@ class SafFileSystemProvider(
if (newUri == null) { // renameDocument returns null on failure
return@firstStep
}
MediaStoreHelper.indexFile(context, sourceUri)
MediaStoreHelper.indexFile(context, newUri)
source.safUri = newUri
return
} catch (ignored: FileNotFoundException) {
@ -295,8 +291,6 @@ class SafFileSystemProvider(
parentUri,
destParentUri
)
MediaStoreHelper.indexFile(context, sourceUri)
MediaStoreHelper.indexFile(context, newUri)
source.safUri = newUri!!
return
}

View File

@ -7,9 +7,9 @@ package org.kde.kdeconnect.Plugins.SftpPlugin.saf
import android.content.Context
import android.net.Uri
import android.util.Log
import androidx.documentfile.provider.DocumentFile
import org.apache.sshd.common.file.util.BasePath
import java.net.URI
import java.nio.file.LinkOption
class SafPath(
@ -21,6 +21,10 @@ class SafPath(
return this.normalize()
}
override fun toUri(): URI {
return URI.create(safUri.toString()) ?: throw IllegalStateException("SafUri is null")
}
fun getDocumentFile(ctx: Context): DocumentFile? {
if (safUri == null) return null
return DocumentFile.fromTreeUri(ctx, safUri!!)