diff --git a/core/shared/src/main/java/de/mm20/launcher2/plugin/config/StorageStrategy.kt b/core/shared/src/main/java/de/mm20/launcher2/plugin/config/StorageStrategy.kt index e374df55..677876c6 100644 --- a/core/shared/src/main/java/de/mm20/launcher2/plugin/config/StorageStrategy.kt +++ b/core/shared/src/main/java/de/mm20/launcher2/plugin/config/StorageStrategy.kt @@ -21,5 +21,13 @@ enum class StorageStrategy { * Use this strategy if your plugin needs to perform network requests to retrieve search * results and if you don't want to implement a cache for search results. */ - StoreCopy; + StoreCopy, + + /** + * The launcher stores all relevant information in its own internal database, like [StoreCopy]. + * A fresh copy is fetched from the plugin provider when the user opens the search result's + * detail view. This allows the plugin provider to update the search result at a later point in + * time, without the time constraints of [StoreReference]. + */ + Deferred, } \ No newline at end of file diff --git a/data/files/src/main/java/de/mm20/launcher2/files/FileSerialization.kt b/data/files/src/main/java/de/mm20/launcher2/files/FileSerialization.kt index a2508fa7..3e864ae5 100644 --- a/data/files/src/main/java/de/mm20/launcher2/files/FileSerialization.kt +++ b/data/files/src/main/java/de/mm20/launcher2/files/FileSerialization.kt @@ -5,6 +5,7 @@ import android.net.Uri import android.provider.MediaStore import androidx.core.database.getStringOrNull import de.mm20.launcher2.crashreporter.CrashReporter +import de.mm20.launcher2.files.providers.DeferredFile import de.mm20.launcher2.files.providers.GDriveFile import de.mm20.launcher2.files.providers.LocalFile import de.mm20.launcher2.files.providers.NextcloudFile @@ -22,6 +23,7 @@ import de.mm20.launcher2.search.FileMetaType import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.search.SearchableDeserializer import de.mm20.launcher2.search.SearchableSerializer +import de.mm20.launcher2.search.UpdateResult import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.toImmutableMap import kotlinx.coroutines.flow.firstOrNull @@ -331,7 +333,7 @@ internal class PluginFileSerializer( "thumbnailUri" to searchable.thumbnailUri?.toString(), "isDirectory" to searchable.isDirectory, "authority" to searchable.authority, - "strategy" to "copy", + "strategy" to if (searchable.storageStrategy == StorageStrategy.StoreCopy) "copy" else "deferred", ).toString() } } @@ -344,14 +346,22 @@ internal class PluginFileSerializer( internal class PluginFileDeserializer( private val context: Context, private val pluginRepository: PluginRepository, -): SearchableDeserializer { +) : SearchableDeserializer { override suspend fun deserialize(serialized: String): SavableSearchable? { val jsonObject = JSONObject(serialized) - return if (jsonObject.optString("strategy", "ref") == "ref") { - getByRef(jsonObject) - } else { - getByCopy(jsonObject) + return when (jsonObject.optString("strategy", "copy")) { + "ref" -> { + getByRef(jsonObject) + } + + "deferred" -> { + getDeferred(jsonObject) + } + + else -> { + getByCopy(jsonObject) + } } } @@ -368,6 +378,33 @@ internal class PluginFileDeserializer( return null } } + + private fun getDeferred(obj: JSONObject): File? { + val cached = getByCopy(obj) ?: return null + val timestamp = obj.optLong("timestamp", 0L) + return DeferredFile( + cachedFile = cached as PluginFile, + timestamp = timestamp, + updatedSelf = { + val plugin = pluginRepository.get(cached.authority).firstOrNull() + ?: return@DeferredFile UpdateResult.PermanentlyUnavailable() + if (!plugin.enabled) return@DeferredFile UpdateResult.PermanentlyUnavailable() + val provider = PluginFileProvider(context, cached.authority) + try { + val file = provider.getFile(cached.id) + if (file == null) { + UpdateResult.PermanentlyUnavailable() + } else { + UpdateResult.Success(file) + } + } catch (e: Exception) { + CrashReporter.logException(e) + UpdateResult.TemporarilyUnavailable(e) + } + } + ) + } + private fun getByCopy(obj: JSONObject): File? { try { val uri = obj.getString("uri") diff --git a/data/files/src/main/java/de/mm20/launcher2/files/providers/DeferredFile.kt b/data/files/src/main/java/de/mm20/launcher2/files/providers/DeferredFile.kt new file mode 100644 index 00000000..a0ab4597 --- /dev/null +++ b/data/files/src/main/java/de/mm20/launcher2/files/providers/DeferredFile.kt @@ -0,0 +1,11 @@ +package de.mm20.launcher2.files.providers + +import de.mm20.launcher2.search.File +import de.mm20.launcher2.search.UpdatableSearchable +import de.mm20.launcher2.search.UpdateResult + +class DeferredFile( + cachedFile: File, + override val timestamp: Long, + override var updatedSelf: (suspend () -> UpdateResult)? = null, +) : File by cachedFile, UpdatableSearchable \ No newline at end of file