From 751e22ac5eaacec3d9954efb68ee08070889c8ee Mon Sep 17 00:00:00 2001 From: Fabian Freund Date: Tue, 30 Sep 2025 13:02:17 +0200 Subject: [PATCH] added support for custom addon collections --- app/lib/core/routing/routes.dart | 1 + app/lib/core/routing/routes.g.dart | 29 ++++ app/lib/core/routing/routes.settings.dart | 13 ++ .../screens/addon_collection.dart | 143 ++++++++++++++++++ .../screens/developer_settings.dart | 12 ++ .../user/data/models/engine_settings.dart | 29 ++-- .../user/data/models/engine_settings.g.dart | 18 +++ .../domain/repositories/engine_settings.dart | 4 + .../repositories/engine_settings.g.dart | 2 +- app/lib/main.dart | 1 + app/lib/utils/form_validators.dart | 8 + .../flutter_mozilla_components/Components.kt | 2 + .../GlobalComponents.kt | 5 +- .../api/GeckoBrowserApiImpl.kt | 18 ++- .../components/Core.kt | 44 ++++-- .../pigeons/Gecko.g.kt | 48 +++++- .../lib/flutter_mozilla_components.dart | 1 + .../src/domain/services/gecko_browser.dart | 8 +- .../lib/src/pigeons/gecko.g.dart | 60 +++++++- .../pigeons/gecko.dart | 18 ++- 20 files changed, 423 insertions(+), 41 deletions(-) create mode 100644 app/lib/features/settings/presentation/screens/addon_collection.dart diff --git a/app/lib/core/routing/routes.dart b/app/lib/core/routing/routes.dart index ebcee5d0..234b5228 100644 --- a/app/lib/core/routing/routes.dart +++ b/app/lib/core/routing/routes.dart @@ -38,6 +38,7 @@ import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/c import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_list.dart'; import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_selection.dart'; import 'package:weblibre/features/onboarding/presentation/onboarding.dart'; +import 'package:weblibre/features/settings/presentation/screens/addon_collection.dart'; import 'package:weblibre/features/settings/presentation/screens/bang_settings.dart'; import 'package:weblibre/features/settings/presentation/screens/developer_settings.dart'; import 'package:weblibre/features/settings/presentation/screens/general_settings.dart'; diff --git a/app/lib/core/routing/routes.g.dart b/app/lib/core/routing/routes.g.dart index 436e92b7..17c5b360 100644 --- a/app/lib/core/routing/routes.g.dart +++ b/app/lib/core/routing/routes.g.dart @@ -112,6 +112,13 @@ RouteBase get $settingsRoute => GoRouteData.$route( path: 'developer', name: 'DeveloperSettingsRoute', factory: $DeveloperSettingsRoute._fromState, + routes: [ + GoRouteData.$route( + path: 'addon_collection', + name: 'AddonCollectionRoute', + factory: $AddonCollectionRoute._fromState, + ), + ], ), ], ); @@ -268,6 +275,28 @@ mixin $DeveloperSettingsRoute on GoRouteData { void replace(BuildContext context) => context.replace(location); } +mixin $AddonCollectionRoute on GoRouteData { + static AddonCollectionRoute _fromState(GoRouterState state) => + AddonCollectionRoute(); + + @override + String get location => + GoRouteData.$location('/settings/developer/addon_collection'); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + RouteBase get $browserRoute => GoRouteData.$route( path: '/', name: 'BrowserRoute', diff --git a/app/lib/core/routing/routes.settings.dart b/app/lib/core/routing/routes.settings.dart index d6b2ee7f..8f379283 100644 --- a/app/lib/core/routing/routes.settings.dart +++ b/app/lib/core/routing/routes.settings.dart @@ -47,6 +47,12 @@ part of 'routes.dart'; TypedGoRoute( name: 'DeveloperSettingsRoute', path: 'developer', + routes: [ + TypedGoRoute( + name: 'AddonCollectionRoute', + path: 'addon_collection', + ), + ], ), ], ) @@ -85,6 +91,13 @@ class DeveloperSettingsRoute extends GoRouteData with $DeveloperSettingsRoute { } } +class AddonCollectionRoute extends GoRouteData with $AddonCollectionRoute { + @override + Widget build(BuildContext context, GoRouterState state) { + return const AddonCollectionScreen(); + } +} + class WebEngineHardeningRoute extends GoRouteData with $WebEngineHardeningRoute { @override diff --git a/app/lib/features/settings/presentation/screens/addon_collection.dart b/app/lib/features/settings/presentation/screens/addon_collection.dart new file mode 100644 index 00000000..ac2d25cf --- /dev/null +++ b/app/lib/features/settings/presentation/screens/addon_collection.dart @@ -0,0 +1,143 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:universal_io/io.dart'; +import 'package:weblibre/features/user/data/models/engine_settings.dart'; +import 'package:weblibre/features/user/domain/repositories/engine_settings.dart'; +import 'package:weblibre/utils/form_validators.dart'; + +const _defaultServerUrl = 'https://services.addons.mozilla.org'; + +class AddonCollectionScreen extends HookConsumerWidget { + const AddonCollectionScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final formKey = useMemoized(() => GlobalKey()); + + final addonCollectionSetting = ref.watch( + engineSettingsWithDefaultsProvider.select( + (value) => value.addonCollection, + ), + ); + + final serverURLController = useTextEditingController( + text: addonCollectionSetting?.serverURL ?? _defaultServerUrl, + keys: [addonCollectionSetting], + ); + + final collectionUserController = useTextEditingController( + text: addonCollectionSetting?.collectionUser, + keys: [addonCollectionSetting], + ); + + final collectionNameController = useTextEditingController( + text: addonCollectionSetting?.collectionName, + keys: [addonCollectionSetting], + ); + + return Scaffold( + appBar: AppBar( + title: const Text('Custom Extension Collection'), + actions: [ + if (addonCollectionSetting != null) + IconButton( + onPressed: () async { + await ref + .read(engineSettingsRepositoryProvider.notifier) + .updateSettings( + (currentSettings) => + currentSettings.copyWith.addonCollection(null), + ); + + unawaited( + Future.delayed(const Duration(seconds: 1)).whenComplete(() { + exit(0); + }), + ); + }, + icon: const Icon(Icons.delete), + ), + ], + ), + body: Form( + key: formKey, + child: SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: ListView( + children: [ + TextFormField( + controller: serverURLController, + decoration: const InputDecoration( + label: Text('Server URL'), + hintText: _defaultServerUrl, + floatingLabelBehavior: FloatingLabelBehavior.always, + ), + keyboardType: TextInputType.url, + validator: (value) { + return validateUrl( + value, + onlyHttpProtocol: true, + eagerParsing: false, + ); + }, + ), + const SizedBox(height: 8), + TextFormField( + controller: collectionUserController, + decoration: const InputDecoration( + label: Text('Collection User'), + floatingLabelBehavior: FloatingLabelBehavior.always, + ), + validator: validateRequired, + ), + const SizedBox(height: 8), + TextFormField( + controller: collectionNameController, + decoration: const InputDecoration( + label: Text('Collection Name'), + floatingLabelBehavior: FloatingLabelBehavior.always, + ), + validator: validateRequired, + ), + const SizedBox(height: 32), + FilledButton( + onPressed: () async { + if (formKey.currentState?.validate() == true) { + await ref + .read(engineSettingsRepositoryProvider.notifier) + .updateSettings( + ( + currentSettings, + ) => currentSettings.copyWith.addonCollection( + AddonCollection( + serverURL: serverURLController.text, + collectionUser: collectionUserController.text, + collectionName: collectionNameController.text, + ), + ), + ); + + unawaited( + Future.delayed(const Duration(seconds: 1)).whenComplete( + () { + exit(0); + }, + ), + ); + } + }, + child: const Text('Save & Restart Browser'), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/app/lib/features/settings/presentation/screens/developer_settings.dart b/app/lib/features/settings/presentation/screens/developer_settings.dart index e437850a..9f1c55a3 100644 --- a/app/lib/features/settings/presentation/screens/developer_settings.dart +++ b/app/lib/features/settings/presentation/screens/developer_settings.dart @@ -29,6 +29,7 @@ import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:universal_io/io.dart'; import 'package:weblibre/core/logger.dart'; +import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/features/settings/presentation/controllers/save_settings.dart'; import 'package:weblibre/features/settings/presentation/widgets/custom_list_tile.dart'; import 'package:weblibre/features/user/data/models/engine_settings.dart'; @@ -198,6 +199,17 @@ class DeveloperSettingsScreen extends HookConsumerWidget { label: const Text('Copy'), ), ), + ListTile( + leading: const Icon(MdiIcons.puzzle), + title: const Text('Custom Extension Collection'), + subtitle: const Text( + 'Custom Add-on Collections are curated lists of extensions that users can create and share.', + ), + trailing: const Icon(Icons.chevron_right), + onTap: () async { + await AddonCollectionRoute().push(context); + }, + ), ], ); }, diff --git a/app/lib/features/user/data/models/engine_settings.dart b/app/lib/features/user/data/models/engine_settings.dart index bfd1f190..7af8dbfd 100644 --- a/app/lib/features/user/data/models/engine_settings.dart +++ b/app/lib/features/user/data/models/engine_settings.dart @@ -17,10 +17,13 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ +import 'dart:convert'; + import 'package:copy_with_extension/copy_with_extension.dart'; import 'package:fast_equatable/fast_equatable.dart'; import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'; import 'package:json_annotation/json_annotation.dart'; +import 'package:nullability/nullability.dart'; part 'engine_settings.g.dart'; @@ -60,6 +63,9 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable { final BounceTrackingProtectionMode bounceTrackingProtectionMode; + @JsonKey(fromJson: _addonCollectionFromJson, toJson: _addonCollectionToJson) + final AddonCollection? addonCollection; + @override @JsonKey(includeFromJson: false, includeToJson: false) ContentBlocking get contentBlocking => ContentBlocking( @@ -85,6 +91,7 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable { required super.enterpriseRootsEnabled, required this.queryParameterStripping, required this.bounceTrackingProtectionMode, + required this.addonCollection, }); EngineSettings.withDefaults({ @@ -102,6 +109,7 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable { BounceTrackingProtectionMode? bounceTrackingProtectionMode, super.userAgent, bool? enterpriseRootsEnabled, + this.addonCollection, }) : queryParameterStripping = queryParameterStripping ?? QueryParameterStripping.disabled, bounceTrackingProtectionMode = @@ -129,6 +137,14 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable { enterpriseRootsEnabled: enterpriseRootsEnabled ?? false, ); + static AddonCollection? _addonCollectionFromJson(String? json) => + json.mapNotNull( + (collection) => AddonCollection.decode(jsonDecode(collection) as List), + ); + + static String? _addonCollectionToJson(AddonCollection? collection) => + collection.mapNotNull((collection) => jsonEncode(collection.encode())); + factory EngineSettings.fromJson(Map json) => _$EngineSettingsFromJson(json); @@ -136,19 +152,8 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable { @override List get hashParameters => [ - super.javascriptEnabled, - super.trackingProtectionPolicy, - super.httpsOnlyMode, - super.globalPrivacyControlEnabled, - super.preferredColorScheme, - super.cookieBannerHandlingMode, - super.cookieBannerHandlingModePrivateBrowsing, - super.cookieBannerHandlingGlobalRules, - super.cookieBannerHandlingGlobalRulesSubFrames, - super.webContentIsolationStrategy, - super.userAgent, queryParameterStripping, bounceTrackingProtectionMode, - super.enterpriseRootsEnabled, + addonCollection, ]; } diff --git a/app/lib/features/user/data/models/engine_settings.g.dart b/app/lib/features/user/data/models/engine_settings.g.dart index 05f353fc..cbb0e7e0 100644 --- a/app/lib/features/user/data/models/engine_settings.g.dart +++ b/app/lib/features/user/data/models/engine_settings.g.dart @@ -51,6 +51,8 @@ abstract class _$EngineSettingsCWProxy { BounceTrackingProtectionMode bounceTrackingProtectionMode, ); + EngineSettings addonCollection(AddonCollection? addonCollection); + /// Creates a new instance with the provided field values. /// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `EngineSettings(...).copyWith.fieldName(value)`. /// @@ -73,6 +75,7 @@ abstract class _$EngineSettingsCWProxy { bool? enterpriseRootsEnabled, QueryParameterStripping queryParameterStripping, BounceTrackingProtectionMode bounceTrackingProtectionMode, + AddonCollection? addonCollection, }); } @@ -153,6 +156,10 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy { BounceTrackingProtectionMode bounceTrackingProtectionMode, ) => call(bounceTrackingProtectionMode: bounceTrackingProtectionMode); + @override + EngineSettings addonCollection(AddonCollection? addonCollection) => + call(addonCollection: addonCollection); + @override /// Creates a new instance with the provided field values. /// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `EngineSettings(...).copyWith.fieldName(value)`. @@ -178,6 +185,7 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy { Object? enterpriseRootsEnabled = const $CopyWithPlaceholder(), Object? queryParameterStripping = const $CopyWithPlaceholder(), Object? bounceTrackingProtectionMode = const $CopyWithPlaceholder(), + Object? addonCollection = const $CopyWithPlaceholder(), }) { return EngineSettings( javascriptEnabled: javascriptEnabled == const $CopyWithPlaceholder() @@ -251,6 +259,10 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy { ? _value.bounceTrackingProtectionMode // ignore: cast_nullable_to_non_nullable : bounceTrackingProtectionMode as BounceTrackingProtectionMode, + addonCollection: addonCollection == const $CopyWithPlaceholder() + ? _value.addonCollection + // ignore: cast_nullable_to_non_nullable + : addonCollection as AddonCollection?, ); } } @@ -308,6 +320,9 @@ EngineSettings _$EngineSettingsFromJson(Map json) => ), userAgent: json['userAgent'] as String?, enterpriseRootsEnabled: json['enterpriseRootsEnabled'] as bool?, + addonCollection: EngineSettings._addonCollectionFromJson( + json['addonCollection'] as String?, + ), ); Map _$EngineSettingsToJson( @@ -337,6 +352,9 @@ Map _$EngineSettingsToJson( 'bounceTrackingProtectionMode': _$BounceTrackingProtectionModeEnumMap[instance .bounceTrackingProtectionMode]!, + 'addonCollection': EngineSettings._addonCollectionToJson( + instance.addonCollection, + ), }; const _$TrackingProtectionPolicyEnumMap = { diff --git a/app/lib/features/user/domain/repositories/engine_settings.dart b/app/lib/features/user/domain/repositories/engine_settings.dart index 84a2a6c0..e628cc95 100644 --- a/app/lib/features/user/domain/repositories/engine_settings.dart +++ b/app/lib/features/user/domain/repositories/engine_settings.dart @@ -94,6 +94,10 @@ class EngineSettingsRepository extends _$EngineSettingsRepository { DriftSqlType.bool, db.typeMapping, ), + 'addonCollection': settings['addonCollection']?.readAs( + DriftSqlType.string, + db.typeMapping, + ), }); } diff --git a/app/lib/features/user/domain/repositories/engine_settings.g.dart b/app/lib/features/user/domain/repositories/engine_settings.g.dart index fdaefe4f..fa9804af 100644 --- a/app/lib/features/user/domain/repositories/engine_settings.g.dart +++ b/app/lib/features/user/domain/repositories/engine_settings.g.dart @@ -34,7 +34,7 @@ final class EngineSettingsRepositoryProvider } String _$engineSettingsRepositoryHash() => - r'd0c2fbe060b5eaee7189f6cb0dd58c97cf5987b5'; + r'8cf0a615f825ddc6411c2985d2cdc19ef31251cb'; abstract class _$EngineSettingsRepository extends $StreamNotifier { diff --git a/app/lib/main.dart b/app/lib/main.dart index 2fa279a0..5267409b 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -91,6 +91,7 @@ void main() async { await GeckoBrowserService().initialize( kDebugMode ? LogLevel.debug : LogLevel.warn, engineSettings.contentBlocking, + engineSettings.addonCollection, ); await ref diff --git a/app/lib/utils/form_validators.dart b/app/lib/utils/form_validators.dart index 250a8546..db281b61 100644 --- a/app/lib/utils/form_validators.dart +++ b/app/lib/utils/form_validators.dart @@ -47,3 +47,11 @@ String? validateUrl( return 'Inavlid URL'; } + +String? validateRequired(String? value, {String message = 'Value required'}) { + if (value.isNotEmpty) { + return null; + } + + return message; +} diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/Components.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/Components.kt index 9a939427..05918c6a 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/Components.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/Components.kt @@ -14,6 +14,7 @@ import eu.weblibre.flutter_mozilla_components.components.Features import eu.weblibre.flutter_mozilla_components.components.Search import eu.weblibre.flutter_mozilla_components.components.Services import eu.weblibre.flutter_mozilla_components.components.UseCases +import eu.weblibre.flutter_mozilla_components.pigeons.AddonCollection import eu.weblibre.flutter_mozilla_components.pigeons.BrowserExtensionEvents import eu.weblibre.flutter_mozilla_components.pigeons.ContentBlocking import eu.weblibre.flutter_mozilla_components.pigeons.GeckoAddonEvents @@ -37,6 +38,7 @@ class Components(private val context: Context, val selectionAction: SelectionActionDelegate, val logLevel: Log.Priority, val contentBlocking: ContentBlocking, + val addonCollection: AddonCollection?, private val addonEvents: GeckoAddonEvents, private val tabContentEvents: GeckoTabContentEvents, private val extensionEvents: BrowserExtensionEvents diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/GlobalComponents.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/GlobalComponents.kt index 6c97a8e8..62b2886a 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/GlobalComponents.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/GlobalComponents.kt @@ -7,6 +7,7 @@ package eu.weblibre.flutter_mozilla_components import android.content.Context +import eu.weblibre.flutter_mozilla_components.pigeons.AddonCollection import eu.weblibre.flutter_mozilla_components.pigeons.BrowserExtensionEvents import eu.weblibre.flutter_mozilla_components.pigeons.ContentBlocking import eu.weblibre.flutter_mozilla_components.pigeons.GeckoAddonEvents @@ -60,7 +61,8 @@ object GlobalComponents { tabContentEvents: GeckoTabContentEvents, extensionEvents: BrowserExtensionEvents, logLevel: Log.Priority, - contentBlocking: ContentBlocking + contentBlocking: ContentBlocking, + addonCollection: AddonCollection?, ) { Logger.debug("Creating new components") @@ -71,6 +73,7 @@ object GlobalComponents { selectionAction, logLevel, contentBlocking, + addonCollection, addonEvents, tabContentEvents, extensionEvents, diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoBrowserApiImpl.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoBrowserApiImpl.kt index 5f44fba3..860c7acc 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoBrowserApiImpl.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoBrowserApiImpl.kt @@ -15,6 +15,7 @@ import eu.weblibre.flutter_mozilla_components.GeckoViewFactory import eu.weblibre.flutter_mozilla_components.GlobalComponents import eu.weblibre.flutter_mozilla_components.activities.NotificationActivity import eu.weblibre.flutter_mozilla_components.feature.DefaultSelectionActionDelegate +import eu.weblibre.flutter_mozilla_components.pigeons.AddonCollection import eu.weblibre.flutter_mozilla_components.pigeons.BrowserExtensionEvents import eu.weblibre.flutter_mozilla_components.pigeons.ContentBlocking import eu.weblibre.flutter_mozilla_components.pigeons.GeckoAddonEvents @@ -140,7 +141,11 @@ class GeckoBrowserApiImpl : GeckoBrowserApi { return GeckoViewBuildConfig.MOZ_APP_VERSION + "-" + GeckoViewBuildConfig.MOZ_APP_BUILDID } - override fun initialize(logLevel: LogLevel, contentBlocking: ContentBlocking) { + override fun initialize( + logLevel: LogLevel, + contentBlocking: ContentBlocking, + addonCollection: AddonCollection? + ) { synchronized(this) { if(!isGeckoInitialized) { val geckoLogging = GeckoLogging(_flutterPluginBinding.binaryMessenger) @@ -154,7 +159,7 @@ class GeckoBrowserApiImpl : GeckoBrowserApi { Log.addSink(PriorityAwareLogSink(level, geckoLogging)) - setupGeckoEngine(level, contentBlocking) + setupGeckoEngine(level, contentBlocking, addonCollection) isGeckoInitialized = true } } @@ -170,7 +175,11 @@ class GeckoBrowserApiImpl : GeckoBrowserApi { return false } - private fun setupGeckoEngine(logLevel: Log.Priority, contentBlocking: ContentBlocking) { + private fun setupGeckoEngine( + logLevel: Log.Priority, + contentBlocking: ContentBlocking, + addonCollection: AddonCollection? + ) { val selectionActionEvents = GeckoSelectionActionEvents(_flutterPluginBinding.binaryMessenger) val selectionActionDelegate = DefaultSelectionActionDelegate(selectionActionEvents) { actions -> @@ -201,7 +210,8 @@ class GeckoBrowserApiImpl : GeckoBrowserApi { tabContentEvents, extensionEvents, logLevel, - contentBlocking + contentBlocking, + addonCollection ) GeckoEngineSettingsApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoEngineSettingsApiImpl()) diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/components/Core.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/components/Core.kt index 28ed7112..1d5488d3 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/components/Core.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/components/Core.kt @@ -60,12 +60,13 @@ import mozilla.components.feature.webnotifications.WebNotificationFeature import mozilla.components.support.base.worker.Frequency import java.util.concurrent.TimeUnit -private const val DAY_IN_MINUTES = 24 * 60L +private const val AMO_COLLECTION_MAX_CACHE_AGE = 24 * 60L -class Core(private val context: Context, - private val components: Components, - private val flutterEvents: GeckoStateEvents, - private val extensionEvents: BrowserExtensionEvents +class Core( + private val context: Context, + private val components: Components, + private val flutterEvents: GeckoStateEvents, + private val extensionEvents: BrowserExtensionEvents ) { val prefs by lazy { PreferenceManager.getDefaultSharedPreferences(context) @@ -137,22 +138,33 @@ class Core(private val context: Context, val addonUpdater by lazy { DefaultAddonUpdater( context, - Frequency(1, TimeUnit.DAYS), + Frequency(12, TimeUnit.HOURS), components.notificationsDelegate ) } val addonsProvider by lazy { - AMOAddonsProvider( - context, - client, - collectionName = "7dfae8669acc4312a65e8ba5553036", - maxCacheAgeInMinutes = DAY_IN_MINUTES, - ) + if (components.addonCollection != null) + AMOAddonsProvider( + context, + client, + serverURL = components.addonCollection.serverURL, + collectionUser = components.addonCollection.collectionUser, + collectionName = components.addonCollection.collectionName, + maxCacheAgeInMinutes = AMO_COLLECTION_MAX_CACHE_AGE + ) else + AMOAddonsProvider( + context, + client, + maxCacheAgeInMinutes = AMO_COLLECTION_MAX_CACHE_AGE + ) } val supportedAddonsChecker by lazy { - DefaultSupportedAddonsChecker(context, Frequency(1, TimeUnit.DAYS)) + DefaultSupportedAddonsChecker( + context, + Frequency(12, TimeUnit.HOURS) + ) } val fileUploadsDirCleaner: FileUploadsDirCleaner by lazy { @@ -164,7 +176,7 @@ class Core(private val context: Context, BrowserStore( middleware = listOf( FlutterEventMiddleware(flutterEvents), - DownloadMiddleware(context, DownloadService::class.java, {false}), + DownloadMiddleware(context, DownloadService::class.java, { false }), ThumbnailsMiddleware(thumbnailStorage), ReaderViewMiddleware(), UndoMiddleware(), @@ -227,7 +239,7 @@ class Core(private val context: Context, */ val historyStorage by lazy { lazyHistoryStorage.value } - val permissionStorage by lazy { PermissionStorage( geckoSitePermissionsStorage ) } + val permissionStorage by lazy { PermissionStorage(geckoSitePermissionsStorage) } /** * Constructs a [TrackingProtectionPolicy] based on current preferences. @@ -241,7 +253,7 @@ class Core(private val context: Context, * @return the constructed tracking protection policy based on preferences. */ private fun createTrackingProtectionPolicy( - trackingPolicy: EngineSession. TrackingProtectionPolicyForSessionTypes, + trackingPolicy: EngineSession.TrackingProtectionPolicyForSessionTypes, normalMode: Boolean = true, privateMode: Boolean = true, ): TrackingProtectionPolicy { 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 1dbbb8f6..1c5a1de2 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 @@ -2104,6 +2104,40 @@ data class ShareInternetResourceState ( override fun hashCode(): Int = toList().hashCode() } + +/** Generated class from Pigeon that represents data sent in messages. */ +data class AddonCollection ( + val serverURL: String, + val collectionUser: String, + val collectionName: String +) + { + companion object { + fun fromList(pigeonVar_list: List): AddonCollection { + val serverURL = pigeonVar_list[0] as String + val collectionUser = pigeonVar_list[1] as String + val collectionName = pigeonVar_list[2] as String + return AddonCollection(serverURL, collectionUser, collectionName) + } + } + fun toList(): List { + return listOf( + serverURL, + collectionUser, + collectionName, + ) + } + override fun equals(other: Any?): Boolean { + if (other !is AddonCollection) { + return false + } + if (this === other) { + return true + } + return GeckoPigeonUtils.deepEquals(toList(), other.toList()) } + + override fun hashCode(): Int = toList().hashCode() +} private open class GeckoPigeonCodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { return when (type) { @@ -2402,6 +2436,11 @@ private open class GeckoPigeonCodec : StandardMessageCodec() { ShareInternetResourceState.fromList(it) } } + 188.toByte() -> { + return (readValue(buffer) as? List)?.let { + AddonCollection.fromList(it) + } + } else -> super.readValueOfType(type, buffer) } } @@ -2643,6 +2682,10 @@ private open class GeckoPigeonCodec : StandardMessageCodec() { stream.write(187) writeValue(stream, value.toList()) } + is AddonCollection -> { + stream.write(188) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } @@ -2652,7 +2695,7 @@ private open class GeckoPigeonCodec : StandardMessageCodec() { /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface GeckoBrowserApi { fun getGeckoVersion(): String - fun initialize(logLevel: LogLevel, contentBlocking: ContentBlocking) + fun initialize(logLevel: LogLevel, contentBlocking: ContentBlocking, addonCollection: AddonCollection?) fun showNativeFragment(): Boolean fun onTrimMemory(level: Long) @@ -2687,8 +2730,9 @@ interface GeckoBrowserApi { val args = message as List val logLevelArg = args[0] as LogLevel val contentBlockingArg = args[1] as ContentBlocking + val addonCollectionArg = args[2] as AddonCollection? val wrapped: List = try { - api.initialize(logLevelArg, contentBlockingArg) + api.initialize(logLevelArg, contentBlockingArg, addonCollectionArg) listOf(null) } catch (exception: Throwable) { GeckoPigeonUtils.wrapError(exception) diff --git a/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart b/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart index 4eb58b22..5dc6725d 100644 --- a/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart +++ b/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart @@ -31,6 +31,7 @@ export 'src/domain/services/gecko_tab_content.dart'; export 'src/geckoview_widget.dart'; export 'src/pigeons/gecko.g.dart' show + AddonCollection, AudioHitResult, BounceTrackingProtectionMode, ColorScheme, diff --git a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_browser.dart b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_browser.dart index 86d6bdea..2b5e0e46 100644 --- a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_browser.dart +++ b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_browser.dart @@ -17,8 +17,12 @@ class GeckoBrowserService { return _api.getGeckoVersion(); } - Future initialize(LogLevel logLevel, ContentBlocking contentBlocking) { - return _api.initialize(logLevel, contentBlocking); + Future initialize( + LogLevel logLevel, + ContentBlocking contentBlocking, + AddonCollection? addonCollection, + ) { + return _api.initialize(logLevel, contentBlocking, addonCollection); } Future showNativeFragment() { 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 76648a18..c67001b3 100644 --- a/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart +++ b/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart @@ -2664,6 +2664,57 @@ class ShareInternetResourceState { ; } +class AddonCollection { + AddonCollection({ + required this.serverURL, + required this.collectionUser, + required this.collectionName, + }); + + String serverURL; + + String collectionUser; + + String collectionName; + + List _toList() { + return [ + serverURL, + collectionUser, + collectionName, + ]; + } + + Object encode() { + return _toList(); } + + static AddonCollection decode(Object result) { + result as List; + return AddonCollection( + serverURL: result[0]! as String, + collectionUser: result[1]! as String, + collectionName: result[2]! as String, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! AddonCollection || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()) +; +} + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @@ -2849,6 +2900,9 @@ class _PigeonCodec extends StandardMessageCodec { } else if (value is ShareInternetResourceState) { buffer.putUint8(187); writeValue(buffer, value.encode()); + } else if (value is AddonCollection) { + buffer.putUint8(188); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -2993,6 +3047,8 @@ class _PigeonCodec extends StandardMessageCodec { return DownloadState.decode(readValue(buffer)!); case 187: return ShareInternetResourceState.decode(readValue(buffer)!); + case 188: + return AddonCollection.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -3040,14 +3096,14 @@ class GeckoBrowserApi { } } - Future initialize(LogLevel logLevel, ContentBlocking contentBlocking) async { + Future initialize(LogLevel logLevel, ContentBlocking contentBlocking, AddonCollection? addonCollection) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoBrowserApi.initialize$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([logLevel, contentBlocking]); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([logLevel, contentBlocking, addonCollection]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { diff --git a/packages/flutter_mozilla_components/pigeons/gecko.dart b/packages/flutter_mozilla_components/pigeons/gecko.dart index a1428c73..0e5d878b 100644 --- a/packages/flutter_mozilla_components/pigeons/gecko.dart +++ b/packages/flutter_mozilla_components/pigeons/gecko.dart @@ -827,6 +827,18 @@ class ShareInternetResourceState { enum LogLevel { debug, info, warn, error } +class AddonCollection { + final String serverURL; + final String collectionUser; + final String collectionName; + + AddonCollection({ + required this.serverURL, + required this.collectionUser, + required this.collectionName, + }); +} + @ConfigurePigeon( PigeonOptions( dartOut: 'lib/src/pigeons/gecko.g.dart', @@ -842,7 +854,11 @@ enum LogLevel { debug, info, warn, error } @HostApi() abstract class GeckoBrowserApi { String getGeckoVersion(); - void initialize(LogLevel logLevel, ContentBlocking contentBlocking); + void initialize( + LogLevel logLevel, + ContentBlocking contentBlocking, + AddonCollection? addonCollection, + ); bool showNativeFragment(); void onTrimMemory(int level); }