added gatekeeper notification options
This commit is contained in:
+21
@@ -24,6 +24,7 @@ class IntentGatekeeperHostApiImpl(private val context: Context) : IntentGatekeep
|
||||
private const val PREFS_NAME = "weblibre_intent_gatekeeper"
|
||||
private const val KEY_ENABLED = "enabled"
|
||||
private const val KEY_BLOCKED_PACKAGES = "blocked_packages"
|
||||
private const val KEY_PENDING_ALWAYS_ALLOW = "pending_always_allow"
|
||||
}
|
||||
|
||||
override fun setConfig(enabled: Boolean, blockedPackages: List<String>) {
|
||||
@@ -50,4 +51,24 @@ class IntentGatekeeperHostApiImpl(private val context: Context) : IntentGatekeep
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPendingAlwaysAllows(): List<String> {
|
||||
val prefs = context.applicationContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
return (prefs.getStringSet(KEY_PENDING_ALWAYS_ALLOW, emptySet()) ?: emptySet()).toList()
|
||||
}
|
||||
|
||||
override fun ackPendingAlwaysAllows(packageNames: List<String>) {
|
||||
if (packageNames.isEmpty()) return
|
||||
|
||||
val prefs = context.applicationContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
val pending = prefs.getStringSet(KEY_PENDING_ALWAYS_ALLOW, emptySet())?.toMutableSet()
|
||||
?: mutableSetOf()
|
||||
if (pending.removeAll(packageNames.toSet())) {
|
||||
if (pending.isEmpty()) {
|
||||
prefs.edit().remove(KEY_PENDING_ALWAYS_ALLOW).apply()
|
||||
} else {
|
||||
prefs.edit().putStringSet(KEY_PENDING_ALWAYS_ALLOW, pending).apply()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+49
-4
@@ -36,6 +36,17 @@ import eu.weblibre.simple_intent_receiver.pigeons.Intent as PigeonIntent
|
||||
import eu.weblibre.simple_intent_receiver.pigeons.IntentGatekeeperHostApi
|
||||
|
||||
class SimpleIntentReceiverPlugin: FlutterPlugin, ActivityAware, PluginRegistry.NewIntentListener {
|
||||
companion object {
|
||||
// Stable names that must match the notification replay path and shared-prefs schema.
|
||||
private const val PREFS_NAME = "weblibre_intent_gatekeeper"
|
||||
private const val KEY_NOTIFICATION_APPROVAL_TOKENS = "notification_approval_tokens"
|
||||
private const val KEY_NOTIFICATION_APPROVAL_PACKAGE_PREFIX = "notification_approval_package_"
|
||||
private const val EXTRA_NOTIFICATION_APPROVAL_TOKEN = "eu.weblibre.gatekeeper.notification_approval_token"
|
||||
private const val EXTRA_ALWAYS_ALLOW_PACKAGE = "eu.weblibre.gatekeeper.always_allow_package"
|
||||
}
|
||||
|
||||
private data class NotificationApproval(val alwaysAllowPackage: String?)
|
||||
|
||||
private lateinit var context: Context
|
||||
private var intentReceiver: IntentReceiver? = null
|
||||
private var lastHandledIntent: String? = null
|
||||
@@ -125,12 +136,17 @@ class SimpleIntentReceiverPlugin: FlutterPlugin, ActivityAware, PluginRegistry.N
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
}
|
||||
|
||||
val pigeonIntent = convertToPigeonIntent(intent)
|
||||
val notificationApproval = consumeNotificationApproval(intent)
|
||||
val pigeonIntent = convertToPigeonIntent(intent, notificationApproval)
|
||||
intentReceiver?.sendIntent(pigeonIntent)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun resolveCallerPackage(intent: Intent): String? {
|
||||
private fun resolveCallerPackage(intent: Intent, notificationApproval: NotificationApproval?): String? {
|
||||
if (notificationApproval != null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val raw = resolveRawCallerPackage(intent) ?: return null
|
||||
// Treat system packages (launcher, shell, SystemUI, etc.) as internal — the
|
||||
// gatekeeper shouldn't prompt the user when the OS itself forwards an intent.
|
||||
@@ -179,10 +195,28 @@ class SimpleIntentReceiverPlugin: FlutterPlugin, ActivityAware, PluginRegistry.N
|
||||
}
|
||||
}
|
||||
|
||||
private fun convertToPigeonIntent(intent: Intent): PigeonIntent {
|
||||
private fun consumeNotificationApproval(intent: Intent): NotificationApproval? {
|
||||
val token = intent.getStringExtra(EXTRA_NOTIFICATION_APPROVAL_TOKEN) ?: return null
|
||||
val prefs = context.applicationContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
val tokens = prefs.getStringSet(KEY_NOTIFICATION_APPROVAL_TOKENS, emptySet())?.toMutableSet()
|
||||
?: return null
|
||||
if (!tokens.remove(token)) {
|
||||
return null
|
||||
}
|
||||
|
||||
val alwaysAllowPackage = prefs.getString("$KEY_NOTIFICATION_APPROVAL_PACKAGE_PREFIX$token", null)
|
||||
prefs.edit()
|
||||
.putStringSet(KEY_NOTIFICATION_APPROVAL_TOKENS, tokens)
|
||||
.remove("$KEY_NOTIFICATION_APPROVAL_PACKAGE_PREFIX$token")
|
||||
.apply()
|
||||
|
||||
return NotificationApproval(alwaysAllowPackage)
|
||||
}
|
||||
|
||||
private fun convertToPigeonIntent(intent: Intent, notificationApproval: NotificationApproval?): PigeonIntent {
|
||||
val action = intent.action
|
||||
val data = intent.dataString
|
||||
val fromPackageName = resolveCallerPackage(intent)
|
||||
val fromPackageName = resolveCallerPackage(intent, notificationApproval)
|
||||
|
||||
val categories = ArrayList<String>()
|
||||
intent.categories?.let {
|
||||
@@ -193,6 +227,13 @@ class SimpleIntentReceiverPlugin: FlutterPlugin, ActivityAware, PluginRegistry.N
|
||||
intent.extras?.let { bundle ->
|
||||
for (key in bundle.keySet()) {
|
||||
try {
|
||||
if (key == EXTRA_NOTIFICATION_APPROVAL_TOKEN) {
|
||||
continue
|
||||
}
|
||||
if (key == EXTRA_ALWAYS_ALLOW_PACKAGE) {
|
||||
continue
|
||||
}
|
||||
|
||||
when (val value = bundle.get(key)) {
|
||||
is Bundle -> {
|
||||
val bundleMap = HashMap<String, Any?>()
|
||||
@@ -224,6 +265,10 @@ class SimpleIntentReceiverPlugin: FlutterPlugin, ActivityAware, PluginRegistry.N
|
||||
}
|
||||
}
|
||||
|
||||
notificationApproval?.alwaysAllowPackage?.let {
|
||||
extras[EXTRA_ALWAYS_ALLOW_PACKAGE] = it
|
||||
}
|
||||
|
||||
return PigeonIntent(
|
||||
fromPackageName = fromPackageName,
|
||||
action = action,
|
||||
|
||||
+46
@@ -309,6 +309,19 @@ interface IntentGatekeeperHostApi {
|
||||
* label cannot be resolved.
|
||||
*/
|
||||
fun resolvePackageLabel(packageName: String): String?
|
||||
/**
|
||||
* Returns the list of packages for which the user tapped "Always allow"
|
||||
* via a blocked-intent notification while WebLibre was not running.
|
||||
* Callers must acknowledge persisted packages via
|
||||
* [ackPendingAlwaysAllows] after Flutter settings were updated
|
||||
* successfully.
|
||||
*/
|
||||
fun getPendingAlwaysAllows(): List<String>
|
||||
/**
|
||||
* Removes the given packages from the pending "Always allow" set after
|
||||
* Flutter has successfully persisted them into its own policy store.
|
||||
*/
|
||||
fun ackPendingAlwaysAllows(packageNames: List<String>)
|
||||
|
||||
companion object {
|
||||
/** The codec used by IntentGatekeeperHostApi. */
|
||||
@@ -355,6 +368,39 @@ interface IntentGatekeeperHostApi {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.simple_intent_receiver.IntentGatekeeperHostApi.getPendingAlwaysAllows$separatedMessageChannelSuffix", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { _, reply ->
|
||||
val wrapped: List<Any?> = try {
|
||||
listOf(api.getPendingAlwaysAllows())
|
||||
} catch (exception: Throwable) {
|
||||
IntentPigeonUtils.wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
run {
|
||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.simple_intent_receiver.IntentGatekeeperHostApi.ackPendingAlwaysAllows$separatedMessageChannelSuffix", codec)
|
||||
if (api != null) {
|
||||
channel.setMessageHandler { message, reply ->
|
||||
val args = message as List<Any?>
|
||||
val packageNamesArg = args[0] as List<String>
|
||||
val wrapped: List<Any?> = try {
|
||||
api.ackPendingAlwaysAllows(packageNamesArg)
|
||||
listOf(null)
|
||||
} catch (exception: Throwable) {
|
||||
IntentPigeonUtils.wrapError(exception)
|
||||
}
|
||||
reply.reply(wrapped)
|
||||
}
|
||||
} else {
|
||||
channel.setMessageHandler(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user