import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:obtainium/pages/home.dart'; import 'package:obtainium/providers/apps_provider.dart'; import 'package:obtainium/providers/logs_provider.dart'; import 'package:obtainium/providers/native_provider.dart'; import 'package:obtainium/providers/notifications_provider.dart'; import 'package:obtainium/providers/settings_provider.dart'; import 'package:obtainium/providers/source_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; import 'package:dynamic_color/dynamic_color.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:background_fetch/background_fetch.dart'; import 'package:easy_localization/easy_localization.dart'; // ignore: implementation_imports import 'package:easy_localization/src/easy_localization_controller.dart'; // ignore: implementation_imports import 'package:easy_localization/src/localization.dart'; List> supportedLocales = const [ MapEntry(Locale('en'), 'English'), MapEntry(Locale('zh'), '简体中文'), MapEntry(Locale('zh', 'Hant_TW'), '臺灣話'), MapEntry(Locale('it'), 'Italiano'), MapEntry(Locale('ja'), '日本語'), MapEntry(Locale('hu'), 'Magyar'), MapEntry(Locale('de'), 'Deutsch'), MapEntry(Locale('fa'), 'فارسی'), MapEntry(Locale('fr'), 'Français'), MapEntry(Locale('es'), 'Español'), MapEntry(Locale('pl'), 'Polski'), MapEntry(Locale('ru'), 'Русский'), MapEntry(Locale('bs'), 'Bosanski'), MapEntry(Locale('pt'), 'Português'), MapEntry(Locale('pt', 'BR'), 'Brasileiro'), MapEntry(Locale('cs'), 'Česky'), MapEntry(Locale('sv'), 'Svenska'), MapEntry(Locale('nl'), 'Nederlands'), MapEntry(Locale('vi'), 'Tiếng Việt'), MapEntry(Locale('tr'), 'Türkçe'), MapEntry(Locale('uk'), 'Українська'), MapEntry(Locale('da'), 'Dansk'), MapEntry(Locale('en', 'EO'), 'Esperanto'), // https://github.com/aissat/easy_localization/issues/220#issuecomment-846035493 MapEntry(Locale('in'), 'Bahasa Indonesia'), MapEntry(Locale('ko'), '한국어'), MapEntry(Locale('ca'), 'Català'), MapEntry(Locale('ar'), 'العربية'), ]; const fallbackLocale = Locale('en'); const localeDir = 'assets/translations'; var fdroid = false; final globalNavigatorKey = GlobalKey(); Future loadTranslations() async { // See easy_localization/issues/210 await EasyLocalizationController.initEasyLocation(); var s = SettingsProvider(); await s.initializeSettings(); var forceLocale = s.forcedLocale; final controller = EasyLocalizationController( saveLocale: true, forceLocale: forceLocale, fallbackLocale: fallbackLocale, supportedLocales: supportedLocales.map((e) => e.key).toList(), assetLoader: const RootBundleAssetLoader(), useOnlyLangCode: false, useFallbackTranslations: true, path: localeDir, onLoadError: (FlutterError e) { throw e; }, ); await controller.loadTranslations(); Localization.load(controller.locale, translations: controller.translations, fallbackTranslations: controller.fallbackTranslations); } @pragma('vm:entry-point') void backgroundFetchHeadlessTask(HeadlessTask task) async { String taskId = task.taskId; bool isTimeout = task.timeout; if (isTimeout) { print('BG update task timed out.'); BackgroundFetch.finish(taskId); return; } await bgUpdateCheck(taskId, null); BackgroundFetch.finish(taskId); } void main() async { WidgetsFlutterBinding.ensureInitialized(); try { ByteData data = await PlatformAssetBundle().load('assets/ca/lets-encrypt-r3.pem'); SecurityContext.defaultContext .setTrustedCertificatesBytes(data.buffer.asUint8List()); } catch (e) { // Already added, do nothing (see #375) } await EasyLocalization.ensureInitialized(); if ((await DeviceInfoPlugin().androidInfo).version.sdkInt >= 29) { SystemChrome.setSystemUIOverlayStyle( const SystemUiOverlayStyle(systemNavigationBarColor: Colors.transparent), ); SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); } final np = NotificationsProvider(); await np.initialize(); runApp(MultiProvider( providers: [ ChangeNotifierProvider(create: (context) => AppsProvider()), ChangeNotifierProvider(create: (context) => SettingsProvider()), Provider(create: (context) => np), Provider(create: (context) => LogsProvider()) ], child: EasyLocalization( supportedLocales: supportedLocales.map((e) => e.key).toList(), path: localeDir, fallbackLocale: fallbackLocale, useOnlyLangCode: false, child: const Obtainium()), )); BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask); } class Obtainium extends StatefulWidget { const Obtainium({super.key}); @override State createState() => _ObtainiumState(); } class _ObtainiumState extends State { var existingUpdateInterval = -1; @override void initState() { super.initState(); initPlatformState(); } Future initPlatformState() async { await BackgroundFetch.configure( BackgroundFetchConfig( minimumFetchInterval: 15, stopOnTerminate: false, startOnBoot: true, enableHeadless: true, requiresBatteryNotLow: false, requiresCharging: false, requiresStorageNotLow: false, requiresDeviceIdle: false, requiredNetworkType: NetworkType.ANY), (String taskId) async { await bgUpdateCheck(taskId, null); BackgroundFetch.finish(taskId); }, (String taskId) async { context.read().add('BG update task timed out.'); BackgroundFetch.finish(taskId); }); if (!mounted) return; } @override Widget build(BuildContext context) { SettingsProvider settingsProvider = context.watch(); AppsProvider appsProvider = context.read(); LogsProvider logs = context.read(); NotificationsProvider notifs = context.read(); if (settingsProvider.prefs == null) { settingsProvider.initializeSettings(); } else { bool isFirstRun = settingsProvider.checkAndFlipFirstRun(); if (isFirstRun) { logs.add('This is the first ever run of Obtainium.'); // If this is the first run, ask for notification permissions and add Obtainium to the Apps list Permission.notification.request(); if (!fdroid) { getInstalledInfo(obtainiumId).then((value) { if (value?.versionName != null) { appsProvider.saveApps([ App( obtainiumId, obtainiumUrl, 'ImranR98', 'Obtainium', value!.versionName, value.versionName!, [], 0, { 'versionDetection': true, 'apkFilterRegEx': 'fdroid', 'invertAPKFilter': true }, null, false) ], onlyIfExists: false); } }).catchError((err) { print(err); }); } } if (!supportedLocales.map((e) => e.key).contains(context.locale) || (settingsProvider.forcedLocale == null && context.deviceLocale != context.locale)) { settingsProvider.resetLocaleSafe(context); } } WidgetsBinding.instance.addPostFrameCallback((_) { notifs.checkLaunchByNotif(); }); return DynamicColorBuilder( builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) { // Decide on a colour/brightness scheme based on OS and user settings ColorScheme lightColorScheme; ColorScheme darkColorScheme; if (lightDynamic != null && darkDynamic != null && settingsProvider.useMaterialYou) { lightColorScheme = lightDynamic.harmonized(); darkColorScheme = darkDynamic.harmonized(); } else { lightColorScheme = ColorScheme.fromSeed(seedColor: settingsProvider.themeColor); darkColorScheme = ColorScheme.fromSeed( seedColor: settingsProvider.themeColor, brightness: Brightness.dark); } // set the background and surface colors to pure black in the amoled theme if (settingsProvider.useBlackTheme) { darkColorScheme = darkColorScheme.copyWith(surface: Colors.black).harmonized(); } if (settingsProvider.useSystemFont) NativeFeatures.loadSystemFont(); return MaterialApp( title: 'Obtainium', localizationsDelegates: context.localizationDelegates, supportedLocales: context.supportedLocales, locale: context.locale, navigatorKey: globalNavigatorKey, debugShowCheckedModeBanner: false, theme: ThemeData( useMaterial3: true, colorScheme: settingsProvider.theme == ThemeSettings.dark ? darkColorScheme : lightColorScheme, fontFamily: settingsProvider.useSystemFont ? 'SystemFont' : 'Montserrat'), darkTheme: ThemeData( useMaterial3: true, colorScheme: settingsProvider.theme == ThemeSettings.light ? lightColorScheme : darkColorScheme, fontFamily: settingsProvider.useSystemFont ? 'SystemFont' : 'Montserrat'), home: Shortcuts(shortcuts: { LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(), }, child: const HomePage())); }); } }