Plugin stuff, idk, i wrote that shit yesterday
This commit is contained in:
parent
c5e524c702
commit
b7452f6bce
@ -0,0 +1,38 @@
|
||||
package de.mm20.launcher2.plugin.config
|
||||
|
||||
import android.os.Bundle
|
||||
import de.mm20.launcher2.plugin.config.StorageStrategy
|
||||
|
||||
data class SearchPluginConfig(
|
||||
/**
|
||||
* Icon resource to indicate that a result is from this provider
|
||||
*/
|
||||
val providerIconRes: Int? = null,
|
||||
/**
|
||||
* Strategy to store items from this provider in the launcher database
|
||||
* @see [StorageStrategy]
|
||||
*/
|
||||
val storageStrategy: StorageStrategy = StorageStrategy.StoreCopy,
|
||||
) {
|
||||
fun toBundle(): Bundle {
|
||||
return Bundle().apply {
|
||||
putInt("providerIconRes", providerIconRes ?: 0)
|
||||
putString("storageStrategy", storageStrategy.name)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
operator fun invoke(bundle: Bundle): SearchPluginConfig? {
|
||||
return SearchPluginConfig(
|
||||
providerIconRes = bundle.getInt("providerIconRes", 0).takeIf { it != 0 },
|
||||
storageStrategy = StorageStrategy.valueOfOrElse(
|
||||
bundle.getString(
|
||||
"storageStrategy",
|
||||
StorageStrategy.StoreCopy.name
|
||||
),
|
||||
StorageStrategy.StoreCopy,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -21,5 +21,14 @@ 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;
|
||||
companion object {
|
||||
fun valueOfOrElse(value: String, default: StorageStrategy): StorageStrategy {
|
||||
return try {
|
||||
valueOf(value)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
default
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -42,8 +42,6 @@ abstract class FilePluginContract {
|
||||
|
||||
const val ThumbnailUri = "thumbnail_uri"
|
||||
|
||||
const val StorageStrategy = "storage_strategy"
|
||||
|
||||
/**
|
||||
* Whether the file is a directory.
|
||||
* Type: Int
|
||||
|
||||
@ -6,5 +6,6 @@ object PluginContract {
|
||||
object Methods {
|
||||
const val GetType = "getType"
|
||||
const val GetState = "getState"
|
||||
const val GetConfig = "getConfig"
|
||||
}
|
||||
}
|
||||
@ -6,9 +6,12 @@ import android.net.Uri
|
||||
import android.os.CancellationSignal
|
||||
import android.util.Log
|
||||
import androidx.core.database.getStringOrNull
|
||||
import de.mm20.launcher2.crashreporter.CrashReporter
|
||||
import de.mm20.launcher2.plugin.Plugin
|
||||
import de.mm20.launcher2.plugin.config.SearchPluginConfig
|
||||
import de.mm20.launcher2.plugin.config.StorageStrategy
|
||||
import de.mm20.launcher2.plugin.contracts.FilePluginContract
|
||||
import de.mm20.launcher2.plugin.contracts.PluginContract
|
||||
import de.mm20.launcher2.plugin.contracts.SearchPluginContract
|
||||
import de.mm20.launcher2.search.File
|
||||
import kotlinx.collections.immutable.persistentMapOf
|
||||
@ -32,12 +35,19 @@ class PluginFileProvider(
|
||||
it.invokeOnCancellation {
|
||||
cancellationSignal.cancel()
|
||||
}
|
||||
val cursor = context.contentResolver.query(
|
||||
uri,
|
||||
null,
|
||||
null,
|
||||
cancellationSignal
|
||||
)
|
||||
val cursor = try {
|
||||
context.contentResolver.query(
|
||||
uri,
|
||||
null,
|
||||
null,
|
||||
cancellationSignal
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e("MM20", "Plugin ${plugin.authority} threw exception")
|
||||
CrashReporter.logException(e)
|
||||
it.resume(emptyList())
|
||||
return@suspendCancellableCoroutine
|
||||
}
|
||||
|
||||
if (cursor == null) {
|
||||
Log.e("MM20", "Plugin ${plugin.authority} returned null cursor")
|
||||
@ -50,6 +60,26 @@ class PluginFileProvider(
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPluginConfig(): SearchPluginConfig? {
|
||||
val configBundle = try {
|
||||
context.contentResolver.call(
|
||||
Uri.Builder()
|
||||
.scheme("content")
|
||||
.authority(plugin.authority)
|
||||
.build(),
|
||||
PluginContract.Methods.GetConfig,
|
||||
null,
|
||||
null
|
||||
) ?: return null
|
||||
} catch (e: Exception) {
|
||||
Log.e("MM20", "Plugin ${plugin.authority} threw exception")
|
||||
CrashReporter.logException(e)
|
||||
return null
|
||||
}
|
||||
|
||||
return SearchPluginConfig(configBundle)
|
||||
}
|
||||
|
||||
suspend fun getFile(id: String): File? {
|
||||
val uri = Uri.Builder()
|
||||
.scheme("content")
|
||||
@ -76,6 +106,14 @@ class PluginFileProvider(
|
||||
}
|
||||
|
||||
private fun fromCursor(cursor: Cursor): List<File>? {
|
||||
val config = getPluginConfig()
|
||||
|
||||
if (config == null) {
|
||||
Log.e("MM20", "Plugin ${plugin.authority} returned null config")
|
||||
cursor.close()
|
||||
return null
|
||||
}
|
||||
|
||||
val idIndex = cursor
|
||||
.getColumnIndex(FilePluginContract.FileColumns.Id)
|
||||
.takeIf { it >= 0 }
|
||||
@ -95,9 +133,6 @@ class PluginFileProvider(
|
||||
val thumbnailUriIndex =
|
||||
cursor.getColumnIndex(FilePluginContract.FileColumns.ThumbnailUri)
|
||||
.takeIf { it >= 0 }
|
||||
val storageStrategyIndex =
|
||||
cursor.getColumnIndex(FilePluginContract.FileColumns.StorageStrategy)
|
||||
.takeIf { it >= 0 }
|
||||
val directoryIndex =
|
||||
cursor.getColumnIndex(FilePluginContract.FileColumns.IsDirectory).takeIf { it >= 0 }
|
||||
|
||||
@ -116,13 +151,7 @@ class PluginFileProvider(
|
||||
thumbnailUri = thumbnailUriIndex?.let {
|
||||
cursor.getStringOrNull(it)
|
||||
}?.let { Uri.parse(it) },
|
||||
storageStrategy = try {
|
||||
storageStrategyIndex?.let {
|
||||
StorageStrategy.valueOf(cursor.getString(it))
|
||||
}
|
||||
} catch (e: IllegalArgumentException) {
|
||||
null
|
||||
} ?: StorageStrategy.StoreCopy,
|
||||
storageStrategy = config.storageStrategy,
|
||||
isDirectory = directoryIndex?.let { cursor.getInt(it) } == 1,
|
||||
authority = plugin.authority,
|
||||
)
|
||||
|
||||
@ -34,6 +34,9 @@ abstract class BasePluginProvider : ContentProvider() {
|
||||
}
|
||||
}
|
||||
}
|
||||
PluginContract.Methods.GetConfig -> {
|
||||
getPluginConfig()
|
||||
}
|
||||
|
||||
else -> super.call(method, arg, extras)
|
||||
}
|
||||
@ -41,6 +44,10 @@ abstract class BasePluginProvider : ContentProvider() {
|
||||
|
||||
internal abstract fun getPluginType(): PluginType
|
||||
|
||||
internal open fun getPluginConfig(): Bundle {
|
||||
return Bundle.EMPTY
|
||||
}
|
||||
|
||||
open suspend fun getPluginState(): PluginState {
|
||||
return PluginState.Ready
|
||||
}
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
package de.mm20.launcher2.sdk.base
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.database.Cursor
|
||||
import android.database.MatrixCursor
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.CancellationSignal
|
||||
import de.mm20.launcher2.plugin.contracts.PluginContract
|
||||
import de.mm20.launcher2.plugin.config.SearchPluginConfig
|
||||
import de.mm20.launcher2.plugin.contracts.SearchPluginContract
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
abstract class SearchPluginProvider<T> : BasePluginProvider() {
|
||||
abstract class SearchPluginProvider<T>(
|
||||
private val config: SearchPluginConfig,
|
||||
) : BasePluginProvider() {
|
||||
|
||||
/**
|
||||
* Search for items matching the given query
|
||||
@ -108,6 +108,10 @@ abstract class SearchPluginProvider<T> : BasePluginProvider() {
|
||||
}
|
||||
}
|
||||
|
||||
final override fun getPluginConfig(): Bundle {
|
||||
return config.toBundle()
|
||||
}
|
||||
|
||||
internal abstract fun createCursor(capacity: Int): MatrixCursor
|
||||
internal abstract fun writeToCursor(cursor: MatrixCursor, item: T)
|
||||
}
|
||||
@ -50,9 +50,4 @@ data class File(
|
||||
* If null, a default icon will be shown, depending on the file type.
|
||||
*/
|
||||
val thumbnailUri: Uri? = null,
|
||||
|
||||
/**
|
||||
* How the launcher should store this file in its database (i.e. when the user adds it to favorites).
|
||||
*/
|
||||
val storageStrategy: StorageStrategy = StorageStrategy.StoreCopy,
|
||||
)
|
||||
@ -2,10 +2,13 @@ package de.mm20.launcher2.sdk.files
|
||||
|
||||
import android.database.MatrixCursor
|
||||
import de.mm20.launcher2.plugin.PluginType
|
||||
import de.mm20.launcher2.plugin.config.SearchPluginConfig
|
||||
import de.mm20.launcher2.plugin.contracts.FilePluginContract
|
||||
import de.mm20.launcher2.sdk.base.SearchPluginProvider
|
||||
|
||||
abstract class FileProvider : SearchPluginProvider<File>() {
|
||||
abstract class FileProvider(
|
||||
config: SearchPluginConfig = SearchPluginConfig(),
|
||||
) : SearchPluginProvider<File>(config) {
|
||||
abstract override suspend fun search(query: String): List<File>
|
||||
|
||||
final override fun getPluginType(): PluginType {
|
||||
@ -23,7 +26,6 @@ abstract class FileProvider : SearchPluginProvider<File>() {
|
||||
FilePluginContract.FileColumns.ContentUri,
|
||||
FilePluginContract.FileColumns.ThumbnailUri,
|
||||
FilePluginContract.FileColumns.IsDirectory,
|
||||
FilePluginContract.FileColumns.StorageStrategy
|
||||
),
|
||||
capacity,
|
||||
)
|
||||
@ -40,7 +42,6 @@ abstract class FileProvider : SearchPluginProvider<File>() {
|
||||
item.uri.toString(),
|
||||
item.thumbnailUri?.toString(),
|
||||
if (item.isDirectory) 1 else 0,
|
||||
item.storageStrategy.name,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user