diff --git a/app/lib/domain/services/app_initialization.dart b/app/lib/domain/services/app_initialization.dart index 3b25a1d2..e8812132 100644 --- a/app/lib/domain/services/app_initialization.dart +++ b/app/lib/domain/services/app_initialization.dart @@ -22,7 +22,7 @@ import 'dart:async'; import 'package:exceptions/exceptions.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:weblibre/core/providers/format.dart'; -import 'package:weblibre/features/about/data/repositories/package_info_repository.dart'; +import 'package:weblibre/features/about/domain/providers.dart'; import 'package:weblibre/features/bangs/data/models/bang_group.dart'; import 'package:weblibre/features/bangs/domain/repositories/sync.dart'; diff --git a/app/lib/features/about/data/repositories/package_info_repository.dart b/app/lib/features/about/data/repositories/package_info_repository.dart deleted file mode 100644 index 9f1c0cdd..00000000 --- a/app/lib/features/about/data/repositories/package_info_repository.dart +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2024-2025 Fabian Freund. - * - * This file is part of WebLibre - * (see https://weblibre.eu). - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -import 'package:package_info_plus/package_info_plus.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; - -part 'package_info_repository.g.dart'; - -@Riverpod(keepAlive: true) -Future packageInfo(Ref ref) { - return PackageInfo.fromPlatform(); -} diff --git a/app/lib/features/about/domain/providers.dart b/app/lib/features/about/domain/providers.dart new file mode 100644 index 00000000..6133e6c9 --- /dev/null +++ b/app/lib/features/about/domain/providers.dart @@ -0,0 +1,15 @@ +import 'package:flutter_mozilla_components/flutter_mozilla_components.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'providers.g.dart'; + +@Riverpod(keepAlive: true) +Future packageInfo(Ref ref) { + return PackageInfo.fromPlatform(); +} + +@Riverpod() +Future geckoVersion(Ref ref) { + return GeckoBrowserService().getGeckoVersion(); +} diff --git a/app/lib/features/about/data/repositories/package_info_repository.g.dart b/app/lib/features/about/domain/providers.g.dart similarity index 58% rename from app/lib/features/about/data/repositories/package_info_repository.g.dart rename to app/lib/features/about/domain/providers.g.dart index b1654369..04026669 100644 --- a/app/lib/features/about/data/repositories/package_info_repository.g.dart +++ b/app/lib/features/about/domain/providers.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'package_info_repository.dart'; +part of 'providers.dart'; // ************************************************************************** // RiverpodGenerator @@ -47,3 +47,36 @@ final class PackageInfoProvider } String _$packageInfoHash() => r'cc57db7b4684ab0d5df0f050b8ea045a3658e89a'; + +@ProviderFor(geckoVersion) +const geckoVersionProvider = GeckoVersionProvider._(); + +final class GeckoVersionProvider + extends $FunctionalProvider, String, FutureOr> + with $FutureModifier, $FutureProvider { + const GeckoVersionProvider._() + : super( + from: null, + argument: null, + retry: null, + name: r'geckoVersionProvider', + isAutoDispose: true, + dependencies: null, + $allTransitiveDependencies: null, + ); + + @override + String debugGetCreateSourceHash() => _$geckoVersionHash(); + + @$internal + @override + $FutureProviderElement $createElement($ProviderPointer pointer) => + $FutureProviderElement(pointer); + + @override + FutureOr create(Ref ref) { + return geckoVersion(ref); + } +} + +String _$geckoVersionHash() => r'3a8639b5d1d07c60a789cf705ede09a91cbaee63'; diff --git a/app/lib/features/about/presentation/screens/about.dart b/app/lib/features/about/presentation/screens/about.dart index 36c869c7..300e4229 100644 --- a/app/lib/features/about/presentation/screens/about.dart +++ b/app/lib/features/about/presentation/screens/about.dart @@ -20,7 +20,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:weblibre/features/about/data/repositories/package_info_repository.dart'; +import 'package:weblibre/features/about/domain/providers.dart'; class AboutDialogScreen extends HookConsumerWidget { const AboutDialogScreen(); @@ -42,6 +42,18 @@ class AboutDialogScreen extends HookConsumerWidget { applicationName: packageInfo.appName, applicationVersion: packageInfo.version, applicationLegalese: 'Copyright © Fabian Freund, 2025', + children: [ + Padding( + padding: const EdgeInsets.only(top: 32.0), + child: Consumer( + builder: (context, ref, child) { + final geckoVersion = ref.watch(geckoVersionProvider); + + return Text('Gecko Version: ${geckoVersion.value}'); + }, + ), + ), + ], ); } } 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 2c9f4c9c..5f44fba3 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 @@ -52,6 +52,7 @@ import mozilla.components.support.base.log.Log import mozilla.components.support.base.log.sink.AndroidLogSink import mozilla.components.support.base.log.sink.LogSink import org.mozilla.gecko.util.ThreadUtils.runOnUiThread +import org.mozilla.geckoview.BuildConfig as GeckoViewBuildConfig class PriorityAwareLogSink( private val minLogPriority: Log.Priority, @@ -135,6 +136,10 @@ class GeckoBrowserApiImpl : GeckoBrowserApi { isPlatformViewRegistered = false } + override fun getGeckoVersion(): String { + return GeckoViewBuildConfig.MOZ_APP_VERSION + "-" + GeckoViewBuildConfig.MOZ_APP_BUILDID + } + override fun initialize(logLevel: LogLevel, contentBlocking: ContentBlocking) { synchronized(this) { if(!isGeckoInitialized) { 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 867aa4ff..ea7a8987 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 @@ -2648,6 +2648,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 showNativeFragment(): Boolean fun onTrimMemory(level: Long) @@ -2661,6 +2662,21 @@ interface GeckoBrowserApi { @JvmOverloads fun setUp(binaryMessenger: BinaryMessenger, api: GeckoBrowserApi?, messageChannelSuffix: String = "") { val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoBrowserApi.getGeckoVersion$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = try { + listOf(api.getGeckoVersion()) + } catch (exception: Throwable) { + GeckoPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoBrowserApi.initialize$separatedMessageChannelSuffix", codec) if (api != null) { 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 d70c67c9..86d6bdea 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 @@ -13,6 +13,10 @@ class GeckoBrowserService { GeckoBrowserService({GeckoBrowserApi? api}) : _api = api ?? _apiInstance; + Future getGeckoVersion() { + return _api.getGeckoVersion(); + } + Future initialize(LogLevel logLevel, ContentBlocking contentBlocking) { return _api.initialize(logLevel, contentBlocking); } 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 205ee1a3..6ae5f31e 100644 --- a/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart +++ b/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart @@ -3007,6 +3007,34 @@ class GeckoBrowserApi { final String pigeonVar_messageChannelSuffix; + Future getGeckoVersion() async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoBrowserApi.getGeckoVersion$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 if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as String?)!; + } + } + Future initialize(LogLevel logLevel, ContentBlocking contentBlocking) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoBrowserApi.initialize$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( diff --git a/packages/flutter_mozilla_components/pigeons/gecko.dart b/packages/flutter_mozilla_components/pigeons/gecko.dart index 6287ebd4..f2afaf7b 100644 --- a/packages/flutter_mozilla_components/pigeons/gecko.dart +++ b/packages/flutter_mozilla_components/pigeons/gecko.dart @@ -838,6 +838,7 @@ enum LogLevel { debug, info, warn, error } ) @HostApi() abstract class GeckoBrowserApi { + String getGeckoVersion(); void initialize(LogLevel logLevel, ContentBlocking contentBlocking); bool showNativeFragment(); void onTrimMemory(int level);