811 lines
27 KiB
Dart
811 lines
27 KiB
Dart
/// Created by Haoyi on 2022/8/24
|
||
|
||
import 'dart:collection';
|
||
import 'dart:core';
|
||
import 'dart:io';
|
||
|
||
import 'package:adjust_sdk/adjust_third_party_sharing.dart';
|
||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||
import 'package:firebase_remote_config/firebase_remote_config.dart';
|
||
import 'package:flutter/foundation.dart';
|
||
import 'package:guru_analytics_flutter/event_logger.dart';
|
||
import 'package:guru_analytics_flutter/event_logger_common.dart';
|
||
import 'package:guru_analytics_flutter/events_constants.dart';
|
||
import 'package:guru_analytics_flutter/guru/guru_event_logger.dart';
|
||
import 'package:guru_analytics_flutter/guru/guru_statistic.dart';
|
||
import 'package:guru_app/account/account_data_store.dart';
|
||
import 'package:guru_app/ads/ads_manager.dart';
|
||
import 'package:guru_app/ads/core/ads_config.dart';
|
||
import 'package:guru_app/aigc/bi/ai_bi.dart';
|
||
import 'package:guru_app/analytics/abtest/abtest_model.dart';
|
||
import 'package:guru_app/analytics/data/analytics_model.dart';
|
||
import 'package:guru_app/analytics/strategy/guru_analytics_strategy.dart';
|
||
import 'package:device_info_plus/device_info_plus.dart';
|
||
import 'package:guru_app/firebase/remoteconfig/remote_config_manager.dart';
|
||
import 'package:guru_app/guru_app.dart';
|
||
import 'package:guru_app/property/app_property.dart';
|
||
import 'package:guru_app/property/property_keys.dart';
|
||
import 'package:guru_app/property/runtime_property.dart';
|
||
import 'package:guru_app/property/settings/guru_settings.dart';
|
||
import 'package:guru_platform_data/guru_platform_data.dart';
|
||
import 'package:guru_utils/collection/collectionutils.dart';
|
||
import 'package:guru_utils/datetime/datetime_utils.dart';
|
||
import 'package:guru_utils/device/device_info.dart';
|
||
import 'package:guru_utils/device/device_utils.dart';
|
||
import 'package:guru_utils/analytics/analytics.dart';
|
||
import 'package:guru_utils/network/network_utils.dart';
|
||
import 'package:intl/intl.dart';
|
||
import 'package:guru_utils/extensions/extensions.dart';
|
||
import 'package:adjust_sdk/adjust_ad_revenue.dart';
|
||
import 'package:adjust_sdk/adjust_config.dart';
|
||
export 'package:adjust_sdk/adjust.dart';
|
||
|
||
part 'modules/ads_analytics.dart';
|
||
|
||
part 'modules/adjust_aware.dart';
|
||
|
||
class GuruAnalytics extends Analytics with AdjustAware {
|
||
bool get release => !_mock && (_enabledAnalytics || kReleaseMode);
|
||
|
||
String appInstanceId = "";
|
||
|
||
static bool _mock = false;
|
||
|
||
static bool _enabledAnalytics = true;
|
||
|
||
static GuruAnalytics instance = GuruAnalytics._();
|
||
|
||
/// Name of virtual currency type.
|
||
static bool initialized = false;
|
||
|
||
static final Map<String, String> facebookEventMapping = {};
|
||
|
||
static String currentScreen = "";
|
||
|
||
static final RegExp _consentPurposeRegExp = RegExp(r"^[01]+$");
|
||
|
||
static String? mockCountryCode;
|
||
|
||
static const errorEventCodes = {
|
||
14, // 上报事件失败
|
||
22, // 网络状态不可用
|
||
101, // 调用api出错
|
||
102, // api返回结果错误
|
||
103, // 设置cacheControl出错
|
||
104, // 删除过期事件出错
|
||
105, // 从数据库取事件以及更改事件状态为正在上报出错
|
||
106, // dns 错误
|
||
};
|
||
|
||
int latestFetchStatisticTs = 0;
|
||
|
||
final BehaviorSubject<GuruStatistic> guruEventStatistic =
|
||
BehaviorSubject.seeded(GuruStatistic.invalid);
|
||
|
||
final BehaviorSubject<Map<String, String>> abTestExperimentVariant = BehaviorSubject.seeded({});
|
||
|
||
Stream<GuruStatistic> get observableGuruEventStatistic => guruEventStatistic.stream;
|
||
|
||
Stream<Map<String, String>> get observableABTestExperimentVariant =>
|
||
abTestExperimentVariant.stream;
|
||
|
||
final BehaviorSubject<UserIdentification> userIdentificationSubject =
|
||
BehaviorSubject.seeded(UserIdentification());
|
||
|
||
UserIdentification get userIdentification => userIdentificationSubject.value;
|
||
|
||
AppEventCapabilities get currentAppEventCapabilities => EventLogger.getCapabilities();
|
||
|
||
static void setMock() {
|
||
_mock = true;
|
||
}
|
||
|
||
static void disableAnalytics() {
|
||
_enabledAnalytics = false;
|
||
}
|
||
|
||
static void enableAnalytics() {
|
||
_enabledAnalytics = true;
|
||
}
|
||
|
||
GuruAnalytics._();
|
||
|
||
String? getProperty(String key) {
|
||
return Analytics.userProperties[key];
|
||
}
|
||
|
||
Future prepare() async {
|
||
if (GuruApp.instance.appSpec.localABTestExperiments.isNotEmpty) {
|
||
await initLocalExperiments();
|
||
}
|
||
RemoteConfigManager.instance.observeConfig().listen((config) {
|
||
Log.i(
|
||
"GuruAnalytics observeConfig changed: ${config.lastFetchStatus} ${config.lastFetchTime}");
|
||
if (config.lastFetchStatus == RemoteConfigFetchStatus.success) {
|
||
refreshABProperties();
|
||
}
|
||
});
|
||
}
|
||
|
||
void init() async {
|
||
Log.d(
|
||
"AnalyticsUtil init### Platform.localeName :${Platform.localeName} ${Intl.getCurrentLocale()}");
|
||
if (!_mock && !initialized) {
|
||
final analyticsConfig = RemoteConfigManager.instance.getAnalyticsConfig();
|
||
EventLogger.setCapabilities(analyticsConfig.toAppEventCapabilities());
|
||
EventLogger.registerTransmitter(EventTransmitter({}, defaultHook: (name, parameters) {
|
||
recordEvents(name, parameters);
|
||
final fbEvent = facebookEventMapping[name];
|
||
if (fbEvent == null) {
|
||
return;
|
||
}
|
||
Log.d("transmit EVENT [$name] => [$fbEvent]");
|
||
EventLogger.facebookLogEvent(name: fbEvent);
|
||
}));
|
||
EventLogger.setGuruPriorityGetter((name, parameters) =>
|
||
GuruApp.instance.conversionEvents.contains(name)
|
||
? EventPriority.EMERGENCE
|
||
: EventPriority.DEFAULT);
|
||
String xDeviceInfo = '';
|
||
try {
|
||
final deviceId = await AppProperty.getInstance().getDeviceId();
|
||
final deviceInfo = await DeviceUtils.buildDeviceInfo(deviceId: deviceId);
|
||
xDeviceInfo = deviceInfo.toXDeviceInfo();
|
||
} catch (error, stacktrace) {
|
||
Log.e("init deviceInfo error: $error, $stacktrace");
|
||
}
|
||
|
||
await GuruAnalyticsStrategy.instance.load();
|
||
EventLogger.initialize(
|
||
appId: GuruApp.instance.appSpec.details.saasAppId,
|
||
deviceInfo: xDeviceInfo,
|
||
delayedInSeconds: analyticsConfig.delayedInSeconds,
|
||
eventExpiredInDays: analyticsConfig.expiredInDays,
|
||
callback: processAnalyticsCallback,
|
||
debug: true,
|
||
);
|
||
_initEnvProperties();
|
||
_logLocale();
|
||
_logDeviceType();
|
||
_logFirstOpen();
|
||
Future.delayed(const Duration(seconds: 1), () {
|
||
initAdjust();
|
||
initFbEventMapping();
|
||
refreshConsents();
|
||
Log.d("register transmitter");
|
||
});
|
||
initialized = true;
|
||
// if (Platform.isAndroid) {
|
||
// _logPeerApps();
|
||
// }
|
||
}
|
||
}
|
||
|
||
Future switchSession(String oldToken, String newToken) async {
|
||
_initEnvProperties();
|
||
_logLocale();
|
||
_logDeviceType();
|
||
}
|
||
|
||
Future initLocalExperiments() async {
|
||
final runningExperiments = await AppProperty.getInstance().loadRunningExperiments();
|
||
final experiments = GuruApp.instance.appSpec.localABTestExperiments;
|
||
final validRunningExperimentKeys =
|
||
runningExperiments.keys.toSet().intersection(experiments.keys.toSet());
|
||
for (var experiment in experiments.values) {
|
||
// 如果在已经开始的实验中,但是不在当前的实验列表中,需要删除
|
||
final needRemove = runningExperiments.containsKey(experiment.name) &&
|
||
!validRunningExperimentKeys.contains(experiment.name);
|
||
if (needRemove) {
|
||
await removeExperiment(experiment.name);
|
||
} else {
|
||
await _applyExperiment(experiment);
|
||
}
|
||
}
|
||
}
|
||
|
||
Future<String> refreshConsents({AnalyticsConfig? analyticsConfig}) async {
|
||
final config = analyticsConfig ?? RemoteConfigManager.instance.getAnalyticsConfig();
|
||
final purposeConsents = await GuruPlatformData.getPurposeConsents();
|
||
Log.i("refreshConsents: '$purposeConsents'");
|
||
if (purposeConsents.isEmpty) {
|
||
return "";
|
||
}
|
||
|
||
/// 如果他不是完全使用 1,0 组成的字符串
|
||
if (!_consentPurposeRegExp.hasMatch(purposeConsents)) {
|
||
Log.i("invalid consents $purposeConsents");
|
||
return "";
|
||
}
|
||
|
||
/// 获取当前的 countryCode, 判断是否在 dma country的范围内
|
||
if (config.dmaCountry.isNotEmpty) {
|
||
final countryCode = getCountryCode();
|
||
if (!config.dmaCountry.contains(countryCode)) {
|
||
Log.i("invalid country $countryCode");
|
||
return "";
|
||
}
|
||
}
|
||
|
||
final length = min(purposeConsents.length, 32);
|
||
int flags = 0;
|
||
for (var i = 0; i < length; i++) {
|
||
flags |= (((purposeConsents[i] == "1") ? 1 : 0) << i);
|
||
}
|
||
|
||
final consentsData = {
|
||
ConsentFieldName.adStorage: config.googleDmaGranted(ConsentType.adStorage, flags),
|
||
ConsentFieldName.analyticsStorage:
|
||
config.googleDmaGranted(ConsentType.analyticsStorage, flags),
|
||
ConsentFieldName.adPersonalization:
|
||
config.googleDmaGranted(ConsentType.adPersonalization, flags),
|
||
ConsentFieldName.adUserData: config.googleDmaGranted(ConsentType.adUserData, flags),
|
||
};
|
||
|
||
String _flag(String key) {
|
||
return consentsData[key] == true ? "1" : "0";
|
||
}
|
||
|
||
Log.d("setConsents consentsData: $consentsData");
|
||
|
||
try {
|
||
final result = await EventLogger.guru.setConsents(consentsData);
|
||
Log.d("setConsents result: $result");
|
||
} catch (error, stacktrace) {
|
||
Log.e("setConsents error! $error, $stacktrace");
|
||
}
|
||
|
||
if (enabledAdjust) {
|
||
AdjustThirdPartySharing adjustThirdPartySharing = AdjustThirdPartySharing(null);
|
||
adjustThirdPartySharing.addGranularOption("google_dma", "eea", "1");
|
||
adjustThirdPartySharing.addGranularOption(
|
||
"google_dma", "ad_personalization", _flag(ConsentFieldName.adPersonalization));
|
||
adjustThirdPartySharing.addGranularOption(
|
||
"google_dma", "ad_user_data", _flag(ConsentFieldName.adUserData));
|
||
Adjust.trackThirdPartySharing(adjustThirdPartySharing);
|
||
Log.d("setAdjust complete!");
|
||
}
|
||
|
||
final result =
|
||
"${_flag(ConsentFieldName.adStorage)}${_flag(ConsentFieldName.analyticsStorage)}${_flag(ConsentFieldName.adPersonalization)}${_flag(ConsentFieldName.adUserData)}";
|
||
final changed = await AppProperty.getInstance().refreshGoogleDma(result);
|
||
if (changed || GuruSettings.instance.debugMode.get()) {
|
||
logEventEx("dma_gg", parameters: {"purpose": purposeConsents, "result": result});
|
||
}
|
||
return result;
|
||
}
|
||
|
||
void processAnalyticsCallback(int code, String? errorInfo) {
|
||
if (!errorEventCodes.contains(code)) {
|
||
return;
|
||
}
|
||
final parameters = {
|
||
"item_category": "error_event",
|
||
"item_name": code.toString(),
|
||
"country": AccountDataStore.instance.countryCode,
|
||
"network": AdsManager.instance.connectivityStatus.toString(),
|
||
};
|
||
|
||
if (errorInfo != null) {
|
||
parameters["err"] = errorInfo.length > 32 ? errorInfo.substring(0, 32) : errorInfo;
|
||
}
|
||
logFirebaseEvent("dev_audit", parameters);
|
||
// Guru Analytics Event(GAE)
|
||
Log.d("[GAE]($code)=>$errorInfo $parameters", tag: "Analytics");
|
||
}
|
||
|
||
void updateUserIdentification(
|
||
{String? firebaseAppInstanceId, String? idfa, String? adId, String? gpsAdId}) {
|
||
final latestUserIdentification = userIdentificationSubject.value;
|
||
bool changed = false;
|
||
String? changedFirebaseInstanceId = latestUserIdentification.firebaseAppInstanceId;
|
||
String? changedIdfa = latestUserIdentification.idfa;
|
||
String? changedAdId = latestUserIdentification.adId;
|
||
String? changedGpsAdId = latestUserIdentification.gpsAdId;
|
||
|
||
if (firebaseAppInstanceId != null &&
|
||
latestUserIdentification.firebaseAppInstanceId != firebaseAppInstanceId) {
|
||
changedFirebaseInstanceId = firebaseAppInstanceId;
|
||
changed = true;
|
||
}
|
||
if (idfa != null && latestUserIdentification.idfa != idfa) {
|
||
changedIdfa = idfa;
|
||
changed = true;
|
||
}
|
||
if (adId != null && latestUserIdentification.adId != adId) {
|
||
changedAdId = adId;
|
||
changed = true;
|
||
}
|
||
if (gpsAdId != null && latestUserIdentification.gpsAdId != gpsAdId) {
|
||
changedGpsAdId = gpsAdId;
|
||
changed = true;
|
||
}
|
||
if (changed) {
|
||
final newUserIdentification = UserIdentification(
|
||
firebaseAppInstanceId: changedFirebaseInstanceId ?? '',
|
||
idfa: changedIdfa,
|
||
adId: changedAdId,
|
||
gpsAdId: changedGpsAdId);
|
||
userIdentificationSubject.add(newUserIdentification);
|
||
Log.d("updateUserIdentification: $newUserIdentification");
|
||
}
|
||
}
|
||
|
||
void parseFbEventMapping() {
|
||
final fbEventMappingString =
|
||
RemoteConfigManager.instance.getString(RemoteConfigReservedConstants.fbEventMapping);
|
||
Log.d("parseFbEventMapping first: $fbEventMappingString");
|
||
if (fbEventMappingString == null) {
|
||
return;
|
||
}
|
||
Map<String, String> result = {};
|
||
final eventEntries = fbEventMappingString.split(";");
|
||
for (String eventEntryString in eventEntries) {
|
||
final eventEntry = eventEntryString.split(":");
|
||
if (eventEntry.length == 2) {
|
||
result[eventEntry.first] = eventEntry.last;
|
||
}
|
||
}
|
||
facebookEventMapping.clear();
|
||
facebookEventMapping.addAll(result);
|
||
Log.d("parseFbEventMapping: $result");
|
||
}
|
||
|
||
void initFbEventMapping() {
|
||
RemoteConfigManager.instance.observeConfig().listen((config) {
|
||
parseFbEventMapping();
|
||
});
|
||
parseFbEventMapping();
|
||
}
|
||
|
||
@override
|
||
Future<String> getAppInstanceId() async {
|
||
if (appInstanceId.isNotEmpty != true) {
|
||
appInstanceId = await EventLogger.getAppInstanceId();
|
||
RuntimeProperty.instance.setString(PropertyKeys.appInstanceId, appInstanceId);
|
||
}
|
||
return appInstanceId;
|
||
}
|
||
|
||
void _initEnvProperties() async {
|
||
final bundle = await AppProperty.getInstance().loadValuesByTag(PropertyTags.analytics);
|
||
|
||
final userId = bundle.getString(PropertyKeys.analyticsUserId);
|
||
if (userId != null) {
|
||
setUserId(userId);
|
||
}
|
||
|
||
final adjustId = bundle.getString(PropertyKeys.analyticsAdjustId);
|
||
if (adjustId != null) {
|
||
setAdjustId(adjustId);
|
||
}
|
||
|
||
final adId = bundle.getString(PropertyKeys.analyticsAdId);
|
||
if (adId != null) {
|
||
setAdId(adId);
|
||
}
|
||
refreshEventStatistic();
|
||
|
||
String? firebaseId = await getAppInstanceId();
|
||
if (firebaseId.isEmpty) {
|
||
firebaseId = bundle.getString(PropertyKeys.analyticsFirebaseId);
|
||
}
|
||
if (firebaseId?.isNotEmpty == true) {
|
||
setFirebaseId(firebaseId!);
|
||
}
|
||
refreshABProperties();
|
||
}
|
||
|
||
void refreshABProperties() {
|
||
final abProperties = RemoteConfigManager.instance.getABProperties();
|
||
|
||
final PropertyBundle propertyBundle = PropertyBundle();
|
||
if (abProperties.isNotEmpty) {
|
||
for (var entry in abProperties.entries) {
|
||
setGuruUserProperty(entry.key, entry.value);
|
||
propertyBundle.setString(PropertyKeys.buildABTestProperty(entry.key), entry.value);
|
||
Log.d("setGuruUserProperty: ${entry.key} = ${entry.value}");
|
||
}
|
||
}
|
||
AppProperty.getInstance().setProperties(propertyBundle);
|
||
}
|
||
|
||
void _logFirstOpen() async {
|
||
int firstInstallTime =
|
||
RuntimeProperty.instance.getInt(PropertyKeys.firstInstallTime, defValue: -1);
|
||
if (firstInstallTime == -1) {
|
||
firstInstallTime = await AppProperty.getInstance()
|
||
.getOrCreateInt(PropertyKeys.firstInstallTime, DateTimeUtils.currentTimeInMillis());
|
||
}
|
||
setUserProperty("first_open_time", firstInstallTime.toString());
|
||
}
|
||
|
||
String getCountryCode() {
|
||
if (mockCountryCode != null) {
|
||
return mockCountryCode!;
|
||
}
|
||
final currentLocale = Platform.localeName.split('_');
|
||
if (currentLocale.length > 1) {
|
||
return currentLocale.last.toLowerCase();
|
||
}
|
||
return "";
|
||
}
|
||
|
||
void _logLocale() {
|
||
if (Platform.localeName.isNotEmpty == true) {
|
||
String lanCode = "";
|
||
String countryCode = "";
|
||
final currentLocale = Platform.localeName.split('_');
|
||
if (currentLocale.isNotEmpty) {
|
||
setUserProperty("lang_code", currentLocale[0].toLowerCase());
|
||
lanCode = currentLocale[0].toLowerCase();
|
||
}
|
||
|
||
if (currentLocale.length > 1) {
|
||
setUserProperty("country_code", currentLocale.last.toLowerCase());
|
||
countryCode = currentLocale.last.toLowerCase();
|
||
}
|
||
Log.d("## locale: [$currentLocale]");
|
||
|
||
if (lanCode.isNotEmpty && countryCode.isNotEmpty) {
|
||
// CountryCodes.init(Locale(lanCode, countryCode));
|
||
} else {
|
||
// CountryCodes.init();
|
||
}
|
||
} else {
|
||
// CountryCodes.init();
|
||
}
|
||
}
|
||
|
||
void _logDeviceType() async {
|
||
setUserProperty("device_type", DeviceUtils.isTablet() ? "tablet" : "phone");
|
||
|
||
final deviceId = await AppProperty.getInstance().getDeviceId();
|
||
setDeviceId(deviceId);
|
||
}
|
||
|
||
@override
|
||
Future setUserProperty(String key, String value) async {
|
||
recordEvents("setUserProperty", {key: value});
|
||
recordProperty(key, value);
|
||
if (release) {
|
||
await EventLogger.setUserProperty(key, value);
|
||
}
|
||
}
|
||
|
||
static String buildVariantKey(String experimentName) {
|
||
return "ab_$experimentName";
|
||
}
|
||
|
||
String getExperimentVariant(String experimentName) {
|
||
return abTestExperimentVariant.value[experimentName] ?? "BASELINE";
|
||
}
|
||
|
||
Future<bool> setLocalABTest(ABTestExperiment experiment, {PropertyBundle? bundle}) async {
|
||
Log.d("setLocalABTest: $experiment");
|
||
String experimentName = experiment.name;
|
||
final exp = await AppProperty.getInstance().getExperiment(experimentName, bundle: bundle);
|
||
if (exp != null) {
|
||
Log.w("Experiment already exists!");
|
||
experiment = exp;
|
||
}
|
||
|
||
return await _applyExperiment(experiment);
|
||
}
|
||
|
||
Future removeExperiment(String experimentName) async {
|
||
await AppProperty.getInstance().removeExperiment(experimentName);
|
||
final data = Map<String, String>.of(abTestExperimentVariant.value);
|
||
data.remove(experimentName);
|
||
abTestExperimentVariant.addIfChanged(data);
|
||
}
|
||
|
||
Future<bool> _applyExperiment(ABTestExperiment experiment) async {
|
||
final experimentName = experiment.name;
|
||
if (experiment.isExpired()) {
|
||
Log.w("Experiment($experimentName) is expired");
|
||
await removeExperiment(experimentName);
|
||
return false;
|
||
}
|
||
|
||
if (!experiment.isMatchAudience()) {
|
||
Log.i("NOT match audience! $experiment! INTO BASELINE");
|
||
return false;
|
||
}
|
||
|
||
String variantName = await AppProperty.getInstance().getExperimentVariant(experimentName);
|
||
if (variantName.isEmpty) {
|
||
variantName = await AppProperty.getInstance().setExperiment(experiment);
|
||
}
|
||
|
||
await setGuruUserProperty(buildVariantKey(experimentName), variantName);
|
||
Log.i("==> Setup Local Experiment($experimentName) variantName: $variantName");
|
||
|
||
final data = Map<String, String>.of(abTestExperimentVariant.value);
|
||
data[experimentName] = variantName;
|
||
abTestExperimentVariant.addIfChanged(data);
|
||
return true;
|
||
}
|
||
|
||
void setDeviceId(String deviceId) {
|
||
Log.d("setDeviceId: $deviceId");
|
||
recordEvents("setDeviceId", {"userId": deviceId});
|
||
recordProperty("deviceId", deviceId);
|
||
if (deviceId.isNotEmpty) {
|
||
AppProperty.getInstance().setAnalyticsDeviceId(deviceId);
|
||
if (release) {
|
||
EventLogger.setUserProperty("device_id", deviceId);
|
||
EventLogger.setDeviceId(deviceId);
|
||
}
|
||
}
|
||
}
|
||
|
||
Future setUserId(String userId) async {
|
||
Log.d("setUserId: $userId");
|
||
recordEvents("setUserId", {"userId": userId});
|
||
recordProperty("userId", userId);
|
||
if (userId.isNotEmpty) {
|
||
await AppProperty.getInstance().setUserId(userId);
|
||
if (release) {
|
||
EventLogger.setUserId(userId);
|
||
FirebaseCrashlytics.instance.setUserIdentifier(userId);
|
||
}
|
||
}
|
||
}
|
||
|
||
void setAdjustId(String adjustId) {
|
||
Log.d("setAdjustId: $adjustId");
|
||
recordEvents("setAdjustId", {"adjustId": adjustId});
|
||
recordProperty("adjustId", adjustId);
|
||
if (adjustId.isNotEmpty) {
|
||
AppProperty.getInstance().setAdjustId(adjustId);
|
||
updateUserIdentification(adId: adjustId);
|
||
if (release) {
|
||
EventLogger.setAdjustId(adjustId);
|
||
}
|
||
}
|
||
}
|
||
|
||
void setFirebaseId(String firebaseId) {
|
||
Log.d("setFirebaseId: $firebaseId");
|
||
recordEvents("setFirebaseId", {"firebaseId": firebaseId});
|
||
recordProperty("firebaseId", firebaseId);
|
||
if (firebaseId.isNotEmpty) {
|
||
AppProperty.getInstance().setFirebaseId(firebaseId);
|
||
updateUserIdentification(firebaseAppInstanceId: firebaseId);
|
||
if (release) {
|
||
EventLogger.setFirebaseId(firebaseId);
|
||
}
|
||
}
|
||
}
|
||
|
||
void setAdId(String adId) {
|
||
Log.d("setAdId: $adId");
|
||
recordEvents("setAdId", {"adId": adId});
|
||
recordProperty("adId", adId);
|
||
AppProperty.getInstance().setAdId(adId);
|
||
updateUserIdentification(gpsAdId: adId);
|
||
if (release) {
|
||
EventLogger.setAdId(adId);
|
||
}
|
||
}
|
||
|
||
void setIdfa(String idfa) {
|
||
Log.d("setIdfa: $idfa");
|
||
recordEvents("setIdfa", {"idfa": idfa});
|
||
recordProperty("idfa", idfa);
|
||
AppProperty.getInstance().setIdfa(idfa);
|
||
updateUserIdentification(idfa: idfa);
|
||
if (release) {
|
||
// 自打点中。idfa变是adId
|
||
EventLogger.setAdId(idfa);
|
||
}
|
||
}
|
||
|
||
void logScreen(String screenName) {
|
||
recordEvents("logScreen", {"name": screenName});
|
||
recordProperty("screen", screenName);
|
||
if (release) {
|
||
FirebaseCrashlytics.instance.log(screenName);
|
||
EventLogger.logScreen(screenName);
|
||
}
|
||
}
|
||
|
||
@override
|
||
void setScreen(String screenName) {
|
||
if (currentScreen != screenName) {
|
||
currentScreen = screenName;
|
||
logScreen(screenName);
|
||
}
|
||
}
|
||
|
||
@override
|
||
void logFirebase(String msg) async {
|
||
if (release) {
|
||
try {
|
||
FirebaseCrashlytics.instance.log(msg);
|
||
if (EventLogger.dumpLog) {
|
||
Log.d("[Firebase]: $msg");
|
||
}
|
||
} catch (error, stacktrace) {}
|
||
} else {
|
||
Log.d("[Firebase]: $msg");
|
||
}
|
||
}
|
||
|
||
AppEventOptions? getOptions(String eventName) {
|
||
return GuruAnalyticsStrategy.instance.getStrategyRule(eventName)?.getAppEventOptions();
|
||
}
|
||
|
||
@override
|
||
void logEvent(String eventName, Map<String, dynamic> parameters, {AppEventOptions? options}) {
|
||
refreshEventStatistic();
|
||
// Firebase Facebook log event
|
||
if (release) {
|
||
EventLogger.logEvent(eventName, parameters, options: options ?? getOptions(eventName));
|
||
_logAdjustEvent(eventName, parameters);
|
||
} else {
|
||
Log.d("logEvent: $eventName $parameters");
|
||
EventLogger.transmit(eventName, parameters);
|
||
}
|
||
}
|
||
|
||
@override
|
||
void logEventEx(String eventName,
|
||
{String? itemCategory,
|
||
String? itemName,
|
||
double? value,
|
||
Map<String, dynamic> parameters = const {},
|
||
AppEventOptions? options}) async {
|
||
Map<String, dynamic> map = Map<String, dynamic>.from(parameters);
|
||
if (itemCategory != null) {
|
||
map["item_category"] = itemCategory;
|
||
}
|
||
|
||
if (itemName != null) {
|
||
map["item_name"] = itemName;
|
||
}
|
||
|
||
if (value != null) {
|
||
map["value"] = value;
|
||
}
|
||
|
||
logEvent(eventName, map, options: options);
|
||
}
|
||
|
||
Future refreshEventStatistic({bool force = false}) async {
|
||
if (!GuruApp.instance.appSpec.deployment.enableAnalyticsStatistic) {
|
||
return;
|
||
}
|
||
final now = DateTimeUtils.currentTimeInMillis();
|
||
if (force || (now - latestFetchStatisticTs > DateTimeUtils.minuteInMillis * 2)) {
|
||
EventLogger.getStatistic().then((statistic) {
|
||
Log.d("Event Statistic:$statistic");
|
||
if (statistic != GuruStatistic.invalid && guruEventStatistic.addIfChanged(statistic)) {
|
||
setUserProperty("lgd", statistic.logged.toString());
|
||
setUserProperty("uld", statistic.uploaded.toString());
|
||
}
|
||
});
|
||
latestFetchStatisticTs = now;
|
||
}
|
||
}
|
||
|
||
Future<String> zipGuruLogs() {
|
||
return EventLogger.zipGuruLogs();
|
||
}
|
||
|
||
Map<String, dynamic> filterOutNulls(Map<String, dynamic> parameters) {
|
||
final Map<String, dynamic> filtered = <String, dynamic>{};
|
||
parameters.forEach((String key, dynamic value) {
|
||
if (value != null) {
|
||
filtered[key] = value;
|
||
}
|
||
});
|
||
return filtered;
|
||
}
|
||
|
||
@override
|
||
void logException(dynamic exception, {StackTrace? stacktrace}) async {
|
||
if (release) {
|
||
Log.d("exception! $exception");
|
||
FirebaseCrashlytics.instance.log(exception.toString());
|
||
FirebaseCrashlytics.instance
|
||
.recordError(exception, stacktrace, printDetails: EventLogger.dumpLog);
|
||
} else {
|
||
Log.w("Occur Error! $exception $stacktrace", stackTrace: stacktrace);
|
||
}
|
||
}
|
||
|
||
void logPurchase(double amount,
|
||
{String currency = "",
|
||
String contentId = "",
|
||
String adPlatform = "",
|
||
Map<String, dynamic> parameters = const <String, dynamic>{}}) async {
|
||
Log.i("logPurchase:$amount, $currency, $contentId, $adPlatform, $parameters");
|
||
try {
|
||
await EventLogger.logFbPurchase(amount,
|
||
currency: currency,
|
||
contentId: contentId,
|
||
adPlatform: adPlatform,
|
||
additionParameters: parameters);
|
||
} catch (error, stacktrace) {
|
||
Log.w("logFbPurchase error$error, $stacktrace");
|
||
}
|
||
}
|
||
|
||
void logEventShare({String? itemCategory, String? itemName}) {
|
||
logEvent("share", {
|
||
"item_category": itemCategory,
|
||
"item_name": itemName,
|
||
"content_type": itemCategory,
|
||
"item_id": itemName
|
||
});
|
||
}
|
||
|
||
void logSpendCredits(String contentId, String contentType, int price,
|
||
{required String virtualCurrencyName, required int balance, String scene = ''}) {
|
||
final levelName = GuruApp.instance.protocol.getLevelName();
|
||
if (release) {
|
||
EventLogger.logSpendCredits(contentId, contentType, price,
|
||
virtualCurrencyName: virtualCurrencyName, balance: balance, scene: scene);
|
||
} else {
|
||
final parameters = <String, dynamic>{
|
||
"item_name": contentId,
|
||
"item_category": contentType,
|
||
"virtual_currency_name": virtualCurrencyName,
|
||
"value": price,
|
||
"balance": balance,
|
||
"scene": scene,
|
||
"level_name": levelName
|
||
};
|
||
Log.d("logEvent: spend_virtual_currency $parameters");
|
||
EventLogger.transmit("spend_virtual_currency", parameters);
|
||
}
|
||
AiBi.instance.spendVirtualCurrency(balance, price.toDouble(), contentType);
|
||
}
|
||
|
||
Future<void> logEarnVirtualCurrency(
|
||
{required String virtualCurrencyName,
|
||
required String method,
|
||
required int balance,
|
||
required int value,
|
||
String? specific,
|
||
String? scene}) async {
|
||
final levelName = GuruApp.instance.protocol.getLevelName();
|
||
logEvent(
|
||
"earn_virtual_currency",
|
||
filterOutNulls(<String, dynamic>{
|
||
"virtual_currency_name": virtualCurrencyName,
|
||
"item_category": method,
|
||
"item_name": specific,
|
||
"value": value,
|
||
"balance": balance,
|
||
"level_name": levelName,
|
||
"scene": scene
|
||
}));
|
||
AiBi.instance.earnVirtualCurrency(balance, value.toDouble(), method);
|
||
}
|
||
|
||
String? peekUserProperty(String key) {
|
||
return Analytics.userProperties[key];
|
||
}
|
||
|
||
Future<void> setGuruUserProperty(String key, String value) async {
|
||
recordProperty(key, value);
|
||
return await EventLogger.setGuruUserProperty(key, value);
|
||
}
|
||
|
||
Future<void> logGuruEvent(String eventName, Map<String, dynamic> parameters) async {
|
||
EventLogger.guruLogEvent(name: eventName, parameters: parameters);
|
||
}
|
||
|
||
Future<void> logFirebaseEvent(String eventName, Map<String, dynamic> parameters) async {
|
||
if (release) {
|
||
EventLogger.firebaseLogEvent(name: eventName, parameters: parameters);
|
||
} else {
|
||
Log.d("logEvent: $eventName $parameters");
|
||
}
|
||
EventLogger.transmit(eventName, parameters);
|
||
}
|
||
}
|