345 lines
11 KiB
Dart
345 lines
11 KiB
Dart
|
|
import 'dart:io';
|
||
|
|
import 'dart:io';
|
||
|
|
|
||
|
|
import 'package:adjust_sdk/adjust_event.dart';
|
||
|
|
import 'package:firebase_analytics/firebase_analytics.dart';
|
||
|
|
import 'package:firebase_core/firebase_core.dart';
|
||
|
|
import 'package:flutter/material.dart';
|
||
|
|
import 'package:guru_app/account/account_data_store.dart';
|
||
|
|
import 'package:guru_app/account/account_manager.dart';
|
||
|
|
import 'package:guru_app/ads/ads_manager.dart';
|
||
|
|
import 'package:guru_app/analytics/guru_analytics.dart';
|
||
|
|
import 'package:guru_app/app/app_models.dart';
|
||
|
|
import 'package:guru_app/database/guru_db.dart';
|
||
|
|
import 'package:guru_app/financial/financial_manager.dart';
|
||
|
|
import 'package:guru_app/financial/iap/iap_manager.dart';
|
||
|
|
import 'package:guru_app/financial/manifest/manifest.dart';
|
||
|
|
import 'package:guru_app/financial/manifest/manifest_manager.dart';
|
||
|
|
import 'package:guru_app/financial/product/product_model.dart';
|
||
|
|
import 'package:guru_app/financial/reward/reward_manager.dart';
|
||
|
|
import 'package:guru_app/firebase/dxlinks/dxlink_manager.dart';
|
||
|
|
import 'package:guru_applovin_flutter/guru_applovin_flutter.dart';
|
||
|
|
import 'package:guru_utils/collection/collectionutils.dart';
|
||
|
|
import 'package:guru_utils/controller/aware/ads/overlay/ads_overlay.dart';
|
||
|
|
import 'package:guru_utils/datetime/datetime_utils.dart';
|
||
|
|
import 'package:guru_utils/lifecycle/lifecycle_manager.dart';
|
||
|
|
import 'package:guru_utils/network/network_utils.dart';
|
||
|
|
import 'package:guru_utils/property/app_property.dart';
|
||
|
|
import 'package:guru_app/property/settings/guru_settings.dart';
|
||
|
|
import 'package:guru_app/firebase/firebase.dart';
|
||
|
|
import 'package:guru_utils/http/http_ex.dart';
|
||
|
|
import 'package:guru_utils/log/log.dart';
|
||
|
|
import 'package:guru_utils/packages/guru_package.dart';
|
||
|
|
import 'package:guru_utils/ads/ads.dart';
|
||
|
|
import 'package:guru_utils/guru_utils.dart';
|
||
|
|
import 'package:logger/logger.dart' as Logger;
|
||
|
|
import 'package:guru_utils/aigc/bi/ai_bi.dart';
|
||
|
|
import 'package:package_info_plus/package_info_plus.dart';
|
||
|
|
import 'package:guru_popup/guru_popup.dart';
|
||
|
|
export 'package:firebase_core/firebase_core.dart';
|
||
|
|
export 'package:guru_app/app/app_models.dart';
|
||
|
|
export 'package:guru_utils/log/log.dart';
|
||
|
|
export 'package:guru_spec/guru_spec.dart';
|
||
|
|
export 'package:guru_app/analytics/guru_analytics.dart';
|
||
|
|
export 'package:guru_app/financial/product/product_model.dart';
|
||
|
|
export 'package:adjust_sdk/adjust_event.dart';
|
||
|
|
export 'package:guru_utils/ads/ads.dart';
|
||
|
|
export 'package:guru_utils/guru_utils.dart';
|
||
|
|
export 'dart:io';
|
||
|
|
export 'dart:math';
|
||
|
|
export 'package:guru_app/financial/manifest/manifest.dart';
|
||
|
|
export 'package:guru_app/firebase/messaging/remote_messaging_manager.dart';
|
||
|
|
|
||
|
|
|
||
|
|
/// Created by Haoyi on 2022/8/25
|
||
|
|
|
||
|
|
abstract class AppSpec {
|
||
|
|
String get appName;
|
||
|
|
|
||
|
|
String get flavor;
|
||
|
|
|
||
|
|
AppDetails get details;
|
||
|
|
|
||
|
|
AdsProfile get adsProfile;
|
||
|
|
|
||
|
|
ProductProfile get productProfile;
|
||
|
|
|
||
|
|
AdjustProfile get adjustProfile;
|
||
|
|
|
||
|
|
Deployment get deployment;
|
||
|
|
|
||
|
|
Map<String, dynamic> get defaultRemoteConfig;
|
||
|
|
}
|
||
|
|
|
||
|
|
class NotImplementationAppSpecCreatorException implements Exception {
|
||
|
|
NotImplementationAppSpecCreatorException();
|
||
|
|
|
||
|
|
@override
|
||
|
|
String toString() {
|
||
|
|
return 'NotImplementationAppSpecCreatorException';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
class AppEnv {
|
||
|
|
final AppSpec spec;
|
||
|
|
final RootPackage package;
|
||
|
|
final BackgroundMessageHandler? backgroundMessageHandler;
|
||
|
|
final ToastDelegate? toastDelegate;
|
||
|
|
|
||
|
|
AppEnv(
|
||
|
|
{required this.spec,
|
||
|
|
required this.package,
|
||
|
|
this.backgroundMessageHandler,
|
||
|
|
this.toastDelegate});
|
||
|
|
}
|
||
|
|
|
||
|
|
extension _GuruPackageExtension on GuruPackage {
|
||
|
|
Iterable<Locale> _mergeSupportedLocales() {
|
||
|
|
final Set<Locale> locales = supportedLocales.toSet();
|
||
|
|
for (var child in children) {
|
||
|
|
locales.addAll(child._mergeSupportedLocales());
|
||
|
|
}
|
||
|
|
return locales;
|
||
|
|
}
|
||
|
|
|
||
|
|
Iterable<LocalizationsDelegate<dynamic>> _mergeLocalizationsDelegates() {
|
||
|
|
final Set<LocalizationsDelegate<dynamic>> delegates = localizationsDelegates.toSet();
|
||
|
|
for (var child in children) {
|
||
|
|
delegates.addAll(child._mergeLocalizationsDelegates());
|
||
|
|
}
|
||
|
|
return delegates;
|
||
|
|
}
|
||
|
|
|
||
|
|
Future _dispatchInitialize() async {
|
||
|
|
await initialize();
|
||
|
|
children.sort((p1, p2) {
|
||
|
|
return p2.priority.compareTo(p1.priority);
|
||
|
|
});
|
||
|
|
for (var child in children) {
|
||
|
|
if (flattenChildrenAsyncInit) {
|
||
|
|
child._dispatchInitialize();
|
||
|
|
} else {
|
||
|
|
await child._dispatchInitialize();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Future _dispatchInitializeAsync() async {
|
||
|
|
initializeAsync();
|
||
|
|
for (var child in children) {
|
||
|
|
child._dispatchInitializeAsync();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
class GuruApp {
|
||
|
|
static late GuruApp _instance;
|
||
|
|
|
||
|
|
static GuruApp get instance => _instance;
|
||
|
|
|
||
|
|
final RootPackage rootPackage;
|
||
|
|
|
||
|
|
final AppSpec appSpec;
|
||
|
|
|
||
|
|
String get appName => appSpec.appName;
|
||
|
|
|
||
|
|
String get flavor => appSpec.flavor;
|
||
|
|
|
||
|
|
AppDetails get details => appSpec.details;
|
||
|
|
|
||
|
|
AdsProfile get adsProfile => appSpec.adsProfile;
|
||
|
|
|
||
|
|
AdjustProfile get adjustProfile => appSpec.adjustProfile;
|
||
|
|
|
||
|
|
ProductProfile get productProfile => appSpec.productProfile;
|
||
|
|
|
||
|
|
Map<String, dynamic> get defaultRemoteConfig => appSpec.defaultRemoteConfig;
|
||
|
|
|
||
|
|
Set<String> get conversionEvents => appSpec.deployment.conversionEvents;
|
||
|
|
|
||
|
|
GuruApp._({required this.appSpec, required this.rootPackage, ToastDelegate? toastDelegate}) {
|
||
|
|
GuruUtils.toastDelegate = toastDelegate;
|
||
|
|
AdsOverlay.bind(showBanner: GuruPopup.instance.showAdsBanner);
|
||
|
|
}
|
||
|
|
|
||
|
|
Iterable<Locale> get supportedLocales => rootPackage._mergeSupportedLocales();
|
||
|
|
|
||
|
|
Iterable<LocalizationsDelegate<dynamic>> get localizationsDelegates =>
|
||
|
|
rootPackage._mergeLocalizationsDelegates();
|
||
|
|
|
||
|
|
bool? _check;
|
||
|
|
|
||
|
|
Future _initialize() async {
|
||
|
|
try {
|
||
|
|
await GuruDB.instance.initDatabase();
|
||
|
|
AppProperty.initialize(GuruDB.instance, cacheSize: appSpec.deployment.propertyCacheSize);
|
||
|
|
await GuruSettings.instance.refresh();
|
||
|
|
Paint.enableDithering = appSpec.deployment.enableDithering; // 3.16 default enabled
|
||
|
|
await _dispatchInitializeSync();
|
||
|
|
_dispatchInitializeAsync();
|
||
|
|
} catch (error, stacktrace) {
|
||
|
|
Log.w("initialize error:$error, $stacktrace");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Future<bool> _checkApp() async {
|
||
|
|
try {
|
||
|
|
final pkgName = (await PackageInfo.fromPlatform()).appName;
|
||
|
|
final result = _check ??= (pkgName != GuruApp.instance.details.appId);
|
||
|
|
GuruAnalytics.instance.logGuruEvent(
|
||
|
|
"dev_audit",
|
||
|
|
CollectionUtils.filterOutNulls({
|
||
|
|
"item_category": "pkg",
|
||
|
|
"result": result == true ? 1 : 0,
|
||
|
|
"err_info": result != true ? pkgName : null,
|
||
|
|
}));
|
||
|
|
return result == true;
|
||
|
|
} catch (error, stacktrace) {
|
||
|
|
Log.w("checkApp error:$error, $stacktrace");
|
||
|
|
GuruAnalytics.instance.logException(error, stacktrace: stacktrace);
|
||
|
|
GuruAnalytics.instance.logGuruEvent(
|
||
|
|
"dev_audit",
|
||
|
|
CollectionUtils.filterOutNulls({
|
||
|
|
"item_category": "pkg",
|
||
|
|
"result": 0,
|
||
|
|
"err_info": error.runtimeType.toString(),
|
||
|
|
}));
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Future _dispatchInitializeSync() async {
|
||
|
|
await RemoteConfigManager.instance.init(appSpec.defaultRemoteConfig);
|
||
|
|
await rootPackage._dispatchInitialize();
|
||
|
|
try {
|
||
|
|
GuruUtils.isTablet = (await GuruApplovinFlutter.instance.isTablet()) ?? false;
|
||
|
|
Log.d("isTablet: ${GuruUtils.isTablet}");
|
||
|
|
} catch (error, stacktrace) {
|
||
|
|
Log.w("invoke isTablet error:$error, $stacktrace");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Future _dispatchInitializeAsync() async {
|
||
|
|
_initCommon();
|
||
|
|
_initRemoteConfig();
|
||
|
|
_initRemoteMessaging();
|
||
|
|
_initAnalytics();
|
||
|
|
if (appSpec.adsProfile != AdsProfile.invalid) {
|
||
|
|
_initAds();
|
||
|
|
}
|
||
|
|
_initFinancial();
|
||
|
|
_initAccount();
|
||
|
|
_initDxLink();
|
||
|
|
rootPackage._dispatchInitializeAsync();
|
||
|
|
Future.delayed(const Duration(seconds: 15), () async {
|
||
|
|
await _checkApp();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
static Future initialize({required AppEnv appEnv}) async {
|
||
|
|
final backgroundMessageHandler = appEnv.backgroundMessageHandler;
|
||
|
|
if (backgroundMessageHandler != null) {
|
||
|
|
FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler);
|
||
|
|
}
|
||
|
|
WidgetsFlutterBinding.ensureInitialized();
|
||
|
|
try {
|
||
|
|
await Firebase.initializeApp();
|
||
|
|
} catch (error, stacktrace) {
|
||
|
|
Log.e("Firebase.initializeApp() error!", error: error, stackTrace: stacktrace);
|
||
|
|
}
|
||
|
|
GuruUtils.flavor = appEnv.spec.flavor;
|
||
|
|
try {
|
||
|
|
_instance = GuruApp._(
|
||
|
|
appSpec: appEnv.spec, rootPackage: appEnv.package, toastDelegate: appEnv.toastDelegate);
|
||
|
|
Log.init(_instance.appName,
|
||
|
|
persistentLogFileSize: appEnv.spec.deployment.logFileSizeLimit,
|
||
|
|
persistentLogCount: appEnv.spec.deployment.logFileCount,
|
||
|
|
persistentLevel: appEnv.spec.deployment.persistentLogLevel);
|
||
|
|
AiBi.instance.init();
|
||
|
|
AdsManager.instance.ensureInitialize();
|
||
|
|
await _instance._initialize();
|
||
|
|
LifecycleManager.instance.init();
|
||
|
|
} catch (error, stacktrace) {
|
||
|
|
Log.e("GuruApp initialize error!", error: error, stackTrace: stacktrace);
|
||
|
|
rethrow;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void showToast(String message, {Duration duration = const Duration(seconds: 3)}) {
|
||
|
|
GuruUtils.showToast(message, duration: duration);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
extension GuruAppInitializerExt on GuruApp {
|
||
|
|
Future _initCommon() async {
|
||
|
|
await NetworkUtils.init();
|
||
|
|
}
|
||
|
|
|
||
|
|
Future _initRemoteConfig() async {
|
||
|
|
await RemoteConfigManager.instance.fetchAndActivate();
|
||
|
|
final cdnConfig = RemoteConfigManager.instance.getCdnConfig();
|
||
|
|
HttpEx.init(cdnConfig, GuruApp.instance.appSpec.details.storagePrefix);
|
||
|
|
|
||
|
|
final remoteDeployment = RemoteConfigManager.instance.getRemoteDeployment();
|
||
|
|
Settings.get()
|
||
|
|
.keepOnScreenDuration
|
||
|
|
.set(remoteDeployment.keepScreenOnDuration * DateTimeUtils.minuteInMillis);
|
||
|
|
}
|
||
|
|
|
||
|
|
void _initAnalytics() {
|
||
|
|
GuruAnalytics.instance.init();
|
||
|
|
}
|
||
|
|
|
||
|
|
void _initRemoteMessaging() async {
|
||
|
|
RemoteMessagingManager.instance.init();
|
||
|
|
}
|
||
|
|
|
||
|
|
void _initDxLink() {
|
||
|
|
Future.delayed(const Duration(seconds: 2), () {
|
||
|
|
DxLinkManager.instance.init();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
void _initAds() async {
|
||
|
|
try {
|
||
|
|
await AccountDataStore.instance.observableSaasUser
|
||
|
|
.firstWhere((saasUser) => saasUser?.isValid == true)
|
||
|
|
.timeout(const Duration(seconds: 3));
|
||
|
|
} catch (error, stacktrace) {
|
||
|
|
Log.w("wait account error! $error", stackTrace: stacktrace);
|
||
|
|
} finally {
|
||
|
|
await AdsManager.instance.initialize(saasUser: AccountDataStore.instance.user);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Future _initFinancial() async {
|
||
|
|
ManifestManager.instance.addBuilders(GuruApp.instance.productProfile.manifestBuilders);
|
||
|
|
|
||
|
|
FinancialManager.instance.init();
|
||
|
|
}
|
||
|
|
|
||
|
|
Future _initAccount() async {
|
||
|
|
await AccountManager.instance.init();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
extension GuruAppFinancialExt on GuruApp {
|
||
|
|
ProductId defineProductId(String sku, int attr, TransactionMethod method) {
|
||
|
|
return productProfile.define(sku, attr, method);
|
||
|
|
}
|
||
|
|
|
||
|
|
ProductId? findProductId({String? sku, int? attr}) {
|
||
|
|
return productProfile.find(sku: sku, attr: attr);
|
||
|
|
}
|
||
|
|
|
||
|
|
Set<ProductId> offerProductIds(ProductId productId) {
|
||
|
|
return productProfile.offerProductIds(productId);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
extension GuruRemoteConfigExt on GuruApp {
|
||
|
|
String getDefaultRemoteConfig(String key, {String defaultValue = ""}) {
|
||
|
|
return defaultRemoteConfig[key] ?? defaultValue;
|
||
|
|
}
|
||
|
|
}
|