140 lines
No EOL
4.6 KiB
Kotlin
140 lines
No EOL
4.6 KiB
Kotlin
/*
|
|
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
|
|
*/
|
|
|
|
package at.bitfire.davdroid.db
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.os.Bundle
|
|
import android.provider.DocumentsContract.Document
|
|
import android.webkit.MimeTypeMap
|
|
import androidx.core.os.bundleOf
|
|
import androidx.room.Entity
|
|
import androidx.room.ForeignKey
|
|
import androidx.room.Index
|
|
import androidx.room.PrimaryKey
|
|
import at.bitfire.davdroid.util.DavUtils.MEDIA_TYPE_OCTET_STREAM
|
|
import at.bitfire.davdroid.webdav.DocumentState
|
|
import okhttp3.HttpUrl
|
|
import okhttp3.MediaType
|
|
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
|
import java.io.FileNotFoundException
|
|
import java.time.Instant
|
|
|
|
@Entity(
|
|
tableName = "webdav_document",
|
|
foreignKeys = [
|
|
ForeignKey(entity = WebDavMount::class, parentColumns = ["id"], childColumns = ["mountId"], onDelete = ForeignKey.CASCADE),
|
|
ForeignKey(entity = WebDavDocument::class, parentColumns = ["id"], childColumns = ["parentId"], onDelete = ForeignKey.CASCADE)
|
|
],
|
|
indices = [
|
|
Index("mountId", "parentId", "name", unique = true),
|
|
Index("parentId")
|
|
]
|
|
)
|
|
// If any column name is modified, also change it in [DavDocumentsProvider$queryChildDocuments]
|
|
data class WebDavDocument(
|
|
|
|
@PrimaryKey(autoGenerate = true)
|
|
val id: Long = 0,
|
|
|
|
/** refers to the [WebDavMount] the document belongs to */
|
|
val mountId: Long,
|
|
|
|
/** refers to parent document (*null* when this document is a root document) */
|
|
val parentId: Long?,
|
|
|
|
/** file name (without any slashes) */
|
|
val name: String,
|
|
val isDirectory: Boolean = false,
|
|
|
|
val displayName: String? = null,
|
|
val mimeType: MediaType? = null,
|
|
val eTag: String? = null,
|
|
val lastModified: Long? = null,
|
|
val size: Long? = null,
|
|
|
|
val mayBind: Boolean? = null,
|
|
val mayUnbind: Boolean? = null,
|
|
val mayWriteContent: Boolean? = null,
|
|
|
|
val quotaAvailable: Long? = null,
|
|
val quotaUsed: Long? = null
|
|
|
|
) {
|
|
|
|
fun cacheKey(): CacheKey? {
|
|
if (eTag != null || lastModified != null)
|
|
return CacheKey(id, DocumentState(eTag, lastModified?.let { ts -> Instant.ofEpochMilli(ts) }))
|
|
return null
|
|
}
|
|
|
|
@SuppressLint("InlinedApi")
|
|
fun toBundle(parent: WebDavDocument?): Bundle {
|
|
if (parent?.isDirectory == false)
|
|
throw IllegalArgumentException("Parent must be a directory")
|
|
|
|
val bundle = bundleOf(
|
|
Document.COLUMN_DOCUMENT_ID to id.toString(),
|
|
Document.COLUMN_DISPLAY_NAME to name
|
|
)
|
|
|
|
displayName?.let { bundle.putString(Document.COLUMN_SUMMARY, it) }
|
|
size?.let { bundle.putLong(Document.COLUMN_SIZE, it) }
|
|
lastModified?.let { bundle.putLong(Document.COLUMN_LAST_MODIFIED, it) }
|
|
|
|
// see RFC 3744 appendix B for required privileges for the various operations
|
|
var flags = Document.FLAG_SUPPORTS_COPY
|
|
if (isDirectory) {
|
|
bundle.putString(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR)
|
|
if (mayBind != false)
|
|
flags += Document.FLAG_DIR_SUPPORTS_CREATE
|
|
} else {
|
|
val reportedMimeType = mimeType ?:
|
|
MimeTypeMap.getSingleton().getMimeTypeFromExtension(
|
|
MimeTypeMap.getFileExtensionFromUrl(name)
|
|
)?.toMediaTypeOrNull() ?:
|
|
MEDIA_TYPE_OCTET_STREAM
|
|
|
|
bundle.putString(Document.COLUMN_MIME_TYPE, reportedMimeType.toString())
|
|
if (mimeType?.type == "image")
|
|
flags += Document.FLAG_SUPPORTS_THUMBNAIL
|
|
if (mayWriteContent != false)
|
|
flags += Document.FLAG_SUPPORTS_WRITE
|
|
}
|
|
if (parent?.mayUnbind != false)
|
|
flags += Document.FLAG_SUPPORTS_DELETE or
|
|
Document.FLAG_SUPPORTS_MOVE or
|
|
Document.FLAG_SUPPORTS_RENAME
|
|
bundle.putInt(Document.COLUMN_FLAGS, flags)
|
|
|
|
return bundle
|
|
}
|
|
|
|
suspend fun toHttpUrl(db: AppDatabase): HttpUrl {
|
|
val mount = db.webDavMountDao().getById(mountId)
|
|
|
|
val segments = mutableListOf(name)
|
|
var parentIter = parentId
|
|
while (parentIter != null) {
|
|
val parent = db.webDavDocumentDao().get(parentIter) ?: throw FileNotFoundException()
|
|
segments += parent.name
|
|
parentIter = parent.parentId
|
|
}
|
|
|
|
val builder = mount.url.newBuilder()
|
|
for (segment in segments.reversed())
|
|
builder.addPathSegment(segment)
|
|
return builder.build()
|
|
}
|
|
|
|
|
|
/**
|
|
* Represents a WebDAV document in a given state (with a given ETag/Last-Modified).
|
|
*/
|
|
data class CacheKey(
|
|
val docId: Long,
|
|
val documentState: DocumentState
|
|
)
|
|
|
|
} |