diff --git a/app/lib/features/geckoview/features/browser/domain/services/engine_settings_replication.dart b/app/lib/features/geckoview/features/browser/domain/services/engine_settings_replication.dart index 8d30f3d2..b6434a04 100644 --- a/app/lib/features/geckoview/features/browser/domain/services/engine_settings_replication.dart +++ b/app/lib/features/geckoview/features/browser/domain/services/engine_settings_replication.dart @@ -22,6 +22,7 @@ import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'; import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:weblibre/core/logger.dart'; +import 'package:weblibre/features/geckoview/features/preferences/data/repositories/preference_observer.dart'; import 'package:weblibre/features/user/domain/repositories/engine_settings.dart'; import 'package:weblibre/features/user/domain/repositories/general_settings.dart'; @@ -134,8 +135,20 @@ class EngineSettingsReplicationService settings.fingerprintingProtectionOverrides, ); } + if (previous.value?.enablePdfJs != settings.enablePdfJs) { + // ignore: only_use_keep_alive_inside_keep_alive + await ref + .read(preferenceFixatorProvider.notifier) + .register('pdfjs.disabled', !settings.enablePdfJs); + } } else { await _service.setDefaultSettings(settings); + + // ignore: only_use_keep_alive_inside_keep_alive + await ref + .read(preferenceFixatorProvider.notifier) + .register('pdfjs.disabled', !settings.enablePdfJs); + initialSettingsSent = true; } } diff --git a/app/lib/features/geckoview/features/browser/domain/services/engine_settings_replication.g.dart b/app/lib/features/geckoview/features/browser/domain/services/engine_settings_replication.g.dart index e03abf08..8d4443d4 100644 --- a/app/lib/features/geckoview/features/browser/domain/services/engine_settings_replication.g.dart +++ b/app/lib/features/geckoview/features/browser/domain/services/engine_settings_replication.g.dart @@ -44,7 +44,7 @@ final class EngineSettingsReplicationServiceProvider } String _$engineSettingsReplicationServiceHash() => - r'3c96e6e45537399582fdf6e8b1fafc44c613b89d'; + r'd28cc58a94f45008a478bf52a9c40d61811b591d'; abstract class _$EngineSettingsReplicationService extends $Notifier { void build(); diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/browser_view.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/browser_view.dart index b3c49ae2..52ef793b 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/browser_view.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/browser_view.dart @@ -44,6 +44,7 @@ import 'package:weblibre/features/geckoview/features/browser/domain/providers/li import 'package:weblibre/features/geckoview/features/browser/domain/services/browser_data.dart'; import 'package:weblibre/features/geckoview/features/browser/domain/services/engine_settings_replication.dart'; import 'package:weblibre/features/geckoview/features/browser/domain/services/proxy_settings_replication.dart'; +import 'package:weblibre/features/geckoview/features/preferences/data/repositories/preference_observer.dart'; import 'package:weblibre/features/share_intent/domain/entities/shared_content.dart'; import 'package:weblibre/features/user/data/models/general_settings.dart'; import 'package:weblibre/features/user/domain/repositories/cache.dart'; @@ -373,6 +374,19 @@ class _BrowserViewState extends ConsumerState }, ); + ref.listenManual( + fireImmediately: true, + preferenceFixatorProvider, + (previous, next) {}, + onError: (error, stackTrace) { + logger.e( + 'Error listening to preferenceFixatorProvider', + error: error, + stackTrace: stackTrace, + ); + }, + ); + ref.listenManual( fireImmediately: true, proxySettingsReplicationProvider, diff --git a/app/lib/features/geckoview/features/preferences/data/models/preference_setting.dart b/app/lib/features/geckoview/features/preferences/data/models/preference_setting.dart index 9dd07dde..2cf33c88 100644 --- a/app/lib/features/geckoview/features/preferences/data/models/preference_setting.dart +++ b/app/lib/features/geckoview/features/preferences/data/models/preference_setting.dart @@ -56,7 +56,7 @@ class PreferenceSetting with FastEquatable { final Object value; @JsonKey(includeFromJson: false, includeToJson: false) - final GeckoPrefValue? current; + final GeckoPref? current; bool get isActive => value == current?.value; diff --git a/app/lib/features/geckoview/features/preferences/data/models/preference_setting.g.dart b/app/lib/features/geckoview/features/preferences/data/models/preference_setting.g.dart index 92bfcb71..a5b622a0 100644 --- a/app/lib/features/geckoview/features/preferences/data/models/preference_setting.g.dart +++ b/app/lib/features/geckoview/features/preferences/data/models/preference_setting.g.dart @@ -80,7 +80,7 @@ abstract class _$PreferenceSettingCWProxy { PreferenceSetting description(String? description); - PreferenceSetting current(GeckoPrefValue? current); + PreferenceSetting current(GeckoPref? current); PreferenceSetting requireUserOptIn(bool requireUserOptIn); @@ -97,7 +97,7 @@ abstract class _$PreferenceSettingCWProxy { Object value, String? title, String? description, - GeckoPrefValue? current, + GeckoPref? current, bool requireUserOptIn, bool shouldBeDefault, }); @@ -121,7 +121,7 @@ class _$PreferenceSettingCWProxyImpl implements _$PreferenceSettingCWProxy { call(description: description); @override - PreferenceSetting current(GeckoPrefValue? current) => call(current: current); + PreferenceSetting current(GeckoPref? current) => call(current: current); @override PreferenceSetting requireUserOptIn(bool requireUserOptIn) => @@ -163,7 +163,7 @@ class _$PreferenceSettingCWProxyImpl implements _$PreferenceSettingCWProxy { current: current == const $CopyWithPlaceholder() ? _value.current // ignore: cast_nullable_to_non_nullable - : current as GeckoPrefValue?, + : current as GeckoPref?, requireUserOptIn: requireUserOptIn == const $CopyWithPlaceholder() || requireUserOptIn == null diff --git a/app/lib/features/geckoview/features/preferences/data/repositories/preference_observer.dart b/app/lib/features/geckoview/features/preferences/data/repositories/preference_observer.dart new file mode 100644 index 00000000..f48ade50 --- /dev/null +++ b/app/lib/features/geckoview/features/preferences/data/repositories/preference_observer.dart @@ -0,0 +1,81 @@ +import 'dart:async'; + +import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:weblibre/features/geckoview/domain/providers.dart'; + +part 'preference_observer.g.dart'; + +@Riverpod() +class PreferenceChangeListener extends _$PreferenceChangeListener { + @override + Stream build() async* { + final events = ref.watch(eventServiceProvider); + + await GeckoPrefService().startObserveChanges(); + + ref.onDispose(() async { + await GeckoPrefService().stopObserveChanges(); + }); + + yield* events.prefUpdateEvent; + } +} + +@Riverpod() +class PreferenceFixator extends _$PreferenceFixator { + Future register(String name, Object value) async { + await GeckoPrefService().applyPrefs({name: value}); + + state = {...state} + ..update( + name, + (_) => value, + ifAbsent: () { + unawaited(GeckoPrefService().registerPrefForObservation(name)); + return value; + }, + ); + } + + Future unregister(String name) async { + await GeckoPrefService().unregisterPrefForObservation(name); + state = {...state}..remove(name); + } + + @override + Map build() { + ref.listen(preferenceChangeListenerProvider, (previous, next) async { + if (next.hasValue) { + final setting = stateOrNull?[next.value!.name]; + if (setting != null && next.value?.value != setting) { + await GeckoPrefService().applyPrefs({next.value!.name: setting}); + } + } + }); + + return {}; + } +} + +// @Riverpod() +// class PreferenceObserver extends _$PreferenceObserver { +// @override +// Stream build(String pref) { +// final updateStream = ref +// .watch(preferenceChangeListenerProvider) +// .where((x) => x.name == pref); + +// unawaited(GeckoPrefService().registerPrefForObservation(pref)); + +// ref.onDispose(() async { +// await GeckoPrefService().unregisterPrefForObservation(pref); +// }); + +// return GeckoPrefService() +// .getPrefs([pref]) +// .asStream() +// .map((value) => value.values.first) +// .concatWith([updateStream]); +// } +// } diff --git a/app/lib/features/geckoview/features/preferences/data/repositories/preference_observer.g.dart b/app/lib/features/geckoview/features/preferences/data/repositories/preference_observer.g.dart new file mode 100644 index 00000000..24eed1a8 --- /dev/null +++ b/app/lib/features/geckoview/features/preferences/data/repositories/preference_observer.g.dart @@ -0,0 +1,109 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'preference_observer.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint, type=warning + +@ProviderFor(PreferenceChangeListener) +const preferenceChangeListenerProvider = PreferenceChangeListenerProvider._(); + +final class PreferenceChangeListenerProvider + extends $StreamNotifierProvider { + const PreferenceChangeListenerProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'preferenceChangeListenerProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$preferenceChangeListenerHash(); + + @$internal + @override + PreferenceChangeListener create() => PreferenceChangeListener(); +} + +String _$preferenceChangeListenerHash() => + r'668c1bcd0636ec515a5e92ee5ba4ea7a67fa7a6d'; + +abstract class _$PreferenceChangeListener extends $StreamNotifier { + Stream build(); + @$mustCallSuper + @override + void runBuild() { + final created = build(); + final ref = this.ref as $Ref, GeckoPref>; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, GeckoPref>, + AsyncValue, + Object?, + Object? + >; + element.handleValue(ref, created); + } +} + +@ProviderFor(PreferenceFixator) +const preferenceFixatorProvider = PreferenceFixatorProvider._(); + +final class PreferenceFixatorProvider + extends $NotifierProvider> { + const PreferenceFixatorProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'preferenceFixatorProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$preferenceFixatorHash(); + + @$internal + @override + PreferenceFixator create() => PreferenceFixator(); + + /// {@macro riverpod.override_with_value} + Override overrideWithValue(Map value) { + return $ProviderOverride( + origin: this, + providerOverride: $SyncValueProvider>(value), + ); + } +} + +String _$preferenceFixatorHash() => r'dfa323859c660d3db74dba7297e2a33b758ed78a'; + +abstract class _$PreferenceFixator extends $Notifier> { + Map build(); + @$mustCallSuper + @override + void runBuild() { + final created = build(); + final ref = this.ref as $Ref, Map>; + final element = + ref.element + as $ClassProviderElement< + AnyNotifier, Map>, + Map, + Object?, + Object? + >; + element.handleValue(ref, created); + } +} diff --git a/app/lib/features/geckoview/features/preferences/data/repositories/preference_settings.dart b/app/lib/features/geckoview/features/preferences/data/repositories/preference_settings.dart index 14e637b0..92cfe060 100644 --- a/app/lib/features/geckoview/features/preferences/data/repositories/preference_settings.dart +++ b/app/lib/features/geckoview/features/preferences/data/repositories/preference_settings.dart @@ -65,7 +65,7 @@ Future _preferenceSettingGroup( class _PreferenceRepository extends _$PreferenceRepository { final _prefManager = GeckoPrefService(); // Use BehaviorSubject instead of StreamController - late BehaviorSubject> _prefSubject; + late BehaviorSubject> _prefSubject; Future _updatePrefs() async { final prefs = await _prefManager.getAllPrefs(); @@ -83,8 +83,8 @@ class _PreferenceRepository extends _$PreferenceRepository { } @override - Raw>> build() { - _prefSubject = BehaviorSubject>(); + Raw>> build() { + _prefSubject = BehaviorSubject>(); ref.onDispose(() async { await _prefSubject.close(); diff --git a/app/lib/features/geckoview/features/preferences/data/repositories/preference_settings.g.dart b/app/lib/features/geckoview/features/preferences/data/repositories/preference_settings.g.dart index 35696dce..a9262bed 100644 --- a/app/lib/features/geckoview/features/preferences/data/repositories/preference_settings.g.dart +++ b/app/lib/features/geckoview/features/preferences/data/repositories/preference_settings.g.dart @@ -229,7 +229,7 @@ final class _PreferenceRepositoryProvider extends $NotifierProvider< _PreferenceRepository, - Raw>> + Raw>> > { const _PreferenceRepositoryProvider._() : super( @@ -250,21 +250,22 @@ final class _PreferenceRepositoryProvider _PreferenceRepository create() => _PreferenceRepository(); /// {@macro riverpod.override_with_value} - Override overrideWithValue(Raw>> value) { + Override overrideWithValue(Raw>> value) { return $ProviderOverride( origin: this, - providerOverride: - $SyncValueProvider>>>(value), + providerOverride: $SyncValueProvider>>>( + value, + ), ); } } String _$_preferenceRepositoryHash() => - r'9a98b95677d4070def531ea1e5b900892045a1ef'; + r'0f6812988a23407f6648e32fb1585984d2433bbc'; abstract class _$PreferenceRepository - extends $Notifier>>> { - Raw>> build(); + extends $Notifier>>> { + Raw>> build(); @$mustCallSuper @override void runBuild() { @@ -272,17 +273,17 @@ abstract class _$PreferenceRepository final ref = this.ref as $Ref< - Raw>>, - Raw>> + Raw>>, + Raw>> >; final element = ref.element as $ClassProviderElement< AnyNotifier< - Raw>>, - Raw>> + Raw>>, + Raw>> >, - Raw>>, + Raw>>, Object?, Object? >; diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoPrefApiImpl.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoPrefApiImpl.kt index 1a2286e8..f5f18f3f 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoPrefApiImpl.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoPrefApiImpl.kt @@ -9,15 +9,17 @@ package eu.weblibre.flutter_mozilla_components.api import eu.weblibre.flutter_mozilla_components.GlobalComponents import eu.weblibre.flutter_mozilla_components.feature.PrefManagerFeature import eu.weblibre.flutter_mozilla_components.feature.ResultConsumer +import eu.weblibre.flutter_mozilla_components.pigeons.GeckoPref import eu.weblibre.flutter_mozilla_components.pigeons.GeckoPrefApi -import eu.weblibre.flutter_mozilla_components.pigeons.GeckoPrefValue import mozilla.components.ExperimentalAndroidComponentsApi import mozilla.components.concept.engine.preferences.Branch +import mozilla.components.concept.engine.preferences.BrowserPrefObserverDelegate +import mozilla.components.concept.engine.preferences.BrowserPreference import mozilla.components.support.ktx.android.org.json.toList import org.json.JSONObject import org.json.JSONArray -class GeckoPrefApiImpl : GeckoPrefApi { +class GeckoPrefApiImpl : GeckoPrefApi, BrowserPrefObserverDelegate { private val components by lazy { requireNotNull(GlobalComponents.components) { "Components not initialized" } } @@ -46,28 +48,32 @@ class GeckoPrefApiImpl : GeckoPrefApi { } override fun getPrefList(callback: (Result>) -> Unit) { - PrefManagerFeature.scheduleRequest("getPrefList", Unit, object : ResultConsumer { - override fun success(result: JSONObject) { - callback(Result.success(result.getJSONArray("result").toList())) - } + PrefManagerFeature.scheduleRequest( + "getPrefList", + Unit, + object : ResultConsumer { + override fun success(result: JSONObject) { + callback(Result.success(result.getJSONArray("result").toList())) + } - override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { - callback(Result.failure(Exception("$errorCode $errorMessage $errorDetails"))) - } - }) + override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { + callback(Result.failure(Exception("$errorCode $errorMessage $errorDetails"))) + } + }) } @OptIn(ExperimentalAndroidComponentsApi::class) override fun getPrefs( preferenceFilter: List, - callback: (Result>) -> Unit + callback: (Result>) -> Unit ) { components.core.engine.getBrowserPrefs( preferenceFilter, onSuccess = { callback( Result.success( it.associate { - it.pref to GeckoPrefValue( + it.pref to GeckoPref( + name = it.pref, value = it.value, defaultValue = it.defaultValue, userValue = it.userValue, @@ -85,7 +91,7 @@ class GeckoPrefApiImpl : GeckoPrefApi { @OptIn(ExperimentalAndroidComponentsApi::class) override fun applyPrefs( prefs: Map, - callback: (Result>) -> Unit + callback: (Result>) -> Unit ) { var fault: Boolean = false; @@ -148,7 +154,7 @@ class GeckoPrefApiImpl : GeckoPrefApi { for (pref in preferenceNames) { components.core.engine.clearBrowserUserPref( pref = pref, - onSuccess = {}, + onSuccess = { }, onError = { callback(Result.failure(Exception("${it.message} ${it.cause}"))) fault = true @@ -159,5 +165,49 @@ class GeckoPrefApiImpl : GeckoPrefApi { return; } } + + callback(Result.success(Unit)) + } + + override fun startObserveChanges() { + components.core.engine.registerPrefObserverDelegate(this); + } + + override fun stopObserveChanges() { + components.core.engine.unregisterPrefObserverDelegate(); + } + + override fun registerPrefForObservation(name: String, callback: (Result) -> Unit) { + components.core.engine.registerPrefForObservation( + name, + onSuccess = { + callback(Result.success(Unit)) + }, + onError = { + callback(Result.failure(Exception("${it.message} ${it.cause}"))) + }) + } + + override fun unregisterPrefForObservation(name: String, callback: (Result) -> Unit) { + components.core.engine.unregisterPrefForObservation( + name, + onSuccess = { + callback(Result.success(Unit)) + }, + onError = { + callback(Result.failure(Exception("${it.message} ${it.cause}"))) + }) + } + + override fun onPreferenceChange(observedPreference: BrowserPreference<*>) { + components.flutterEvents.onPreferenceChange( + System.currentTimeMillis(), GeckoPref( + name = observedPreference.pref, + value = observedPreference.value, + defaultValue = observedPreference.defaultValue, + userValue = observedPreference.userValue, + hasUserChangedValue = observedPreference.hasUserChangedValue, + ) + ) { _ -> } } } \ No newline at end of file diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/pigeons/Gecko.g.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/pigeons/Gecko.g.kt index 44de38bb..692fa172 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/pigeons/Gecko.g.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/pigeons/Gecko.g.kt @@ -2196,7 +2196,8 @@ data class AddonCollection ( } /** Generated class from Pigeon that represents data sent in messages. */ -data class GeckoPrefValue ( +data class GeckoPref ( + val name: String, val value: Any? = null, val defaultValue: Any? = null, val userValue: Any? = null, @@ -2204,16 +2205,18 @@ data class GeckoPrefValue ( ) { companion object { - fun fromList(pigeonVar_list: List): GeckoPrefValue { - val value = pigeonVar_list[0] - val defaultValue = pigeonVar_list[1] - val userValue = pigeonVar_list[2] - val hasUserChangedValue = pigeonVar_list[3] as Boolean - return GeckoPrefValue(value, defaultValue, userValue, hasUserChangedValue) + fun fromList(pigeonVar_list: List): GeckoPref { + val name = pigeonVar_list[0] as String + val value = pigeonVar_list[1] + val defaultValue = pigeonVar_list[2] + val userValue = pigeonVar_list[3] + val hasUserChangedValue = pigeonVar_list[4] as Boolean + return GeckoPref(name, value, defaultValue, userValue, hasUserChangedValue) } } fun toList(): List { return listOf( + name, value, defaultValue, userValue, @@ -2221,7 +2224,7 @@ data class GeckoPrefValue ( ) } override fun equals(other: Any?): Boolean { - if (other !is GeckoPrefValue) { + if (other !is GeckoPref) { return false } if (this === other) { @@ -2546,7 +2549,7 @@ private open class GeckoPigeonCodec : StandardMessageCodec() { } 191.toByte() -> { return (readValue(buffer) as? List)?.let { - GeckoPrefValue.fromList(it) + GeckoPref.fromList(it) } } else -> super.readValueOfType(type, buffer) @@ -2802,7 +2805,7 @@ private open class GeckoPigeonCodec : StandardMessageCodec() { stream.write(190) writeValue(stream, value.toList()) } - is GeckoPrefValue -> { + is GeckoPref -> { stream.write(191) writeValue(stream, value.toList()) } @@ -3761,9 +3764,13 @@ interface GeckoIconsApi { /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface GeckoPrefApi { fun getPrefList(callback: (Result>) -> Unit) - fun getPrefs(preferenceFilter: List, callback: (Result>) -> Unit) - fun applyPrefs(prefs: Map, callback: (Result>) -> Unit) + fun getPrefs(preferenceFilter: List, callback: (Result>) -> Unit) + fun applyPrefs(prefs: Map, callback: (Result>) -> Unit) fun resetPrefs(preferenceNames: List, callback: (Result) -> Unit) + fun startObserveChanges() + fun stopObserveChanges() + fun registerPrefForObservation(name: String, callback: (Result) -> Unit) + fun unregisterPrefForObservation(name: String, callback: (Result) -> Unit) companion object { /** The codec used by GeckoPrefApi. */ @@ -3798,7 +3805,7 @@ interface GeckoPrefApi { channel.setMessageHandler { message, reply -> val args = message as List val preferenceFilterArg = args[0] as List - api.getPrefs(preferenceFilterArg) { result: Result> -> + api.getPrefs(preferenceFilterArg) { result: Result> -> val error = result.exceptionOrNull() if (error != null) { reply.reply(GeckoPigeonUtils.wrapError(error)) @@ -3818,7 +3825,7 @@ interface GeckoPrefApi { channel.setMessageHandler { message, reply -> val args = message as List val prefsArg = args[0] as Map - api.applyPrefs(prefsArg) { result: Result> -> + api.applyPrefs(prefsArg) { result: Result> -> val error = result.exceptionOrNull() if (error != null) { reply.reply(GeckoPigeonUtils.wrapError(error)) @@ -3851,6 +3858,76 @@ interface GeckoPrefApi { channel.setMessageHandler(null) } } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.startObserveChanges$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.startObserveChanges() + listOf(null) + } catch (exception: Throwable) { + GeckoPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.stopObserveChanges$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + api.stopObserveChanges() + listOf(null) + } catch (exception: Throwable) { + GeckoPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.registerPrefForObservation$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val nameArg = args[0] as String + api.registerPrefForObservation(nameArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeckoPigeonUtils.wrapError(error)) + } else { + reply.reply(GeckoPigeonUtils.wrapResult(null)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.unregisterPrefForObservation$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val nameArg = args[0] as String + api.unregisterPrefForObservation(nameArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeckoPigeonUtils.wrapError(error)) + } else { + reply.reply(GeckoPigeonUtils.wrapResult(null)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } } } } @@ -4422,6 +4499,23 @@ class GeckoStateEvents(private val binaryMessenger: BinaryMessenger, private val } } } + fun onPreferenceChange(timestampArg: Long, valueArg: GeckoPref, callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onPreferenceChange$separatedMessageChannelSuffix" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(timestampArg, valueArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(GeckoPigeonUtils.createConnectionError(channelName))) + } + } + } } /** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */ class GeckoLogging(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") { diff --git a/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart b/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart index 236fa4a4..29e231ba 100644 --- a/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart +++ b/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart @@ -42,7 +42,7 @@ export 'src/pigeons/gecko.g.dart' DohSettingsMode, EmailHitResult, GeckoEngineSettings, - GeckoPrefValue, + GeckoPref, GeckoSuggestion, GeckoSuggestionType, GeoHitResult, diff --git a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_event.dart b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_event.dart index 8ee71b73..e1f5ee3b 100644 --- a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_event.dart +++ b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_event.dart @@ -39,6 +39,7 @@ class GeckoEventService extends GeckoStateEvents { final _findResultsSubject = PublishSubject(); final _longPressSubject = PublishSubject(); final _scrollEventSubject = PublishSubject(); + final _prefUpdateSubject = PublishSubject(); final _tabAddedSubject = PublishSubject(); @@ -59,6 +60,7 @@ class GeckoEventService extends GeckoStateEvents { Stream get findResultsEvent => _findResultsSubject.stream; Stream get longPressEvent => _longPressSubject.stream; Stream get scrollEvent => _scrollEventSubject.stream; + Stream get prefUpdateEvent => _prefUpdateSubject.stream; Stream get tabAddedStream => _tabAddedSubject.stream; @@ -177,6 +179,11 @@ class GeckoEventService extends GeckoStateEvents { )); } + @override + void onPreferenceChange(int timestamp, GeckoPref value) { + _prefUpdateSubject.addWhenMoreRecent(timestamp, value.name, value); + } + GeckoEventService.setUp({ BinaryMessenger? binaryMessenger, String messageChannelSuffix = '', @@ -189,6 +196,8 @@ class GeckoEventService extends GeckoStateEvents { } void dispose() { + unawaited(_viewStateSubject.close()); + unawaited(_engineStateSubject.close()); unawaited(_tabListSubject.close()); unawaited(_selectedTabSubject.close()); unawaited(_tabContentSubject.close()); @@ -196,10 +205,12 @@ class GeckoEventService extends GeckoStateEvents { unawaited(_readerableSubject.close()); unawaited(_securityInfoSubject.close()); unawaited(_iconChangeSubject.close()); + unawaited(_iconUpdateSubject.close()); unawaited(_thumbnailSubject.close()); unawaited(_findResultsSubject.close()); unawaited(_longPressSubject.close()); unawaited(_scrollEventSubject.close()); unawaited(_tabAddedSubject.close()); + unawaited(_prefUpdateSubject.close()); } } diff --git a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_pref.dart b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_pref.dart index 4ccc0625..7232ce75 100644 --- a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_pref.dart +++ b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_pref.dart @@ -13,19 +13,35 @@ class GeckoPrefService { return _apiInstance.getPrefList(); } - Future> getAllPrefs() async { + Future> getAllPrefs() async { return _apiInstance.getPrefs(await getPrefList()); } - Future> getPrefs(List prefs) { + Future> getPrefs(List prefs) { return _apiInstance.getPrefs(prefs); } - Future> applyPrefs(Map prefs) { + Future> applyPrefs(Map prefs) { return _apiInstance.applyPrefs(prefs); } Future resetPrefs(List prefs) { return _apiInstance.resetPrefs(prefs); } + + Future startObserveChanges() { + return _apiInstance.startObserveChanges(); + } + + Future stopObserveChanges() { + return _apiInstance.stopObserveChanges(); + } + + Future registerPrefForObservation(String name) { + return _apiInstance.registerPrefForObservation(name); + } + + Future unregisterPrefForObservation(String name) { + return _apiInstance.unregisterPrefForObservation(name); + } } diff --git a/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart b/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart index 1d30c813..3f3e8c99 100644 --- a/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart +++ b/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart @@ -2788,14 +2788,17 @@ class AddonCollection { ; } -class GeckoPrefValue { - GeckoPrefValue({ +class GeckoPref { + GeckoPref({ + required this.name, this.value, this.defaultValue, this.userValue, required this.hasUserChangedValue, }); + String name; + Object? value; Object? defaultValue; @@ -2806,6 +2809,7 @@ class GeckoPrefValue { List _toList() { return [ + name, value, defaultValue, userValue, @@ -2816,20 +2820,21 @@ class GeckoPrefValue { Object encode() { return _toList(); } - static GeckoPrefValue decode(Object result) { + static GeckoPref decode(Object result) { result as List; - return GeckoPrefValue( - value: result[0], - defaultValue: result[1], - userValue: result[2], - hasUserChangedValue: result[3]! as bool, + return GeckoPref( + name: result[0]! as String, + value: result[1], + defaultValue: result[2], + userValue: result[3], + hasUserChangedValue: result[4]! as bool, ); } @override // ignore: avoid_equals_and_hash_code_on_mutable_classes bool operator ==(Object other) { - if (other is! GeckoPrefValue || other.runtimeType != runtimeType) { + if (other is! GeckoPref || other.runtimeType != runtimeType) { return false; } if (identical(this, other)) { @@ -3038,7 +3043,7 @@ class _PigeonCodec extends StandardMessageCodec { } else if (value is AddonCollection) { buffer.putUint8(190); writeValue(buffer, value.encode()); - } else if (value is GeckoPrefValue) { + } else if (value is GeckoPref) { buffer.putUint8(191); writeValue(buffer, value.encode()); } else { @@ -3193,7 +3198,7 @@ class _PigeonCodec extends StandardMessageCodec { case 190: return AddonCollection.decode(readValue(buffer)!); case 191: - return GeckoPrefValue.decode(readValue(buffer)!); + return GeckoPref.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -4357,7 +4362,7 @@ class GeckoPrefApi { } } - Future> getPrefs(List preferenceFilter) async { + Future> getPrefs(List preferenceFilter) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.getPrefs$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, @@ -4381,11 +4386,11 @@ class GeckoPrefApi { message: 'Host platform returned null value for non-null return value.', ); } else { - return (pigeonVar_replyList[0] as Map?)!.cast(); + return (pigeonVar_replyList[0] as Map?)!.cast(); } } - Future> applyPrefs(Map prefs) async { + Future> applyPrefs(Map prefs) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.applyPrefs$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, @@ -4409,7 +4414,7 @@ class GeckoPrefApi { message: 'Host platform returned null value for non-null return value.', ); } else { - return (pigeonVar_replyList[0] as Map?)!.cast(); + return (pigeonVar_replyList[0] as Map?)!.cast(); } } @@ -4435,6 +4440,98 @@ class GeckoPrefApi { return; } } + + Future startObserveChanges() async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.startObserveChanges$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future stopObserveChanges() async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.stopObserveChanges$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future registerPrefForObservation(String name) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.registerPrefForObservation$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([name]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future unregisterPrefForObservation(String name) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.unregisterPrefForObservation$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([name]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } } class GeckoMlApi { @@ -4809,6 +4906,8 @@ abstract class GeckoStateEvents { void onScrollChange(int timestamp, String tabId, int scrollY); + void onPreferenceChange(int timestamp, GeckoPref value); + static void setUp(GeckoStateEvents? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { @@ -5252,6 +5351,34 @@ abstract class GeckoStateEvents { }); } } + { + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onPreferenceChange$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onPreferenceChange was null.'); + final List args = (message as List?)!; + final int? arg_timestamp = (args[0] as int?); + assert(arg_timestamp != null, + 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onPreferenceChange was null, expected non-null int.'); + final GeckoPref? arg_value = (args[1] as GeckoPref?); + assert(arg_value != null, + 'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onPreferenceChange was null, expected non-null GeckoPref.'); + try { + api.onPreferenceChange(arg_timestamp!, arg_value!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } } } diff --git a/packages/flutter_mozilla_components/pigeons/gecko.dart b/packages/flutter_mozilla_components/pigeons/gecko.dart index 66b812d1..351b44fa 100644 --- a/packages/flutter_mozilla_components/pigeons/gecko.dart +++ b/packages/flutter_mozilla_components/pigeons/gecko.dart @@ -1075,13 +1075,15 @@ abstract class GeckoIconsApi { IconResult loadIcon(IconRequest request); } -class GeckoPrefValue { +class GeckoPref { + final String name; final Object? value; final Object? defaultValue; final Object? userValue; final bool hasUserChangedValue; - GeckoPrefValue( + GeckoPref( + this.name, this.value, this.defaultValue, this.userValue, @@ -1094,11 +1096,18 @@ abstract class GeckoPrefApi { @async List getPrefList(); @async - Map getPrefs(List preferenceFilter); + Map getPrefs(List preferenceFilter); @async - Map applyPrefs(Map prefs); + Map applyPrefs(Map prefs); @async void resetPrefs(List preferenceNames); + + void startObserveChanges(); + void stopObserveChanges(); + @async + void registerPrefForObservation(String name); + @async + void unregisterPrefForObservation(String name); } @HostApi() @@ -1198,6 +1207,7 @@ abstract class GeckoStateEvents { void onLongPress(int timestamp, String id, HitResult hitResult); void onScrollChange(int timestamp, String tabId, int scrollY); + void onPreferenceChange(int timestamp, GeckoPref value); } @FlutterApi()