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
|
* 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;
|
||||||
|
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 ThumbnailUri = "thumbnail_uri"
|
||||||
|
|
||||||
const val StorageStrategy = "storage_strategy"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the file is a directory.
|
* Whether the file is a directory.
|
||||||
* Type: Int
|
* Type: Int
|
||||||
|
|||||||
@ -6,5 +6,6 @@ object PluginContract {
|
|||||||
object Methods {
|
object Methods {
|
||||||
const val GetType = "getType"
|
const val GetType = "getType"
|
||||||
const val GetState = "getState"
|
const val GetState = "getState"
|
||||||
|
const val GetConfig = "getConfig"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6,9 +6,12 @@ import android.net.Uri
|
|||||||
import android.os.CancellationSignal
|
import android.os.CancellationSignal
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.database.getStringOrNull
|
import androidx.core.database.getStringOrNull
|
||||||
|
import de.mm20.launcher2.crashreporter.CrashReporter
|
||||||
import de.mm20.launcher2.plugin.Plugin
|
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.config.StorageStrategy
|
||||||
import de.mm20.launcher2.plugin.contracts.FilePluginContract
|
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.plugin.contracts.SearchPluginContract
|
||||||
import de.mm20.launcher2.search.File
|
import de.mm20.launcher2.search.File
|
||||||
import kotlinx.collections.immutable.persistentMapOf
|
import kotlinx.collections.immutable.persistentMapOf
|
||||||
@ -32,12 +35,19 @@ class PluginFileProvider(
|
|||||||
it.invokeOnCancellation {
|
it.invokeOnCancellation {
|
||||||
cancellationSignal.cancel()
|
cancellationSignal.cancel()
|
||||||
}
|
}
|
||||||
val cursor = context.contentResolver.query(
|
val cursor = try {
|
||||||
uri,
|
context.contentResolver.query(
|
||||||
null,
|
uri,
|
||||||
null,
|
null,
|
||||||
cancellationSignal
|
null,
|
||||||
)
|
cancellationSignal
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("MM20", "Plugin ${plugin.authority} threw exception")
|
||||||
|
CrashReporter.logException(e)
|
||||||
|
it.resume(emptyList())
|
||||||
|
return@suspendCancellableCoroutine
|
||||||
|
}
|
||||||
|
|
||||||
if (cursor == null) {
|
if (cursor == null) {
|
||||||
Log.e("MM20", "Plugin ${plugin.authority} returned null cursor")
|
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? {
|
suspend fun getFile(id: String): File? {
|
||||||
val uri = Uri.Builder()
|
val uri = Uri.Builder()
|
||||||
.scheme("content")
|
.scheme("content")
|
||||||
@ -76,6 +106,14 @@ class PluginFileProvider(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun fromCursor(cursor: Cursor): List<File>? {
|
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
|
val idIndex = cursor
|
||||||
.getColumnIndex(FilePluginContract.FileColumns.Id)
|
.getColumnIndex(FilePluginContract.FileColumns.Id)
|
||||||
.takeIf { it >= 0 }
|
.takeIf { it >= 0 }
|
||||||
@ -95,9 +133,6 @@ class PluginFileProvider(
|
|||||||
val thumbnailUriIndex =
|
val thumbnailUriIndex =
|
||||||
cursor.getColumnIndex(FilePluginContract.FileColumns.ThumbnailUri)
|
cursor.getColumnIndex(FilePluginContract.FileColumns.ThumbnailUri)
|
||||||
.takeIf { it >= 0 }
|
.takeIf { it >= 0 }
|
||||||
val storageStrategyIndex =
|
|
||||||
cursor.getColumnIndex(FilePluginContract.FileColumns.StorageStrategy)
|
|
||||||
.takeIf { it >= 0 }
|
|
||||||
val directoryIndex =
|
val directoryIndex =
|
||||||
cursor.getColumnIndex(FilePluginContract.FileColumns.IsDirectory).takeIf { it >= 0 }
|
cursor.getColumnIndex(FilePluginContract.FileColumns.IsDirectory).takeIf { it >= 0 }
|
||||||
|
|
||||||
@ -116,13 +151,7 @@ class PluginFileProvider(
|
|||||||
thumbnailUri = thumbnailUriIndex?.let {
|
thumbnailUri = thumbnailUriIndex?.let {
|
||||||
cursor.getStringOrNull(it)
|
cursor.getStringOrNull(it)
|
||||||
}?.let { Uri.parse(it) },
|
}?.let { Uri.parse(it) },
|
||||||
storageStrategy = try {
|
storageStrategy = config.storageStrategy,
|
||||||
storageStrategyIndex?.let {
|
|
||||||
StorageStrategy.valueOf(cursor.getString(it))
|
|
||||||
}
|
|
||||||
} catch (e: IllegalArgumentException) {
|
|
||||||
null
|
|
||||||
} ?: StorageStrategy.StoreCopy,
|
|
||||||
isDirectory = directoryIndex?.let { cursor.getInt(it) } == 1,
|
isDirectory = directoryIndex?.let { cursor.getInt(it) } == 1,
|
||||||
authority = plugin.authority,
|
authority = plugin.authority,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -34,6 +34,9 @@ abstract class BasePluginProvider : ContentProvider() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PluginContract.Methods.GetConfig -> {
|
||||||
|
getPluginConfig()
|
||||||
|
}
|
||||||
|
|
||||||
else -> super.call(method, arg, extras)
|
else -> super.call(method, arg, extras)
|
||||||
}
|
}
|
||||||
@ -41,6 +44,10 @@ abstract class BasePluginProvider : ContentProvider() {
|
|||||||
|
|
||||||
internal abstract fun getPluginType(): PluginType
|
internal abstract fun getPluginType(): PluginType
|
||||||
|
|
||||||
|
internal open fun getPluginConfig(): Bundle {
|
||||||
|
return Bundle.EMPTY
|
||||||
|
}
|
||||||
|
|
||||||
open suspend fun getPluginState(): PluginState {
|
open suspend fun getPluginState(): PluginState {
|
||||||
return PluginState.Ready
|
return PluginState.Ready
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,19 @@
|
|||||||
package de.mm20.launcher2.sdk.base
|
package de.mm20.launcher2.sdk.base
|
||||||
|
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.database.MatrixCursor
|
import android.database.MatrixCursor
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.CancellationSignal
|
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 de.mm20.launcher2.plugin.contracts.SearchPluginContract
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.runBlocking
|
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
|
* 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 createCursor(capacity: Int): MatrixCursor
|
||||||
internal abstract fun writeToCursor(cursor: MatrixCursor, item: T)
|
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.
|
* If null, a default icon will be shown, depending on the file type.
|
||||||
*/
|
*/
|
||||||
val thumbnailUri: Uri? = null,
|
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 android.database.MatrixCursor
|
||||||
import de.mm20.launcher2.plugin.PluginType
|
import de.mm20.launcher2.plugin.PluginType
|
||||||
|
import de.mm20.launcher2.plugin.config.SearchPluginConfig
|
||||||
import de.mm20.launcher2.plugin.contracts.FilePluginContract
|
import de.mm20.launcher2.plugin.contracts.FilePluginContract
|
||||||
import de.mm20.launcher2.sdk.base.SearchPluginProvider
|
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>
|
abstract override suspend fun search(query: String): List<File>
|
||||||
|
|
||||||
final override fun getPluginType(): PluginType {
|
final override fun getPluginType(): PluginType {
|
||||||
@ -23,7 +26,6 @@ abstract class FileProvider : SearchPluginProvider<File>() {
|
|||||||
FilePluginContract.FileColumns.ContentUri,
|
FilePluginContract.FileColumns.ContentUri,
|
||||||
FilePluginContract.FileColumns.ThumbnailUri,
|
FilePluginContract.FileColumns.ThumbnailUri,
|
||||||
FilePluginContract.FileColumns.IsDirectory,
|
FilePluginContract.FileColumns.IsDirectory,
|
||||||
FilePluginContract.FileColumns.StorageStrategy
|
|
||||||
),
|
),
|
||||||
capacity,
|
capacity,
|
||||||
)
|
)
|
||||||
@ -40,7 +42,6 @@ abstract class FileProvider : SearchPluginProvider<File>() {
|
|||||||
item.uri.toString(),
|
item.uri.toString(),
|
||||||
item.thumbnailUri?.toString(),
|
item.thumbnailUri?.toString(),
|
||||||
if (item.isDirectory) 1 else 0,
|
if (item.isDirectory) 1 else 0,
|
||||||
item.storageStrategy.name,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user