Add plugin settings screen

This commit is contained in:
MM20
2023-11-06 19:30:41 +01:00
parent 7c20d541cd
commit e7ae751340
20 changed files with 398 additions and 85 deletions
@@ -2,6 +2,8 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.util.Log
import de.mm20.launcher2.crashreporter.CrashReporter
import de.mm20.launcher2.plugin.Plugin
import de.mm20.launcher2.plugin.PluginType
@@ -16,37 +18,44 @@ class PluginScanner(
val plugins = mutableListOf<Plugin>()
for (cr in contentResolvers) {
val providerInfo = cr.providerInfo ?: continue
val authority = providerInfo.authority ?: continue
val bundle = context.contentResolver.call(
Uri.Builder()
.scheme("content")
.authority(authority)
.build(),
"getType",
null,
null,
) ?: continue
val type = bundle.getString("type")
?.let {
try {
PluginType.valueOf(it)
} catch (e: IllegalArgumentException) {
null
}
} ?: continue
plugins.add(
Plugin(
label = cr.loadLabel(context.packageManager).toString(),
description = providerInfo.metaData?.getString("de.mm20.launcher2.plugin.description"),
packageName = providerInfo.packageName,
className = providerInfo.name,
type = type,
authority = authority,
settingsActivity = providerInfo.metaData?.getString("de.mm20.launcher2.plugin.settings"),
enabled = false,
try {
val providerInfo = cr.providerInfo ?: continue
val authority = providerInfo.authority ?: continue
val bundle = context.contentResolver.call(
Uri.Builder()
.scheme("content")
.authority(authority)
.build(),
"getType",
null,
null,
) ?: continue
val type = bundle.getString("type")
?.let {
try {
PluginType.valueOf(it)
} catch (e: IllegalArgumentException) {
null
}
} ?: continue
plugins.add(
Plugin(
label = cr.loadLabel(context.packageManager).toString(),
description = providerInfo.metaData?.getString("de.mm20.launcher2.plugin.description"),
packageName = providerInfo.packageName,
className = providerInfo.name,
type = type,
authority = authority,
settingsActivity = providerInfo.metaData?.getString("de.mm20.launcher2.plugin.settings"),
enabled = false,
)
)
)
} catch (e: SecurityException) {
continue
} catch (e: Exception) {
CrashReporter.logException(e)
continue
}
}
return plugins
@@ -2,9 +2,12 @@ package de.mm20.launcher2.plugins
import PluginScanner
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.net.Uri
import androidx.core.content.ContextCompat
import de.mm20.launcher2.plugin.Plugin
@@ -16,6 +19,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
@@ -32,7 +36,11 @@ interface PluginService {
fun enablePlugin(plugin: Plugin)
fun disablePlugin(plugin: Plugin)
fun getPluginsWithState(type: PluginType? = null): Flow<List<PluginWithState>>
fun isPluginHostInstalled(): Flow<Boolean>
suspend fun getPluginState(plugin: Plugin): PluginState?
suspend fun getPluginIcon(plugin: Plugin): Drawable?
}
internal class PluginServiceImpl(
@@ -42,6 +50,10 @@ internal class PluginServiceImpl(
private val scope: CoroutineScope = CoroutineScope(Job() + Dispatchers.Default)
private val pluginHostInstalled = MutableStateFlow(false)
private val mutex = Mutex()
init {
refreshPlugins()
ContextCompat.registerReceiver(
@@ -65,9 +77,16 @@ internal class PluginServiceImpl(
repository.update(plugin.copy(enabled = false))
}
private val mutex = Mutex()
private fun refreshPlugins() {
scope.launch {
try {
val permission =
context.packageManager.getPermissionInfo(PluginContract.Permission, 0)
pluginHostInstalled.value = permission != null
} catch (e: PackageManager.NameNotFoundException) {
pluginHostInstalled.value = false
return@launch
}
mutex.withLock {
val enabledPlugins =
repository.findMany(enabled = true).first().map { it.authority }
@@ -79,8 +98,8 @@ internal class PluginServiceImpl(
it
}
}
repository.deleteMany()
repository.insertMany(plugins)
repository.deleteMany().join()
repository.insertMany(plugins).join()
}
}
}
@@ -128,6 +147,26 @@ internal class PluginServiceImpl(
}
}
override fun isPluginHostInstalled(): Flow<Boolean> {
return pluginHostInstalled
}
override suspend fun getPluginIcon(plugin: Plugin): Drawable? {
return withContext(Dispatchers.IO) {
val info = try {
context.packageManager.getProviderInfo(
ComponentName(
plugin.packageName,
plugin.className
), 0
)
} catch (e: PackageManager.NameNotFoundException) {
return@withContext null
}
info.loadIcon(context.packageManager) ?: info.applicationInfo?.loadIcon(context.packageManager)
}
}
private inner class AppUpdateReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
refreshPlugins()