Add deferred plugin storage strategy

This commit is contained in:
MM20 2024-04-09 18:00:32 +02:00
parent ef1d6c70e8
commit 2efc159741
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
3 changed files with 63 additions and 7 deletions

View File

@ -21,5 +21,13 @@ enum class StorageStrategy {
* Use this strategy if your plugin needs to perform network requests to retrieve search * 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. * 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,
} }

View File

@ -5,6 +5,7 @@ import android.net.Uri
import android.provider.MediaStore import android.provider.MediaStore
import androidx.core.database.getStringOrNull import androidx.core.database.getStringOrNull
import de.mm20.launcher2.crashreporter.CrashReporter 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.GDriveFile
import de.mm20.launcher2.files.providers.LocalFile import de.mm20.launcher2.files.providers.LocalFile
import de.mm20.launcher2.files.providers.NextcloudFile 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.SavableSearchable
import de.mm20.launcher2.search.SearchableDeserializer import de.mm20.launcher2.search.SearchableDeserializer
import de.mm20.launcher2.search.SearchableSerializer import de.mm20.launcher2.search.SearchableSerializer
import de.mm20.launcher2.search.UpdateResult
import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.toImmutableMap import kotlinx.collections.immutable.toImmutableMap
import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.firstOrNull
@ -331,7 +333,7 @@ internal class PluginFileSerializer(
"thumbnailUri" to searchable.thumbnailUri?.toString(), "thumbnailUri" to searchable.thumbnailUri?.toString(),
"isDirectory" to searchable.isDirectory, "isDirectory" to searchable.isDirectory,
"authority" to searchable.authority, "authority" to searchable.authority,
"strategy" to "copy", "strategy" to if (searchable.storageStrategy == StorageStrategy.StoreCopy) "copy" else "deferred",
).toString() ).toString()
} }
} }
@ -344,14 +346,22 @@ internal class PluginFileSerializer(
internal class PluginFileDeserializer( internal class PluginFileDeserializer(
private val context: Context, private val context: Context,
private val pluginRepository: PluginRepository, private val pluginRepository: PluginRepository,
): SearchableDeserializer { ) : SearchableDeserializer {
override suspend fun deserialize(serialized: String): SavableSearchable? { override suspend fun deserialize(serialized: String): SavableSearchable? {
val jsonObject = JSONObject(serialized) val jsonObject = JSONObject(serialized)
return if (jsonObject.optString("strategy", "ref") == "ref") { return when (jsonObject.optString("strategy", "copy")) {
getByRef(jsonObject) "ref" -> {
} else { getByRef(jsonObject)
getByCopy(jsonObject) }
"deferred" -> {
getDeferred(jsonObject)
}
else -> {
getByCopy(jsonObject)
}
} }
} }
@ -368,6 +378,33 @@ internal class PluginFileDeserializer(
return null 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? { private fun getByCopy(obj: JSONObject): File? {
try { try {
val uri = obj.getString("uri") val uri = obj.getString("uri")

View File

@ -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<File>)? = null,
) : File by cachedFile, UpdatableSearchable<File>