added support for overriding locales
This commit is contained in:
@@ -45,6 +45,7 @@ import 'package:weblibre/features/settings/presentation/screens/developer_settin
|
|||||||
import 'package:weblibre/features/settings/presentation/screens/doh_settings.dart';
|
import 'package:weblibre/features/settings/presentation/screens/doh_settings.dart';
|
||||||
import 'package:weblibre/features/settings/presentation/screens/fingerprint_settings.dart';
|
import 'package:weblibre/features/settings/presentation/screens/fingerprint_settings.dart';
|
||||||
import 'package:weblibre/features/settings/presentation/screens/general_settings.dart';
|
import 'package:weblibre/features/settings/presentation/screens/general_settings.dart';
|
||||||
|
import 'package:weblibre/features/settings/presentation/screens/locale_settings.dart';
|
||||||
import 'package:weblibre/features/settings/presentation/screens/settings.dart';
|
import 'package:weblibre/features/settings/presentation/screens/settings.dart';
|
||||||
import 'package:weblibre/features/settings/presentation/screens/web_engine_hardening.dart';
|
import 'package:weblibre/features/settings/presentation/screens/web_engine_hardening.dart';
|
||||||
import 'package:weblibre/features/settings/presentation/screens/web_engine_hardening_group.dart';
|
import 'package:weblibre/features/settings/presentation/screens/web_engine_hardening_group.dart';
|
||||||
|
|||||||
@@ -116,6 +116,11 @@ RouteBase get $settingsRoute => GoRouteData.$route(
|
|||||||
name: 'FingerprintSettingsRoute',
|
name: 'FingerprintSettingsRoute',
|
||||||
factory: $FingerprintSettingsRoute._fromState,
|
factory: $FingerprintSettingsRoute._fromState,
|
||||||
),
|
),
|
||||||
|
GoRouteData.$route(
|
||||||
|
path: 'locales',
|
||||||
|
name: 'LocaleSettingsRoute',
|
||||||
|
factory: $LocaleSettingsRoute._fromState,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
GoRouteData.$route(
|
GoRouteData.$route(
|
||||||
@@ -306,6 +311,27 @@ mixin $FingerprintSettingsRoute on GoRouteData {
|
|||||||
void replace(BuildContext context) => context.replace(location);
|
void replace(BuildContext context) => context.replace(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mixin $LocaleSettingsRoute on GoRouteData {
|
||||||
|
static LocaleSettingsRoute _fromState(GoRouterState state) =>
|
||||||
|
LocaleSettingsRoute();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get location => GoRouteData.$location('/settings/web_engine/locales');
|
||||||
|
|
||||||
|
@override
|
||||||
|
void go(BuildContext context) => context.go(location);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void pushReplacement(BuildContext context) =>
|
||||||
|
context.pushReplacement(location);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void replace(BuildContext context) => context.replace(location);
|
||||||
|
}
|
||||||
|
|
||||||
mixin $DeveloperSettingsRoute on GoRouteData {
|
mixin $DeveloperSettingsRoute on GoRouteData {
|
||||||
static DeveloperSettingsRoute _fromState(GoRouterState state) =>
|
static DeveloperSettingsRoute _fromState(GoRouterState state) =>
|
||||||
DeveloperSettingsRoute();
|
DeveloperSettingsRoute();
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ part of 'routes.dart';
|
|||||||
name: 'FingerprintSettingsRoute',
|
name: 'FingerprintSettingsRoute',
|
||||||
path: 'fingerprint',
|
path: 'fingerprint',
|
||||||
),
|
),
|
||||||
|
TypedGoRoute<LocaleSettingsRoute>(
|
||||||
|
name: 'LocaleSettingsRoute',
|
||||||
|
path: 'locales',
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
TypedGoRoute<DeveloperSettingsRoute>(
|
TypedGoRoute<DeveloperSettingsRoute>(
|
||||||
@@ -97,6 +101,13 @@ class FingerprintSettingsRoute extends GoRouteData
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LocaleSettingsRoute extends GoRouteData with $LocaleSettingsRoute {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, GoRouterState state) {
|
||||||
|
return const LocaleSettingsScreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class WebEngineSettingsRoute extends GoRouteData with $WebEngineSettingsRoute {
|
class WebEngineSettingsRoute extends GoRouteData with $WebEngineSettingsRoute {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, GoRouterState state) {
|
Widget build(BuildContext context, GoRouterState state) {
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import 'package:flutter/widgets.dart' hide Locale;
|
||||||
|
import 'package:intl/locale.dart' as intl;
|
||||||
|
import 'package:locale_resolver/locale_resolver.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
import 'package:weblibre/extensions/locale.dart';
|
||||||
|
|
||||||
|
part 'locale_resolver.g.dart';
|
||||||
|
|
||||||
|
@Riverpod(keepAlive: true)
|
||||||
|
class LocaleResolverRepository extends _$LocaleResolverRepository {
|
||||||
|
final _service = LocaleResolver();
|
||||||
|
final _cache = <intl.Locale, LocalizedResult>{};
|
||||||
|
|
||||||
|
Future<LocalizedResult> resolve(intl.Locale locale) async {
|
||||||
|
final cached = _cache[locale];
|
||||||
|
if (cached != null) {
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _cache[locale] = await _service.resolve(
|
||||||
|
locale.toLanguageTag(),
|
||||||
|
targetLocale.toLanguageTag(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void build(intl.Locale targetLocale) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Riverpod()
|
||||||
|
Future<LocalizedResult> resolveLocale(Ref ref, intl.Locale locale) {
|
||||||
|
return ref
|
||||||
|
.read(
|
||||||
|
localeResolverRepositoryProvider(
|
||||||
|
WidgetsBinding.instance.platformDispatcher.locale.toIntlLocale(),
|
||||||
|
).notifier,
|
||||||
|
)
|
||||||
|
.resolve(locale);
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'locale_resolver.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// RiverpodGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint, type=warning
|
||||||
|
|
||||||
|
@ProviderFor(LocaleResolverRepository)
|
||||||
|
const localeResolverRepositoryProvider = LocaleResolverRepositoryFamily._();
|
||||||
|
|
||||||
|
final class LocaleResolverRepositoryProvider
|
||||||
|
extends $NotifierProvider<LocaleResolverRepository, void> {
|
||||||
|
const LocaleResolverRepositoryProvider._({
|
||||||
|
required LocaleResolverRepositoryFamily super.from,
|
||||||
|
required intl.Locale super.argument,
|
||||||
|
}) : super(
|
||||||
|
retry: null,
|
||||||
|
name: r'localeResolverRepositoryProvider',
|
||||||
|
isAutoDispose: false,
|
||||||
|
dependencies: null,
|
||||||
|
$allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String debugGetCreateSourceHash() => _$localeResolverRepositoryHash();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return r'localeResolverRepositoryProvider'
|
||||||
|
''
|
||||||
|
'($argument)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@$internal
|
||||||
|
@override
|
||||||
|
LocaleResolverRepository create() => LocaleResolverRepository();
|
||||||
|
|
||||||
|
/// {@macro riverpod.override_with_value}
|
||||||
|
Override overrideWithValue(void value) {
|
||||||
|
return $ProviderOverride(
|
||||||
|
origin: this,
|
||||||
|
providerOverride: $SyncValueProvider<void>(value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return other is LocaleResolverRepositoryProvider &&
|
||||||
|
other.argument == argument;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return argument.hashCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _$localeResolverRepositoryHash() =>
|
||||||
|
r'dc74908d6ac1f5cc851e2a10dcf33b0dc68b5b15';
|
||||||
|
|
||||||
|
final class LocaleResolverRepositoryFamily extends $Family
|
||||||
|
with
|
||||||
|
$ClassFamilyOverride<
|
||||||
|
LocaleResolverRepository,
|
||||||
|
void,
|
||||||
|
void,
|
||||||
|
void,
|
||||||
|
intl.Locale
|
||||||
|
> {
|
||||||
|
const LocaleResolverRepositoryFamily._()
|
||||||
|
: super(
|
||||||
|
retry: null,
|
||||||
|
name: r'localeResolverRepositoryProvider',
|
||||||
|
dependencies: null,
|
||||||
|
$allTransitiveDependencies: null,
|
||||||
|
isAutoDispose: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
LocaleResolverRepositoryProvider call(intl.Locale targetLocale) =>
|
||||||
|
LocaleResolverRepositoryProvider._(argument: targetLocale, from: this);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => r'localeResolverRepositoryProvider';
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _$LocaleResolverRepository extends $Notifier<void> {
|
||||||
|
late final _$args = ref.$arg as intl.Locale;
|
||||||
|
intl.Locale get targetLocale => _$args;
|
||||||
|
|
||||||
|
void build(intl.Locale targetLocale);
|
||||||
|
@$mustCallSuper
|
||||||
|
@override
|
||||||
|
void runBuild() {
|
||||||
|
build(_$args);
|
||||||
|
final ref = this.ref as $Ref<void, void>;
|
||||||
|
final element =
|
||||||
|
ref.element
|
||||||
|
as $ClassProviderElement<
|
||||||
|
AnyNotifier<void, void>,
|
||||||
|
void,
|
||||||
|
Object?,
|
||||||
|
Object?
|
||||||
|
>;
|
||||||
|
element.handleValue(ref, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ProviderFor(resolveLocale)
|
||||||
|
const resolveLocaleProvider = ResolveLocaleFamily._();
|
||||||
|
|
||||||
|
final class ResolveLocaleProvider
|
||||||
|
extends
|
||||||
|
$FunctionalProvider<
|
||||||
|
AsyncValue<LocalizedResult>,
|
||||||
|
LocalizedResult,
|
||||||
|
FutureOr<LocalizedResult>
|
||||||
|
>
|
||||||
|
with $FutureModifier<LocalizedResult>, $FutureProvider<LocalizedResult> {
|
||||||
|
const ResolveLocaleProvider._({
|
||||||
|
required ResolveLocaleFamily super.from,
|
||||||
|
required intl.Locale super.argument,
|
||||||
|
}) : super(
|
||||||
|
retry: null,
|
||||||
|
name: r'resolveLocaleProvider',
|
||||||
|
isAutoDispose: true,
|
||||||
|
dependencies: null,
|
||||||
|
$allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String debugGetCreateSourceHash() => _$resolveLocaleHash();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return r'resolveLocaleProvider'
|
||||||
|
''
|
||||||
|
'($argument)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@$internal
|
||||||
|
@override
|
||||||
|
$FutureProviderElement<LocalizedResult> $createElement(
|
||||||
|
$ProviderPointer pointer,
|
||||||
|
) => $FutureProviderElement(pointer);
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOr<LocalizedResult> create(Ref ref) {
|
||||||
|
final argument = this.argument as intl.Locale;
|
||||||
|
return resolveLocale(ref, argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return other is ResolveLocaleProvider && other.argument == argument;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return argument.hashCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _$resolveLocaleHash() => r'94d7a9b307a81de372b8b40b06f046acc76e01c8';
|
||||||
|
|
||||||
|
final class ResolveLocaleFamily extends $Family
|
||||||
|
with $FunctionalFamilyOverride<FutureOr<LocalizedResult>, intl.Locale> {
|
||||||
|
const ResolveLocaleFamily._()
|
||||||
|
: super(
|
||||||
|
retry: null,
|
||||||
|
name: r'resolveLocaleProvider',
|
||||||
|
dependencies: null,
|
||||||
|
$allTransitiveDependencies: null,
|
||||||
|
isAutoDispose: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
ResolveLocaleProvider call(intl.Locale locale) =>
|
||||||
|
ResolveLocaleProvider._(argument: locale, from: this);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => r'resolveLocaleProvider';
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
|
import 'package:intl/locale.dart' as intl;
|
||||||
|
|
||||||
|
extension LocaleFormat on intl.Locale {
|
||||||
|
String rawToString(String separator) {
|
||||||
|
final StringBuffer out = StringBuffer(languageCode);
|
||||||
|
if (scriptCode != null && scriptCode!.isNotEmpty) {
|
||||||
|
out.write('$separator$scriptCode');
|
||||||
|
}
|
||||||
|
final String? countryCode = this.countryCode;
|
||||||
|
if (countryCode != null && countryCode.isNotEmpty) {
|
||||||
|
out.write('$separator${this.countryCode}');
|
||||||
|
}
|
||||||
|
return out.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension LocaleConverter on ui.Locale {
|
||||||
|
intl.Locale toIntlLocale() {
|
||||||
|
return intl.Locale.fromSubtags(
|
||||||
|
languageCode: languageCode,
|
||||||
|
countryCode: countryCode,
|
||||||
|
scriptCode: scriptCode,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
@@ -17,6 +17,7 @@
|
|||||||
* You should have received a copy of the GNU Affero General Public License
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart' show ThemeMode;
|
import 'package:flutter/material.dart' show ThemeMode;
|
||||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||||
import 'package:riverpod/riverpod.dart';
|
import 'package:riverpod/riverpod.dart';
|
||||||
@@ -141,6 +142,18 @@ class EngineSettingsReplicationService
|
|||||||
.read(preferenceFixatorProvider.notifier)
|
.read(preferenceFixatorProvider.notifier)
|
||||||
.register('pdfjs.disabled', !settings.enablePdfJs);
|
.register('pdfjs.disabled', !settings.enablePdfJs);
|
||||||
}
|
}
|
||||||
|
if (!const DeepCollectionEquality.unordered().equals(
|
||||||
|
previous.value?.locales,
|
||||||
|
settings.locales,
|
||||||
|
)) {
|
||||||
|
// ignore: only_use_keep_alive_inside_keep_alive
|
||||||
|
await ref
|
||||||
|
.read(preferenceFixatorProvider.notifier)
|
||||||
|
.register(
|
||||||
|
'intl.accept_languages',
|
||||||
|
settings.locales.join(','),
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
await _service.setDefaultSettings(settings);
|
await _service.setDefaultSettings(settings);
|
||||||
|
|
||||||
@@ -149,6 +162,11 @@ class EngineSettingsReplicationService
|
|||||||
.read(preferenceFixatorProvider.notifier)
|
.read(preferenceFixatorProvider.notifier)
|
||||||
.register('pdfjs.disabled', !settings.enablePdfJs);
|
.register('pdfjs.disabled', !settings.enablePdfJs);
|
||||||
|
|
||||||
|
// ignore: only_use_keep_alive_inside_keep_alive
|
||||||
|
await ref
|
||||||
|
.read(preferenceFixatorProvider.notifier)
|
||||||
|
.register('intl.accept_languages', settings.locales.join(','));
|
||||||
|
|
||||||
initialSettingsSent = true;
|
initialSettingsSent = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -44,7 +44,7 @@ final class EngineSettingsReplicationServiceProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$engineSettingsReplicationServiceHash() =>
|
String _$engineSettingsReplicationServiceHash() =>
|
||||||
r'd28cc58a94f45008a478bf52a9c40d61811b591d';
|
r'7583ebc8ca9c11377486ff5a3e763493dcdc903a';
|
||||||
|
|
||||||
abstract class _$EngineSettingsReplicationService extends $Notifier<void> {
|
abstract class _$EngineSettingsReplicationService extends $Notifier<void> {
|
||||||
void build();
|
void build();
|
||||||
|
|||||||
+2
-2
@@ -25,8 +25,6 @@ class PreferenceChangeListener extends _$PreferenceChangeListener {
|
|||||||
@Riverpod()
|
@Riverpod()
|
||||||
class PreferenceFixator extends _$PreferenceFixator {
|
class PreferenceFixator extends _$PreferenceFixator {
|
||||||
Future<void> register(String name, Object value) async {
|
Future<void> register(String name, Object value) async {
|
||||||
await GeckoPrefService().applyPrefs({name: value});
|
|
||||||
|
|
||||||
state = {...state}
|
state = {...state}
|
||||||
..update(
|
..update(
|
||||||
name,
|
name,
|
||||||
@@ -36,6 +34,8 @@ class PreferenceFixator extends _$PreferenceFixator {
|
|||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await GeckoPrefService().applyPrefs({name: value});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> unregister(String name) async {
|
Future<void> unregister(String name) async {
|
||||||
|
|||||||
+1
-1
@@ -87,7 +87,7 @@ final class PreferenceFixatorProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$preferenceFixatorHash() => r'dfa323859c660d3db74dba7297e2a33b758ed78a';
|
String _$preferenceFixatorHash() => r'c76245d5c420e2e3f6c026f23c017c939b69ef59';
|
||||||
|
|
||||||
abstract class _$PreferenceFixator extends $Notifier<Map<String, Object>> {
|
abstract class _$PreferenceFixator extends $Notifier<Map<String, Object>> {
|
||||||
Map<String, Object> build();
|
Map<String, Object> build();
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
import 'package:country_flags/country_flags.dart';
|
||||||
|
import 'package:fast_equatable/fast_equatable.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:intl/locale.dart';
|
||||||
|
import 'package:nullability/nullability.dart';
|
||||||
|
import 'package:weblibre/domain/repositories/locale_resolver.dart';
|
||||||
|
import 'package:weblibre/extensions/locale.dart';
|
||||||
|
import 'package:weblibre/features/settings/presentation/controllers/save_settings.dart';
|
||||||
|
import 'package:weblibre/features/user/data/models/engine_settings.dart';
|
||||||
|
import 'package:weblibre/features/user/domain/repositories/engine_settings.dart';
|
||||||
|
|
||||||
|
class LocaleSettingsScreen extends HookConsumerWidget {
|
||||||
|
const LocaleSettingsScreen();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final formKey = useMemoized(() => GlobalKey<FormState>());
|
||||||
|
|
||||||
|
final systemLocales = useMemoized(
|
||||||
|
() => WidgetsBinding.instance.platformDispatcher.locales
|
||||||
|
.map((locale) => locale.toIntlLocale())
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
|
||||||
|
final userLocales = ref.watch(
|
||||||
|
engineSettingsWithDefaultsProvider.select(
|
||||||
|
(settings) => EquatableValue(
|
||||||
|
settings.locales.map(Locale.tryParse).nonNulls.toSet(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final availableLocales = {
|
||||||
|
...systemLocales,
|
||||||
|
...userLocales.value,
|
||||||
|
Locale.fromSubtags(languageCode: 'en', countryCode: 'US'),
|
||||||
|
};
|
||||||
|
|
||||||
|
final customLocaleController = useTextEditingController();
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(title: const Text('Browser Languages')),
|
||||||
|
body: SafeArea(
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
...availableLocales.map((locale) {
|
||||||
|
return CheckboxListTile.adaptive(
|
||||||
|
value: userLocales.value.contains(locale),
|
||||||
|
onChanged: (value) async {
|
||||||
|
if (value != null) {
|
||||||
|
await ref
|
||||||
|
.read(saveEngineSettingsControllerProvider.notifier)
|
||||||
|
.save(
|
||||||
|
(currentSettings) => currentSettings.copyWith.locales(
|
||||||
|
value
|
||||||
|
? {
|
||||||
|
...currentSettings.locales,
|
||||||
|
locale.toLanguageTag(),
|
||||||
|
}.toList()
|
||||||
|
: ([
|
||||||
|
...currentSettings.locales,
|
||||||
|
]..remove(locale.toLanguageTag())).toList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title: Consumer(
|
||||||
|
builder: (context, ref, child) {
|
||||||
|
final resolvedAsync = ref.watch(
|
||||||
|
resolveLocaleProvider(locale),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Text(
|
||||||
|
resolvedAsync.maybeWhen(
|
||||||
|
data: (data) =>
|
||||||
|
data.mapNotNull(
|
||||||
|
(data) =>
|
||||||
|
'${data.languageName} ${data.countryName.mapNotNull((country) => '($country)') ?? ''}'
|
||||||
|
.trim(),
|
||||||
|
) ??
|
||||||
|
locale.toLanguageTag(),
|
||||||
|
orElse: () => locale.toLanguageTag(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
subtitle: Text(locale.toLanguageTag()),
|
||||||
|
secondary: CountryFlag.fromLanguageCode(
|
||||||
|
locale.languageCode,
|
||||||
|
theme: const ImageTheme(shape: RoundedRectangle(8.0)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
const Divider(),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 16.0, right: 20),
|
||||||
|
child: Form(
|
||||||
|
key: formKey,
|
||||||
|
child: TextFormField(
|
||||||
|
controller: customLocaleController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
label: const Text('Custom Locale'),
|
||||||
|
hint: const Text('en-US'),
|
||||||
|
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (formKey.currentState?.validate() == true) {
|
||||||
|
formKey.currentState?.save();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
validator: (value) {
|
||||||
|
if (value != null && Locale.tryParse(value) == null) {
|
||||||
|
return 'Invalid locale identifier';
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
onSaved: (newValue) async {
|
||||||
|
if (newValue != null) {
|
||||||
|
await ref
|
||||||
|
.read(saveEngineSettingsControllerProvider.notifier)
|
||||||
|
.save(
|
||||||
|
(currentSettings) =>
|
||||||
|
currentSettings.copyWith.locales(
|
||||||
|
{
|
||||||
|
...currentSettings.locales,
|
||||||
|
Locale.parse(newValue).toLanguageTag(),
|
||||||
|
}.toList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
customLocaleController.clear();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -203,6 +203,18 @@ class WebEngineSettingsScreen extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
title: const Text('Browser Languages'),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 8.0,
|
||||||
|
horizontal: 16.0,
|
||||||
|
),
|
||||||
|
leading: const Icon(MdiIcons.translate),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
onTap: () async {
|
||||||
|
await LocaleSettingsRoute().push(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
SwitchListTile.adaptive(
|
SwitchListTile.adaptive(
|
||||||
title: const Text('Global Privacy Control (GPC)'),
|
title: const Text('Global Privacy Control (GPC)'),
|
||||||
secondary: const Icon(MdiIcons.incognitoCircleOff),
|
secondary: const Icon(MdiIcons.incognitoCircleOff),
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import 'dart:convert';
|
|||||||
|
|
||||||
import 'package:copy_with_extension/copy_with_extension.dart';
|
import 'package:copy_with_extension/copy_with_extension.dart';
|
||||||
import 'package:fast_equatable/fast_equatable.dart';
|
import 'package:fast_equatable/fast_equatable.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
import 'package:nullability/nullability.dart';
|
import 'package:nullability/nullability.dart';
|
||||||
@@ -75,6 +76,9 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
|||||||
@override
|
@override
|
||||||
bool get enterpriseRootsEnabled => super.enterpriseRootsEnabled!;
|
bool get enterpriseRootsEnabled => super.enterpriseRootsEnabled!;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<String> get locales => super.locales!;
|
||||||
|
|
||||||
final QueryParameterStripping queryParameterStripping;
|
final QueryParameterStripping queryParameterStripping;
|
||||||
|
|
||||||
final BounceTrackingProtectionMode bounceTrackingProtectionMode;
|
final BounceTrackingProtectionMode bounceTrackingProtectionMode;
|
||||||
@@ -130,6 +134,7 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
|||||||
required this.dohExceptionsList,
|
required this.dohExceptionsList,
|
||||||
required super.fingerprintingProtectionOverrides,
|
required super.fingerprintingProtectionOverrides,
|
||||||
required this.enablePdfJs,
|
required this.enablePdfJs,
|
||||||
|
required super.locales,
|
||||||
});
|
});
|
||||||
|
|
||||||
EngineSettings.withDefaults({
|
EngineSettings.withDefaults({
|
||||||
@@ -154,6 +159,7 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
|||||||
List<String>? dohExceptionsList,
|
List<String>? dohExceptionsList,
|
||||||
String? fingerprintingProtectionOverrides,
|
String? fingerprintingProtectionOverrides,
|
||||||
bool? enablePdfJs,
|
bool? enablePdfJs,
|
||||||
|
List<String>? locales,
|
||||||
}) : queryParameterStripping =
|
}) : queryParameterStripping =
|
||||||
queryParameterStripping ?? QueryParameterStripping.disabled,
|
queryParameterStripping ?? QueryParameterStripping.disabled,
|
||||||
bounceTrackingProtectionMode =
|
bounceTrackingProtectionMode =
|
||||||
@@ -188,6 +194,11 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
|||||||
fingerprintingProtectionOverrides:
|
fingerprintingProtectionOverrides:
|
||||||
fingerprintingProtectionOverrides ??
|
fingerprintingProtectionOverrides ??
|
||||||
FingerprintOverrides.defaults().toString(),
|
FingerprintOverrides.defaults().toString(),
|
||||||
|
locales:
|
||||||
|
locales ??
|
||||||
|
WidgetsBinding.instance.platformDispatcher.locales
|
||||||
|
.map((x) => x.toLanguageTag())
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
|
|
||||||
static AddonCollection? _addonCollectionFromJson(String? json) =>
|
static AddonCollection? _addonCollectionFromJson(String? json) =>
|
||||||
@@ -226,5 +237,6 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
|||||||
dohExceptionsList,
|
dohExceptionsList,
|
||||||
fingerprintingProtectionOverrides,
|
fingerprintingProtectionOverrides,
|
||||||
enablePdfJs,
|
enablePdfJs,
|
||||||
|
locales,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ abstract class _$EngineSettingsCWProxy {
|
|||||||
|
|
||||||
EngineSettings enablePdfJs(bool enablePdfJs);
|
EngineSettings enablePdfJs(bool enablePdfJs);
|
||||||
|
|
||||||
|
EngineSettings locales(List<String>? locales);
|
||||||
|
|
||||||
/// Creates a new instance with the provided field values.
|
/// 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)`.
|
/// 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)`.
|
||||||
///
|
///
|
||||||
@@ -96,6 +98,7 @@ abstract class _$EngineSettingsCWProxy {
|
|||||||
List<String> dohExceptionsList,
|
List<String> dohExceptionsList,
|
||||||
String? fingerprintingProtectionOverrides,
|
String? fingerprintingProtectionOverrides,
|
||||||
bool enablePdfJs,
|
bool enablePdfJs,
|
||||||
|
List<String>? locales,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,6 +210,9 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy {
|
|||||||
EngineSettings enablePdfJs(bool enablePdfJs) =>
|
EngineSettings enablePdfJs(bool enablePdfJs) =>
|
||||||
call(enablePdfJs: enablePdfJs);
|
call(enablePdfJs: enablePdfJs);
|
||||||
|
|
||||||
|
@override
|
||||||
|
EngineSettings locales(List<String>? locales) => call(locales: locales);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
/// Creates a new instance with the provided field values.
|
/// 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)`.
|
/// 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)`.
|
||||||
@@ -239,6 +245,7 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy {
|
|||||||
Object? dohExceptionsList = const $CopyWithPlaceholder(),
|
Object? dohExceptionsList = const $CopyWithPlaceholder(),
|
||||||
Object? fingerprintingProtectionOverrides = const $CopyWithPlaceholder(),
|
Object? fingerprintingProtectionOverrides = const $CopyWithPlaceholder(),
|
||||||
Object? enablePdfJs = const $CopyWithPlaceholder(),
|
Object? enablePdfJs = const $CopyWithPlaceholder(),
|
||||||
|
Object? locales = const $CopyWithPlaceholder(),
|
||||||
}) {
|
}) {
|
||||||
return EngineSettings(
|
return EngineSettings(
|
||||||
javascriptEnabled: javascriptEnabled == const $CopyWithPlaceholder()
|
javascriptEnabled: javascriptEnabled == const $CopyWithPlaceholder()
|
||||||
@@ -350,6 +357,10 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy {
|
|||||||
? _value.enablePdfJs
|
? _value.enablePdfJs
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
: enablePdfJs as bool,
|
: enablePdfJs as bool,
|
||||||
|
locales: locales == const $CopyWithPlaceholder()
|
||||||
|
? _value.locales
|
||||||
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
: locales as List<String>?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -422,6 +433,9 @@ EngineSettings _$EngineSettingsFromJson(Map<String, dynamic> json) =>
|
|||||||
fingerprintingProtectionOverrides:
|
fingerprintingProtectionOverrides:
|
||||||
json['fingerprintingProtectionOverrides'] as String?,
|
json['fingerprintingProtectionOverrides'] as String?,
|
||||||
enablePdfJs: json['enablePdfJs'] as bool?,
|
enablePdfJs: json['enablePdfJs'] as bool?,
|
||||||
|
locales: (json['locales'] as List<dynamic>?)
|
||||||
|
?.map((e) => e as String)
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$EngineSettingsToJson(
|
Map<String, dynamic> _$EngineSettingsToJson(
|
||||||
@@ -448,6 +462,7 @@ Map<String, dynamic> _$EngineSettingsToJson(
|
|||||||
_$WebContentIsolationStrategyEnumMap[instance
|
_$WebContentIsolationStrategyEnumMap[instance
|
||||||
.webContentIsolationStrategy]!,
|
.webContentIsolationStrategy]!,
|
||||||
'enterpriseRootsEnabled': instance.enterpriseRootsEnabled,
|
'enterpriseRootsEnabled': instance.enterpriseRootsEnabled,
|
||||||
|
'locales': instance.locales,
|
||||||
'queryParameterStripping':
|
'queryParameterStripping':
|
||||||
_$QueryParameterStrippingEnumMap[instance.queryParameterStripping]!,
|
_$QueryParameterStrippingEnumMap[instance.queryParameterStripping]!,
|
||||||
'bounceTrackingProtectionMode':
|
'bounceTrackingProtectionMode':
|
||||||
|
|||||||
@@ -124,6 +124,9 @@ class EngineSettingsRepository extends _$EngineSettingsRepository {
|
|||||||
DriftSqlType.bool,
|
DriftSqlType.bool,
|
||||||
db.typeMapping,
|
db.typeMapping,
|
||||||
),
|
),
|
||||||
|
'locales': settings['locales']
|
||||||
|
?.readAs(DriftSqlType.string, db.typeMapping)
|
||||||
|
.mapNotNull(jsonDecode),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ final class EngineSettingsRepositoryProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$engineSettingsRepositoryHash() =>
|
String _$engineSettingsRepositoryHash() =>
|
||||||
r'cd087c5631faaddf1fced31569b0fd577d6030a7';
|
r'916abcd932fc3b3b1e060bc93a46c1a3a38bb984';
|
||||||
|
|
||||||
abstract class _$EngineSettingsRepository
|
abstract class _$EngineSettingsRepository
|
||||||
extends $StreamNotifier<EngineSettings> {
|
extends $StreamNotifier<EngineSettings> {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ dependencies:
|
|||||||
background_fetch: ^1.4.0
|
background_fetch: ^1.4.0
|
||||||
collection: ^1.19.1
|
collection: ^1.19.1
|
||||||
copy_with_extension: ^9.1.1
|
copy_with_extension: ^9.1.1
|
||||||
|
country_flags: ^4.1.0
|
||||||
drift: ^2.29.0
|
drift: ^2.29.0
|
||||||
dynamic_color: ^1.8.1
|
dynamic_color: ^1.8.1
|
||||||
exceptions: ^0.6.1
|
exceptions: ^0.6.1
|
||||||
@@ -42,6 +43,8 @@ dependencies:
|
|||||||
lexo_rank:
|
lexo_rank:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/FaFre/lexo_rank.git
|
url: https://github.com/FaFre/lexo_rank.git
|
||||||
|
locale_resolver:
|
||||||
|
path: ../packages/locale_resolver
|
||||||
local_auth: ^3.0.0
|
local_auth: ^3.0.0
|
||||||
logger: ^2.6.2
|
logger: ^2.6.2
|
||||||
markdown: ^7.3.0
|
markdown: ^7.3.0
|
||||||
|
|||||||
+1
@@ -73,6 +73,7 @@ object EngineProvider {
|
|||||||
//builder.debugLogging(components.logLevel == Log.Priority.DEBUG)
|
//builder.debugLogging(components.logLevel == Log.Priority.DEBUG)
|
||||||
builder.consoleOutput(components.logLevel == Log.Priority.DEBUG)
|
builder.consoleOutput(components.logLevel == Log.Priority.DEBUG)
|
||||||
builder.contentBlocking(contentBlocking.build())
|
builder.contentBlocking(contentBlocking.build())
|
||||||
|
builder.locales(arrayOf("en-US", "en")) // Will be overridden later
|
||||||
|
|
||||||
runtime = GeckoRuntime.create(context, builder.build())
|
runtime = GeckoRuntime.create(context, builder.build())
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
package eu.weblibre.flutter_mozilla_components.api
|
package eu.weblibre.flutter_mozilla_components.api
|
||||||
|
|
||||||
|
import eu.weblibre.flutter_mozilla_components.EngineProvider
|
||||||
import eu.weblibre.flutter_mozilla_components.GlobalComponents
|
import eu.weblibre.flutter_mozilla_components.GlobalComponents
|
||||||
import eu.weblibre.flutter_mozilla_components.pigeons.ColorScheme
|
import eu.weblibre.flutter_mozilla_components.pigeons.ColorScheme
|
||||||
import eu.weblibre.flutter_mozilla_components.pigeons.CookieBannerHandlingMode
|
import eu.weblibre.flutter_mozilla_components.pigeons.CookieBannerHandlingMode
|
||||||
@@ -147,6 +148,10 @@ class GeckoEngineSettingsApiImpl : GeckoEngineSettingsApi {
|
|||||||
if(settings.fingerprintingProtectionOverrides != null) {
|
if(settings.fingerprintingProtectionOverrides != null) {
|
||||||
components.core.engineSettings.fingerprintingProtectionOverrides = settings.fingerprintingProtectionOverrides
|
components.core.engineSettings.fingerprintingProtectionOverrides = settings.fingerprintingProtectionOverrides
|
||||||
}
|
}
|
||||||
|
if(settings.locales != null) {
|
||||||
|
// components.core.engineSettings.automaticLanguageAdjustment = false
|
||||||
|
components.core.runtime.settings.locales = settings.locales.toTypedArray()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateRuntimeSettings(settings: GeckoEngineSettings) {
|
override fun updateRuntimeSettings(settings: GeckoEngineSettings) {
|
||||||
|
|||||||
+6
@@ -14,6 +14,7 @@ import eu.weblibre.flutter_mozilla_components.Components
|
|||||||
import eu.weblibre.flutter_mozilla_components.interceptor.AppRequestInterceptor
|
import eu.weblibre.flutter_mozilla_components.interceptor.AppRequestInterceptor
|
||||||
import eu.weblibre.flutter_mozilla_components.services.DownloadService
|
import eu.weblibre.flutter_mozilla_components.services.DownloadService
|
||||||
import eu.weblibre.flutter_mozilla_components.EngineProvider
|
import eu.weblibre.flutter_mozilla_components.EngineProvider
|
||||||
|
import eu.weblibre.flutter_mozilla_components.EngineProvider.getOrCreateRuntime
|
||||||
import eu.weblibre.flutter_mozilla_components.PermissionStorage
|
import eu.weblibre.flutter_mozilla_components.PermissionStorage
|
||||||
import eu.weblibre.flutter_mozilla_components.services.MediaSessionService
|
import eu.weblibre.flutter_mozilla_components.services.MediaSessionService
|
||||||
import eu.weblibre.flutter_mozilla_components.activities.NotificationActivity
|
import eu.weblibre.flutter_mozilla_components.activities.NotificationActivity
|
||||||
@@ -58,6 +59,7 @@ import mozilla.components.feature.session.middleware.undo.UndoMiddleware
|
|||||||
import mozilla.components.feature.sitepermissions.OnDiskSitePermissionsStorage
|
import mozilla.components.feature.sitepermissions.OnDiskSitePermissionsStorage
|
||||||
import mozilla.components.feature.webnotifications.WebNotificationFeature
|
import mozilla.components.feature.webnotifications.WebNotificationFeature
|
||||||
import mozilla.components.support.base.worker.Frequency
|
import mozilla.components.support.base.worker.Frequency
|
||||||
|
import org.mozilla.geckoview.GeckoRuntime
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
private const val AMO_COLLECTION_MAX_CACHE_AGE = 24 * 60L
|
private const val AMO_COLLECTION_MAX_CACHE_AGE = 24 * 60L
|
||||||
@@ -107,6 +109,10 @@ class Core(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val runtime: GeckoRuntime by lazy {
|
||||||
|
getOrCreateRuntime(context)
|
||||||
|
}
|
||||||
|
|
||||||
val engine: Engine by lazy {
|
val engine: Engine by lazy {
|
||||||
EngineProvider.createEngine(context, engineSettings, extensionEvents)
|
EngineProvider.createEngine(context, engineSettings, extensionEvents)
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -1678,7 +1678,8 @@ data class GeckoEngineSettings (
|
|||||||
val contentBlocking: ContentBlocking? = null,
|
val contentBlocking: ContentBlocking? = null,
|
||||||
val enterpriseRootsEnabled: Boolean? = null,
|
val enterpriseRootsEnabled: Boolean? = null,
|
||||||
val dohSettings: DohSettings? = null,
|
val dohSettings: DohSettings? = null,
|
||||||
val fingerprintingProtectionOverrides: String? = null
|
val fingerprintingProtectionOverrides: String? = null,
|
||||||
|
val locales: List<String>? = null
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
companion object {
|
companion object {
|
||||||
@@ -1698,7 +1699,8 @@ data class GeckoEngineSettings (
|
|||||||
val enterpriseRootsEnabled = pigeonVar_list[12] as Boolean?
|
val enterpriseRootsEnabled = pigeonVar_list[12] as Boolean?
|
||||||
val dohSettings = pigeonVar_list[13] as DohSettings?
|
val dohSettings = pigeonVar_list[13] as DohSettings?
|
||||||
val fingerprintingProtectionOverrides = pigeonVar_list[14] as String?
|
val fingerprintingProtectionOverrides = pigeonVar_list[14] as String?
|
||||||
return GeckoEngineSettings(javascriptEnabled, trackingProtectionPolicy, httpsOnlyMode, globalPrivacyControlEnabled, preferredColorScheme, cookieBannerHandlingMode, cookieBannerHandlingModePrivateBrowsing, cookieBannerHandlingGlobalRules, cookieBannerHandlingGlobalRulesSubFrames, webContentIsolationStrategy, userAgent, contentBlocking, enterpriseRootsEnabled, dohSettings, fingerprintingProtectionOverrides)
|
val locales = pigeonVar_list[15] as List<String>?
|
||||||
|
return GeckoEngineSettings(javascriptEnabled, trackingProtectionPolicy, httpsOnlyMode, globalPrivacyControlEnabled, preferredColorScheme, cookieBannerHandlingMode, cookieBannerHandlingModePrivateBrowsing, cookieBannerHandlingGlobalRules, cookieBannerHandlingGlobalRulesSubFrames, webContentIsolationStrategy, userAgent, contentBlocking, enterpriseRootsEnabled, dohSettings, fingerprintingProtectionOverrides, locales)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun toList(): List<Any?> {
|
fun toList(): List<Any?> {
|
||||||
@@ -1718,6 +1720,7 @@ data class GeckoEngineSettings (
|
|||||||
enterpriseRootsEnabled,
|
enterpriseRootsEnabled,
|
||||||
dohSettings,
|
dohSettings,
|
||||||
fingerprintingProtectionOverrides,
|
fingerprintingProtectionOverrides,
|
||||||
|
locales,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
|
|||||||
@@ -2044,6 +2044,7 @@ class GeckoEngineSettings {
|
|||||||
this.enterpriseRootsEnabled,
|
this.enterpriseRootsEnabled,
|
||||||
this.dohSettings,
|
this.dohSettings,
|
||||||
this.fingerprintingProtectionOverrides,
|
this.fingerprintingProtectionOverrides,
|
||||||
|
this.locales,
|
||||||
});
|
});
|
||||||
|
|
||||||
bool? javascriptEnabled;
|
bool? javascriptEnabled;
|
||||||
@@ -2076,6 +2077,8 @@ class GeckoEngineSettings {
|
|||||||
|
|
||||||
String? fingerprintingProtectionOverrides;
|
String? fingerprintingProtectionOverrides;
|
||||||
|
|
||||||
|
List<String>? locales;
|
||||||
|
|
||||||
List<Object?> _toList() {
|
List<Object?> _toList() {
|
||||||
return <Object?>[
|
return <Object?>[
|
||||||
javascriptEnabled,
|
javascriptEnabled,
|
||||||
@@ -2093,6 +2096,7 @@ class GeckoEngineSettings {
|
|||||||
enterpriseRootsEnabled,
|
enterpriseRootsEnabled,
|
||||||
dohSettings,
|
dohSettings,
|
||||||
fingerprintingProtectionOverrides,
|
fingerprintingProtectionOverrides,
|
||||||
|
locales,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2117,6 +2121,7 @@ class GeckoEngineSettings {
|
|||||||
enterpriseRootsEnabled: result[12] as bool?,
|
enterpriseRootsEnabled: result[12] as bool?,
|
||||||
dohSettings: result[13] as DohSettings?,
|
dohSettings: result[13] as DohSettings?,
|
||||||
fingerprintingProtectionOverrides: result[14] as String?,
|
fingerprintingProtectionOverrides: result[14] as String?,
|
||||||
|
locales: (result[15] as List<Object?>?)?.cast<String>(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -667,6 +667,7 @@ class GeckoEngineSettings {
|
|||||||
final bool? enterpriseRootsEnabled;
|
final bool? enterpriseRootsEnabled;
|
||||||
final DohSettings? dohSettings;
|
final DohSettings? dohSettings;
|
||||||
final String? fingerprintingProtectionOverrides;
|
final String? fingerprintingProtectionOverrides;
|
||||||
|
final List<String>? locales;
|
||||||
|
|
||||||
GeckoEngineSettings(
|
GeckoEngineSettings(
|
||||||
this.javascriptEnabled,
|
this.javascriptEnabled,
|
||||||
@@ -684,6 +685,7 @@ class GeckoEngineSettings {
|
|||||||
this.enterpriseRootsEnabled,
|
this.enterpriseRootsEnabled,
|
||||||
this.dohSettings,
|
this.dohSettings,
|
||||||
this.fingerprintingProtectionOverrides,
|
this.fingerprintingProtectionOverrides,
|
||||||
|
this.locales,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.build/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
.swiftpm/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||||
|
/pubspec.lock
|
||||||
|
**/doc/api/
|
||||||
|
.dart_tool/
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
/build/
|
||||||
|
/coverage/
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: "9f455d2486bcb28cad87b062475f42edc959f636"
|
||||||
|
channel: "stable"
|
||||||
|
|
||||||
|
project_type: plugin
|
||||||
|
|
||||||
|
# Tracks metadata for the flutter migrate command
|
||||||
|
migration:
|
||||||
|
platforms:
|
||||||
|
- platform: root
|
||||||
|
create_revision: 9f455d2486bcb28cad87b062475f42edc959f636
|
||||||
|
base_revision: 9f455d2486bcb28cad87b062475f42edc959f636
|
||||||
|
- platform: android
|
||||||
|
create_revision: 9f455d2486bcb28cad87b062475f42edc959f636
|
||||||
|
base_revision: 9f455d2486bcb28cad87b062475f42edc959f636
|
||||||
|
|
||||||
|
# User provided section
|
||||||
|
|
||||||
|
# List of Local paths (relative to this file) that should be
|
||||||
|
# ignored by the migrate tool.
|
||||||
|
#
|
||||||
|
# Files that are not part of the templates will be ignored by default.
|
||||||
|
unmanaged_files:
|
||||||
|
- 'lib/main.dart'
|
||||||
|
- 'ios/Runner.xcodeproj/project.pbxproj'
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
## 0.0.1
|
||||||
|
|
||||||
|
* TODO: Describe initial release.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
TODO: Add your license here.
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# locale_resolver
|
||||||
|
|
||||||
|
A new Flutter plugin project.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
This project is a starting point for a Flutter
|
||||||
|
[plug-in package](https://flutter.dev/to/develop-plugins),
|
||||||
|
a specialized package that includes platform-specific implementation code for
|
||||||
|
Android and/or iOS.
|
||||||
|
|
||||||
|
For help getting started with Flutter development, view the
|
||||||
|
[online documentation](https://docs.flutter.dev), which offers tutorials,
|
||||||
|
samples, guidance on mobile development, and a full API reference.
|
||||||
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# This file configures the static analysis results for your project (errors,
|
||||||
|
# warnings, and lints).
|
||||||
|
#
|
||||||
|
# This enables the 'recommended' set of lints from `package:lints`.
|
||||||
|
# This set helps identify many issues that may lead to problems when running
|
||||||
|
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||||
|
# style and format.
|
||||||
|
#
|
||||||
|
# If you want a smaller set of lints you can change this to specify
|
||||||
|
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||||
|
# (the recommended set includes the core lints).
|
||||||
|
# The core lints are also what is used by pub.dev for scoring packages.
|
||||||
|
|
||||||
|
include: package:lint/package.yaml
|
||||||
|
# Uncomment the following section to specify additional rules.
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
unawaited_futures: true
|
||||||
|
discarded_futures: true
|
||||||
|
collection_methods_unrelated_type: true
|
||||||
|
|
||||||
|
analyzer:
|
||||||
|
plugins:
|
||||||
|
- custom_lint
|
||||||
|
exclude:
|
||||||
|
- "**.g.dart"
|
||||||
|
- "**.swagger.dart"
|
||||||
|
- "**.freezed.dart"
|
||||||
|
- "**.chopper.dart"
|
||||||
|
# For more information about the core and recommended set of lints, see
|
||||||
|
# https://dart.dev/go/core-lints
|
||||||
|
|
||||||
|
# For additional information about configuring this file, see
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/libraries
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.cxx
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
group = "eu.weblibre.locale_resolver"
|
||||||
|
version = "1.0-SNAPSHOT"
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
ext.kotlin_version = "2.1.20"
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath("com.android.tools.build:gradle:8.11.1")
|
||||||
|
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apply plugin: "com.android.library"
|
||||||
|
apply plugin: "kotlin-android"
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "eu.weblibre.locale_resolver"
|
||||||
|
|
||||||
|
compileSdk = 36
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main.java.srcDirs += "src/main/kotlin"
|
||||||
|
test.java.srcDirs += "src/test/kotlin"
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk = 24
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testImplementation("org.jetbrains.kotlin:kotlin-test")
|
||||||
|
testImplementation("org.mockito:mockito-core:5.0.0")
|
||||||
|
}
|
||||||
|
|
||||||
|
testOptions {
|
||||||
|
unitTests.all {
|
||||||
|
useJUnitPlatform()
|
||||||
|
|
||||||
|
testLogging {
|
||||||
|
events "passed", "skipped", "failed", "standardOut", "standardError"
|
||||||
|
outputs.upToDateWhen {false}
|
||||||
|
showStandardStreams = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
rootProject.name = 'locale_resolver'
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="eu.weblibre.locale_resolver">
|
||||||
|
</manifest>
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package eu.weblibre.locale_resolver
|
||||||
|
|
||||||
|
import eu.weblibre.locale_resolver.pigeons.LocaleResolver
|
||||||
|
import eu.weblibre.locale_resolver.pigeons.LocalizedResult
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class LocaleResolverImpl : LocaleResolver {
|
||||||
|
override fun resolve(languageTag: String, targetLangouageTag: String): LocalizedResult {
|
||||||
|
val deviceLocale = Locale.forLanguageTag(targetLangouageTag)
|
||||||
|
val targetLocale = Locale.forLanguageTag(languageTag)
|
||||||
|
|
||||||
|
val languageName = targetLocale.getDisplayLanguage(deviceLocale)
|
||||||
|
val countryName = targetLocale.getDisplayCountry(deviceLocale)
|
||||||
|
|
||||||
|
return LocalizedResult(
|
||||||
|
languageName,
|
||||||
|
countryName.ifBlank { null }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
package eu.weblibre.locale_resolver
|
||||||
|
|
||||||
|
import eu.weblibre.locale_resolver.pigeons.LocaleResolver
|
||||||
|
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||||
|
import io.flutter.plugin.common.MethodCall
|
||||||
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||||
|
import io.flutter.plugin.common.MethodChannel.Result
|
||||||
|
|
||||||
|
/** LocaleResolverPlugin */
|
||||||
|
class LocaleResolverPlugin : FlutterPlugin {
|
||||||
|
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||||
|
LocaleResolver.setUp(flutterPluginBinding.binaryMessenger, LocaleResolverImpl())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+166
@@ -0,0 +1,166 @@
|
|||||||
|
// Autogenerated from Pigeon (v26.0.1), do not edit directly.
|
||||||
|
// See also: https://pub.dev/packages/pigeon
|
||||||
|
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
|
||||||
|
|
||||||
|
package eu.weblibre.locale_resolver.pigeons
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import io.flutter.plugin.common.BasicMessageChannel
|
||||||
|
import io.flutter.plugin.common.BinaryMessenger
|
||||||
|
import io.flutter.plugin.common.EventChannel
|
||||||
|
import io.flutter.plugin.common.MessageCodec
|
||||||
|
import io.flutter.plugin.common.StandardMethodCodec
|
||||||
|
import io.flutter.plugin.common.StandardMessageCodec
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
private object LocalesPigeonUtils {
|
||||||
|
|
||||||
|
fun wrapResult(result: Any?): List<Any?> {
|
||||||
|
return listOf(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun wrapError(exception: Throwable): List<Any?> {
|
||||||
|
return if (exception is FlutterError) {
|
||||||
|
listOf(
|
||||||
|
exception.code,
|
||||||
|
exception.message,
|
||||||
|
exception.details
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
listOf(
|
||||||
|
exception.javaClass.simpleName,
|
||||||
|
exception.toString(),
|
||||||
|
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun deepEquals(a: Any?, b: Any?): Boolean {
|
||||||
|
if (a is ByteArray && b is ByteArray) {
|
||||||
|
return a.contentEquals(b)
|
||||||
|
}
|
||||||
|
if (a is IntArray && b is IntArray) {
|
||||||
|
return a.contentEquals(b)
|
||||||
|
}
|
||||||
|
if (a is LongArray && b is LongArray) {
|
||||||
|
return a.contentEquals(b)
|
||||||
|
}
|
||||||
|
if (a is DoubleArray && b is DoubleArray) {
|
||||||
|
return a.contentEquals(b)
|
||||||
|
}
|
||||||
|
if (a is Array<*> && b is Array<*>) {
|
||||||
|
return a.size == b.size &&
|
||||||
|
a.indices.all{ deepEquals(a[it], b[it]) }
|
||||||
|
}
|
||||||
|
if (a is List<*> && b is List<*>) {
|
||||||
|
return a.size == b.size &&
|
||||||
|
a.indices.all{ deepEquals(a[it], b[it]) }
|
||||||
|
}
|
||||||
|
if (a is Map<*, *> && b is Map<*, *>) {
|
||||||
|
return a.size == b.size && a.all {
|
||||||
|
(b as Map<Any?, Any?>).containsKey(it.key) &&
|
||||||
|
deepEquals(it.value, b[it.key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a == b
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error class for passing custom error details to Flutter via a thrown PlatformException.
|
||||||
|
* @property code The error code.
|
||||||
|
* @property message The error message.
|
||||||
|
* @property details The error details. Must be a datatype supported by the api codec.
|
||||||
|
*/
|
||||||
|
class FlutterError (
|
||||||
|
val code: String,
|
||||||
|
override val message: String? = null,
|
||||||
|
val details: Any? = null
|
||||||
|
) : Throwable()
|
||||||
|
|
||||||
|
/** Generated class from Pigeon that represents data sent in messages. */
|
||||||
|
data class LocalizedResult (
|
||||||
|
val languageName: String,
|
||||||
|
val countryName: String? = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
companion object {
|
||||||
|
fun fromList(pigeonVar_list: List<Any?>): LocalizedResult {
|
||||||
|
val languageName = pigeonVar_list[0] as String
|
||||||
|
val countryName = pigeonVar_list[1] as String?
|
||||||
|
return LocalizedResult(languageName, countryName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun toList(): List<Any?> {
|
||||||
|
return listOf(
|
||||||
|
languageName,
|
||||||
|
countryName,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (other !is LocalizedResult) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (this === other) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return LocalesPigeonUtils.deepEquals(toList(), other.toList()) }
|
||||||
|
|
||||||
|
override fun hashCode(): Int = toList().hashCode()
|
||||||
|
}
|
||||||
|
private open class LocalesPigeonCodec : StandardMessageCodec() {
|
||||||
|
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
|
||||||
|
return when (type) {
|
||||||
|
129.toByte() -> {
|
||||||
|
return (readValue(buffer) as? List<Any?>)?.let {
|
||||||
|
LocalizedResult.fromList(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> super.readValueOfType(type, buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
|
||||||
|
when (value) {
|
||||||
|
is LocalizedResult -> {
|
||||||
|
stream.write(129)
|
||||||
|
writeValue(stream, value.toList())
|
||||||
|
}
|
||||||
|
else -> super.writeValue(stream, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||||
|
interface LocaleResolver {
|
||||||
|
fun resolve(languageTag: String, targetLangouageTag: String): LocalizedResult
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/** The codec used by LocaleResolver. */
|
||||||
|
val codec: MessageCodec<Any?> by lazy {
|
||||||
|
LocalesPigeonCodec()
|
||||||
|
}
|
||||||
|
/** Sets up an instance of `LocaleResolver` to handle messages through the `binaryMessenger`. */
|
||||||
|
@JvmOverloads
|
||||||
|
fun setUp(binaryMessenger: BinaryMessenger, api: LocaleResolver?, messageChannelSuffix: String = "") {
|
||||||
|
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.locale_resolver.LocaleResolver.resolve$separatedMessageChannelSuffix", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { message, reply ->
|
||||||
|
val args = message as List<Any?>
|
||||||
|
val languageTagArg = args[0] as String
|
||||||
|
val targetLangouageTagArg = args[1] as String
|
||||||
|
val wrapped: List<Any?> = try {
|
||||||
|
listOf(api.resolve(languageTagArg, targetLangouageTagArg))
|
||||||
|
} catch (exception: Throwable) {
|
||||||
|
LocalesPigeonUtils.wrapError(exception)
|
||||||
|
}
|
||||||
|
reply.reply(wrapped)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
package eu.weblibre.locale_resolver
|
||||||
|
|
||||||
|
import io.flutter.plugin.common.MethodCall
|
||||||
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
import org.mockito.Mockito
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation.
|
||||||
|
*
|
||||||
|
* Once you have built the plugin's example app, you can run these tests from the command
|
||||||
|
* line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or
|
||||||
|
* you can run them directly from IDEs that support JUnit such as Android Studio.
|
||||||
|
*/
|
||||||
|
|
||||||
|
internal class LocaleResolverPluginTest {
|
||||||
|
@Test
|
||||||
|
fun onMethodCall_getPlatformVersion_returnsExpectedValue() {
|
||||||
|
val plugin = LocaleResolverPlugin()
|
||||||
|
|
||||||
|
val call = MethodCall("getPlatformVersion", null)
|
||||||
|
val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java)
|
||||||
|
plugin.onMethodCall(call, mockResult)
|
||||||
|
|
||||||
|
Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.build/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
.swiftpm/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
**/doc/api/
|
||||||
|
**/ios/Flutter/.last_build_id
|
||||||
|
.dart_tool/
|
||||||
|
.flutter-plugins-dependencies
|
||||||
|
.pub-cache/
|
||||||
|
.pub/
|
||||||
|
/build/
|
||||||
|
/coverage/
|
||||||
|
|
||||||
|
# Symbolication related
|
||||||
|
app.*.symbols
|
||||||
|
|
||||||
|
# Obfuscation related
|
||||||
|
app.*.map.json
|
||||||
|
|
||||||
|
# Android Studio will place build artifacts here
|
||||||
|
/android/app/debug
|
||||||
|
/android/app/profile
|
||||||
|
/android/app/release
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# locale_resolver_example
|
||||||
|
|
||||||
|
Demonstrates how to use the locale_resolver plugin.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
This project is a starting point for a Flutter application.
|
||||||
|
|
||||||
|
A few resources to get you started if this is your first Flutter project:
|
||||||
|
|
||||||
|
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
||||||
|
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
||||||
|
|
||||||
|
For help getting started with Flutter development, view the
|
||||||
|
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
||||||
|
samples, guidance on mobile development, and a full API reference.
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# This file configures the analyzer, which statically analyzes Dart code to
|
||||||
|
# check for errors, warnings, and lints.
|
||||||
|
#
|
||||||
|
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
||||||
|
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
||||||
|
# invoked from the command line by running `flutter analyze`.
|
||||||
|
|
||||||
|
# The following line activates a set of recommended lints for Flutter apps,
|
||||||
|
# packages, and plugins designed to encourage good coding practices.
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
linter:
|
||||||
|
# The lint rules applied to this project can be customized in the
|
||||||
|
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
||||||
|
# included above or to enable additional rules. A list of all available lints
|
||||||
|
# and their documentation is published at https://dart.dev/lints.
|
||||||
|
#
|
||||||
|
# Instead of disabling a lint rule for the entire project in the
|
||||||
|
# section below, it can also be suppressed for a single line of code
|
||||||
|
# or a specific dart file by using the `// ignore: name_of_lint` and
|
||||||
|
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
||||||
|
# producing the lint.
|
||||||
|
rules:
|
||||||
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
gradle-wrapper.jar
|
||||||
|
/.gradle
|
||||||
|
/captures/
|
||||||
|
/gradlew
|
||||||
|
/gradlew.bat
|
||||||
|
/local.properties
|
||||||
|
GeneratedPluginRegistrant.java
|
||||||
|
.cxx/
|
||||||
|
|
||||||
|
# Remember to never publicly share your keystore.
|
||||||
|
# See https://flutter.dev/to/reference-keystore
|
||||||
|
key.properties
|
||||||
|
**/*.keystore
|
||||||
|
**/*.jks
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
plugins {
|
||||||
|
id("com.android.application")
|
||||||
|
id("kotlin-android")
|
||||||
|
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
||||||
|
id("dev.flutter.flutter-gradle-plugin")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "eu.weblibre.locale_resolver_example"
|
||||||
|
compileSdk = flutter.compileSdkVersion
|
||||||
|
ndkVersion = flutter.ndkVersion
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = JavaVersion.VERSION_11.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
|
applicationId = "eu.weblibre.locale_resolver_example"
|
||||||
|
// You can update the following values to match your application needs.
|
||||||
|
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||||
|
minSdk = flutter.minSdkVersion
|
||||||
|
targetSdk = flutter.targetSdkVersion
|
||||||
|
versionCode = flutter.versionCode
|
||||||
|
versionName = flutter.versionName
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
// TODO: Add your own signing config for the release build.
|
||||||
|
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||||
|
signingConfig = signingConfigs.getByName("debug")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flutter {
|
||||||
|
source = "../.."
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
|
the Flutter tool needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<application
|
||||||
|
android:label="locale_resolver_example"
|
||||||
|
android:name="${applicationName}"
|
||||||
|
android:icon="@mipmap/ic_launcher">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:taskAffinity=""
|
||||||
|
android:theme="@style/LaunchTheme"
|
||||||
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
|
android:windowSoftInputMode="adjustResize">
|
||||||
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
|
the Android process has started. This theme is visible to the user
|
||||||
|
while the Flutter UI initializes. After that, this theme continues
|
||||||
|
to determine the Window background behind the Flutter UI. -->
|
||||||
|
<meta-data
|
||||||
|
android:name="io.flutter.embedding.android.NormalTheme"
|
||||||
|
android:resource="@style/NormalTheme"
|
||||||
|
/>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<!-- Don't delete the meta-data below.
|
||||||
|
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||||
|
<meta-data
|
||||||
|
android:name="flutterEmbedding"
|
||||||
|
android:value="2" />
|
||||||
|
</application>
|
||||||
|
<!-- Required to query activities that can process text, see:
|
||||||
|
https://developer.android.com/training/package-visibility and
|
||||||
|
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
||||||
|
|
||||||
|
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
||||||
|
<queries>
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
||||||
|
<data android:mimeType="text/plain"/>
|
||||||
|
</intent>
|
||||||
|
</queries>
|
||||||
|
</manifest>
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package eu.weblibre.locale_resolver_example
|
||||||
|
|
||||||
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
|
||||||
|
class MainActivity : FlutterActivity()
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Modify this file to customize your launch splash screen -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="?android:colorBackground" />
|
||||||
|
|
||||||
|
<!-- You can insert your own image assets here -->
|
||||||
|
<!-- <item>
|
||||||
|
<bitmap
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@mipmap/launch_image" />
|
||||||
|
</item> -->
|
||||||
|
</layer-list>
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Modify this file to customize your launch splash screen -->
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@android:color/white" />
|
||||||
|
|
||||||
|
<!-- You can insert your own image assets here -->
|
||||||
|
<!-- <item>
|
||||||
|
<bitmap
|
||||||
|
android:gravity="center"
|
||||||
|
android:src="@mipmap/launch_image" />
|
||||||
|
</item> -->
|
||||||
|
</layer-list>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 544 B |
Binary file not shown.
|
After Width: | Height: | Size: 442 B |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 721 B |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
|
the Flutter engine draws its first frame -->
|
||||||
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||||
|
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<!-- Show a splash screen on the activity. Automatically removed when
|
||||||
|
the Flutter engine draws its first frame -->
|
||||||
|
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||||
|
</style>
|
||||||
|
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||||
|
This theme determines the color of the Android Window while your
|
||||||
|
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||||
|
running.
|
||||||
|
|
||||||
|
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||||
|
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||||
|
<item name="android:windowBackground">?android:colorBackground</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- The INTERNET permission is required for development. Specifically,
|
||||||
|
the Flutter tool needs it to communicate with the running application
|
||||||
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
|
-->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val newBuildDir: Directory =
|
||||||
|
rootProject.layout.buildDirectory
|
||||||
|
.dir("../../build")
|
||||||
|
.get()
|
||||||
|
rootProject.layout.buildDirectory.value(newBuildDir)
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
|
||||||
|
project.layout.buildDirectory.value(newSubprojectBuildDir)
|
||||||
|
}
|
||||||
|
subprojects {
|
||||||
|
project.evaluationDependsOn(":app")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register<Delete>("clean") {
|
||||||
|
delete(rootProject.layout.buildDirectory)
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=true
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
pluginManagement {
|
||||||
|
val flutterSdkPath =
|
||||||
|
run {
|
||||||
|
val properties = java.util.Properties()
|
||||||
|
file("local.properties").inputStream().use { properties.load(it) }
|
||||||
|
val flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||||
|
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
|
||||||
|
flutterSdkPath
|
||||||
|
}
|
||||||
|
|
||||||
|
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
||||||
|
id("com.android.application") version "8.9.1" apply false
|
||||||
|
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
|
||||||
|
}
|
||||||
|
|
||||||
|
include(":app")
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
// import 'package:flutter/material.dart';
|
||||||
|
// import 'dart:async';
|
||||||
|
|
||||||
|
// import 'package:flutter/services.dart';
|
||||||
|
// import 'package:locale_resolver/locale_resolver.dart';
|
||||||
|
|
||||||
|
// void main() {
|
||||||
|
// runApp(const MyApp());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// class MyApp extends StatefulWidget {
|
||||||
|
// const MyApp({super.key});
|
||||||
|
|
||||||
|
// @override
|
||||||
|
// State<MyApp> createState() => _MyAppState();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// class _MyAppState extends State<MyApp> {
|
||||||
|
// String _platformVersion = 'Unknown';
|
||||||
|
// final _localeResolverPlugin = LocaleResolver();
|
||||||
|
|
||||||
|
// @override
|
||||||
|
// void initState() {
|
||||||
|
// super.initState();
|
||||||
|
// initPlatformState();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Platform messages are asynchronous, so we initialize in an async method.
|
||||||
|
// Future<void> initPlatformState() async {
|
||||||
|
// String platformVersion;
|
||||||
|
// // Platform messages may fail, so we use a try/catch PlatformException.
|
||||||
|
// // We also handle the message potentially returning null.
|
||||||
|
// try {
|
||||||
|
// platformVersion =
|
||||||
|
// await _localeResolverPlugin.getPlatformVersion() ?? 'Unknown platform version';
|
||||||
|
// } on PlatformException {
|
||||||
|
// platformVersion = 'Failed to get platform version.';
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // If the widget was removed from the tree while the asynchronous platform
|
||||||
|
// // message was in flight, we want to discard the reply rather than calling
|
||||||
|
// // setState to update our non-existent appearance.
|
||||||
|
// if (!mounted) return;
|
||||||
|
|
||||||
|
// setState(() {
|
||||||
|
// _platformVersion = platformVersion;
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// @override
|
||||||
|
// Widget build(BuildContext context) {
|
||||||
|
// return MaterialApp(
|
||||||
|
// home: Scaffold(
|
||||||
|
// appBar: AppBar(
|
||||||
|
// title: const Text('Plugin example app'),
|
||||||
|
// ),
|
||||||
|
// body: Center(
|
||||||
|
// child: Text('Running on: $_platformVersion\n'),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
// }
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
name: locale_resolver_example
|
||||||
|
description: "Demonstrates how to use the locale_resolver plugin."
|
||||||
|
# The following line prevents the package from being accidentally published to
|
||||||
|
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||||
|
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
|
resolution: workspace
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ^3.9.2
|
||||||
|
|
||||||
|
# Dependencies specify other packages that your package needs in order to work.
|
||||||
|
# To automatically upgrade your package dependencies to the latest versions
|
||||||
|
# consider running `flutter pub upgrade --major-versions`. Alternatively,
|
||||||
|
# dependencies can be manually updated by changing the version numbers below to
|
||||||
|
# the latest version available on pub.dev. To see which dependencies have newer
|
||||||
|
# versions available, run `flutter pub outdated`.
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
locale_resolver:
|
||||||
|
# When depending on this package from a real application you should use:
|
||||||
|
# locale_resolver: ^x.y.z
|
||||||
|
# See https://dart.dev/tools/pub/dependencies#version-constraints
|
||||||
|
# The example app is bundled with the plugin so we use a path dependency on
|
||||||
|
# the parent directory to use the current plugin's version.
|
||||||
|
path: ../
|
||||||
|
|
||||||
|
# The following adds the Cupertino Icons font to your application.
|
||||||
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
|
cupertino_icons: ^1.0.8
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
integration_test:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
# The "flutter_lints" package below contains a set of recommended lints to
|
||||||
|
# encourage good coding practices. The lint set provided by the package is
|
||||||
|
# activated in the `analysis_options.yaml` file located at the root of your
|
||||||
|
# package. See that file for information about deactivating specific lint
|
||||||
|
# rules and activating additional ones.
|
||||||
|
flutter_lints: ^6.0.0
|
||||||
|
|
||||||
|
# For information on the generic Dart part of this file, see the
|
||||||
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
||||||
|
# The following section is specific to Flutter packages.
|
||||||
|
flutter:
|
||||||
|
|
||||||
|
# The following line ensures that the Material Icons font is
|
||||||
|
# included with your application, so that you can use the icons in
|
||||||
|
# the material Icons class.
|
||||||
|
uses-material-design: true
|
||||||
|
|
||||||
|
# To add assets to your application, add an assets section, like this:
|
||||||
|
# assets:
|
||||||
|
# - images/a_dot_burr.jpeg
|
||||||
|
# - images/a_dot_ham.jpeg
|
||||||
|
|
||||||
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
|
# https://flutter.dev/to/resolution-aware-images
|
||||||
|
|
||||||
|
# For details regarding adding assets from package dependencies, see
|
||||||
|
# https://flutter.dev/to/asset-from-package
|
||||||
|
|
||||||
|
# To add custom fonts to your application, add a fonts section here,
|
||||||
|
# in this "flutter" section. Each entry in this list should have a
|
||||||
|
# "family" key with the font family name, and a "fonts" key with a
|
||||||
|
# list giving the asset and other descriptors for the font. For
|
||||||
|
# example:
|
||||||
|
# fonts:
|
||||||
|
# - family: Schyler
|
||||||
|
# fonts:
|
||||||
|
# - asset: fonts/Schyler-Regular.ttf
|
||||||
|
# - asset: fonts/Schyler-Italic.ttf
|
||||||
|
# style: italic
|
||||||
|
# - family: Trajan Pro
|
||||||
|
# fonts:
|
||||||
|
# - asset: fonts/TrajanPro.ttf
|
||||||
|
# - asset: fonts/TrajanPro_Bold.ttf
|
||||||
|
# weight: 700
|
||||||
|
#
|
||||||
|
# For details regarding fonts from package dependencies,
|
||||||
|
# see https://flutter.dev/to/font-from-package
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export 'src/pigeons/locales.g.dart' show LocaleResolver, LocalizedResult;
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
// Autogenerated from Pigeon (v26.0.1), do not edit directly.
|
||||||
|
// See also: https://pub.dev/packages/pigeon
|
||||||
|
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
PlatformException _createConnectionError(String channelName) {
|
||||||
|
return PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel: "$channelName".',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
bool _deepEquals(Object? a, Object? b) {
|
||||||
|
if (a is List && b is List) {
|
||||||
|
return a.length == b.length &&
|
||||||
|
a.indexed
|
||||||
|
.every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1]));
|
||||||
|
}
|
||||||
|
if (a is Map && b is Map) {
|
||||||
|
return a.length == b.length && a.entries.every((MapEntry<Object?, Object?> entry) =>
|
||||||
|
(b as Map<Object?, Object?>).containsKey(entry.key) &&
|
||||||
|
_deepEquals(entry.value, b[entry.key]));
|
||||||
|
}
|
||||||
|
return a == b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class LocalizedResult {
|
||||||
|
LocalizedResult({
|
||||||
|
required this.languageName,
|
||||||
|
this.countryName,
|
||||||
|
});
|
||||||
|
|
||||||
|
String languageName;
|
||||||
|
|
||||||
|
String? countryName;
|
||||||
|
|
||||||
|
List<Object?> _toList() {
|
||||||
|
return <Object?>[
|
||||||
|
languageName,
|
||||||
|
countryName,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
Object encode() {
|
||||||
|
return _toList(); }
|
||||||
|
|
||||||
|
static LocalizedResult decode(Object result) {
|
||||||
|
result as List<Object?>;
|
||||||
|
return LocalizedResult(
|
||||||
|
languageName: result[0]! as String,
|
||||||
|
countryName: result[1] as String?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
// ignore: avoid_equals_and_hash_code_on_mutable_classes
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (other is! LocalizedResult || 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();
|
||||||
|
@override
|
||||||
|
void writeValue(WriteBuffer buffer, Object? value) {
|
||||||
|
if (value is int) {
|
||||||
|
buffer.putUint8(4);
|
||||||
|
buffer.putInt64(value);
|
||||||
|
} else if (value is LocalizedResult) {
|
||||||
|
buffer.putUint8(129);
|
||||||
|
writeValue(buffer, value.encode());
|
||||||
|
} else {
|
||||||
|
super.writeValue(buffer, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object? readValueOfType(int type, ReadBuffer buffer) {
|
||||||
|
switch (type) {
|
||||||
|
case 129:
|
||||||
|
return LocalizedResult.decode(readValue(buffer)!);
|
||||||
|
default:
|
||||||
|
return super.readValueOfType(type, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LocaleResolver {
|
||||||
|
/// Constructor for [LocaleResolver]. The [binaryMessenger] named argument is
|
||||||
|
/// available for dependency injection. If it is left null, the default
|
||||||
|
/// BinaryMessenger will be used which routes to the host platform.
|
||||||
|
LocaleResolver({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})
|
||||||
|
: pigeonVar_binaryMessenger = binaryMessenger,
|
||||||
|
pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
|
||||||
|
final BinaryMessenger? pigeonVar_binaryMessenger;
|
||||||
|
|
||||||
|
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
|
||||||
|
|
||||||
|
final String pigeonVar_messageChannelSuffix;
|
||||||
|
|
||||||
|
Future<LocalizedResult> resolve(String languageTag, String targetLangouageTag) async {
|
||||||
|
final String pigeonVar_channelName = 'dev.flutter.pigeon.locale_resolver.LocaleResolver.resolve$pigeonVar_messageChannelSuffix';
|
||||||
|
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||||
|
pigeonVar_channelName,
|
||||||
|
pigeonChannelCodec,
|
||||||
|
binaryMessenger: pigeonVar_binaryMessenger,
|
||||||
|
);
|
||||||
|
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[languageTag, targetLangouageTag]);
|
||||||
|
final List<Object?>? pigeonVar_replyList =
|
||||||
|
await pigeonVar_sendFuture as List<Object?>?;
|
||||||
|
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 LocalizedResult?)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import 'package:pigeon/pigeon.dart';
|
||||||
|
|
||||||
|
class LocalizedResult {
|
||||||
|
final String languageName;
|
||||||
|
final String? countryName;
|
||||||
|
|
||||||
|
LocalizedResult(this.languageName, this.countryName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigurePigeon(
|
||||||
|
PigeonOptions(
|
||||||
|
dartOut: 'lib/src/pigeons/locales.g.dart',
|
||||||
|
dartOptions: DartOptions(),
|
||||||
|
kotlinOut:
|
||||||
|
'android/src/main/kotlin/eu/weblibre/locale_resolver/pigeons/Locales.g.kt',
|
||||||
|
kotlinOptions: KotlinOptions(
|
||||||
|
package: 'eu.weblibre.locale_resolver.pigeons',
|
||||||
|
),
|
||||||
|
dartPackageName: 'locale_resolver',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
@HostApi()
|
||||||
|
abstract class LocaleResolver {
|
||||||
|
LocalizedResult resolve(String languageTag, String targetLangouageTag);
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
name: locale_resolver
|
||||||
|
description: "A new Flutter plugin project."
|
||||||
|
version: 0.0.1
|
||||||
|
publish_to: 'none'
|
||||||
|
resolution: workspace
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ^3.9.2
|
||||||
|
flutter: '>=3.3.0'
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
lint: ^2.8.0
|
||||||
|
pigeon: ^26.0.2
|
||||||
|
|
||||||
|
# For information on the generic Dart part of this file, see the
|
||||||
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|
|
||||||
|
# The following section is specific to Flutter packages.
|
||||||
|
flutter:
|
||||||
|
# This section identifies this Flutter project as a plugin project.
|
||||||
|
# The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)
|
||||||
|
# which should be registered in the plugin registry. This is required for
|
||||||
|
# using method channels.
|
||||||
|
# The Android 'package' specifies package in which the registered class is.
|
||||||
|
# This is required for using method channels on Android.
|
||||||
|
# The 'ffiPlugin' specifies that native code should be built and bundled.
|
||||||
|
# This is required for using `dart:ffi`.
|
||||||
|
# All these are used by the tooling to maintain consistency when
|
||||||
|
# adding or updating assets for this project.
|
||||||
|
plugin:
|
||||||
|
platforms:
|
||||||
|
android:
|
||||||
|
package: eu.weblibre.locale_resolver
|
||||||
|
pluginClass: LocaleResolverPlugin
|
||||||
|
|
||||||
|
# To add assets to your plugin package, add an assets section, like this:
|
||||||
|
# assets:
|
||||||
|
# - images/a_dot_burr.jpeg
|
||||||
|
# - images/a_dot_ham.jpeg
|
||||||
|
#
|
||||||
|
# For details regarding assets in packages, see
|
||||||
|
# https://flutter.dev/to/asset-from-package
|
||||||
|
#
|
||||||
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
|
# https://flutter.dev/to/resolution-aware-images
|
||||||
|
|
||||||
|
# To add custom fonts to your plugin package, add a fonts section here,
|
||||||
|
# in this "flutter" section. Each entry in this list should have a
|
||||||
|
# "family" key with the font family name, and a "fonts" key with a
|
||||||
|
# list giving the asset and other descriptors for the font. For
|
||||||
|
# example:
|
||||||
|
# fonts:
|
||||||
|
# - family: Schyler
|
||||||
|
# fonts:
|
||||||
|
# - asset: fonts/Schyler-Regular.ttf
|
||||||
|
# - asset: fonts/Schyler-Italic.ttf
|
||||||
|
# style: italic
|
||||||
|
# - family: Trajan Pro
|
||||||
|
# fonts:
|
||||||
|
# - asset: fonts/TrajanPro.ttf
|
||||||
|
# - asset: fonts/TrajanPro_Bold.ttf
|
||||||
|
# weight: 700
|
||||||
|
#
|
||||||
|
# For details regarding fonts in packages, see
|
||||||
|
# https://flutter.dev/to/font-from-package
|
||||||
@@ -10,6 +10,8 @@ workspace:
|
|||||||
- packages/simple_intent_receiver
|
- packages/simple_intent_receiver
|
||||||
- packages/simple_intent_receiver/example
|
- packages/simple_intent_receiver/example
|
||||||
- packages/tor
|
- packages/tor
|
||||||
|
- packages/locale_resolver
|
||||||
|
- packages/locale_resolver/example
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
melos: ^7.2.0
|
melos: ^7.2.0
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user