2023-12-21 09:14:40 +00:00
|
|
|
|
/// Created by Haoyi on 2022/8/24
|
|
|
|
|
|
|
|
|
|
|
|
import 'dart:collection';
|
|
|
|
|
|
import 'dart:core';
|
|
|
|
|
|
import 'dart:io';
|
|
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
|
import 'package:adjust_sdk/adjust_third_party_sharing.dart';
|
2023-12-21 09:14:40 +00:00
|
|
|
|
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
2024-03-07 03:46:50 +00:00
|
|
|
|
import 'package:firebase_remote_config/firebase_remote_config.dart';
|
2023-12-21 09:14:40 +00:00
|
|
|
|
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';
|
2024-03-07 03:46:50 +00:00
|
|
|
|
import 'package:guru_app/analytics/abtest/abtest_model.dart';
|
2023-12-21 09:14:40 +00:00
|
|
|
|
import 'package:guru_app/analytics/data/analytics_model.dart';
|
|
|
|
|
|
import 'package:guru_app/analytics/strategy/guru_analytics_strategy.dart';
|
2024-03-07 03:46:50 +00:00
|
|
|
|
import 'package:device_info_plus/device_info_plus.dart';
|
2023-12-21 09:14:40 +00:00
|
|
|
|
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';
|
2024-03-07 03:46:50 +00:00
|
|
|
|
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';
|
2023-12-21 09:14:40 +00:00
|
|
|
|
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 {
|
2024-03-07 03:46:50 +00:00
|
|
|
|
bool get release => !_mock && (_enabledAnalytics || kReleaseMode);
|
2023-12-21 09:14:40 +00:00
|
|
|
|
|
|
|
|
|
|
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 = "";
|
|
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
|
static final RegExp _consentPurposeRegExp = RegExp(r"^[01]+$");
|
|
|
|
|
|
|
|
|
|
|
|
static String? mockCountryCode;
|
|
|
|
|
|
|
2023-12-21 09:14:40 +00:00
|
|
|
|
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);
|
|
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
|
final BehaviorSubject<Map<String, String>> abTestExperimentVariant = BehaviorSubject.seeded({});
|
|
|
|
|
|
|
2023-12-21 09:14:40 +00:00
|
|
|
|
Stream<GuruStatistic> get observableGuruEventStatistic => guruEventStatistic.stream;
|
|
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
|
Stream<Map<String, String>> get observableABTestExperimentVariant =>
|
|
|
|
|
|
abTestExperimentVariant.stream;
|
|
|
|
|
|
|
2023-12-21 09:14:40 +00:00
|
|
|
|
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];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
|
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();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-21 09:14:40 +00:00
|
|
|
|
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();
|
2024-03-07 03:46:50 +00:00
|
|
|
|
refreshConsents();
|
2023-12-21 09:14:40 +00:00
|
|
|
|
Log.d("register transmitter");
|
|
|
|
|
|
});
|
|
|
|
|
|
initialized = true;
|
|
|
|
|
|
// if (Platform.isAndroid) {
|
|
|
|
|
|
// _logPeerApps();
|
|
|
|
|
|
// }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-21 09:14:40 +00:00
|
|
|
|
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!);
|
|
|
|
|
|
}
|
2024-03-07 03:46:50 +00:00
|
|
|
|
refreshABProperties();
|
|
|
|
|
|
}
|
2023-12-21 09:14:40 +00:00
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
|
void refreshABProperties() {
|
2023-12-21 09:14:40 +00:00
|
|
|
|
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());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
|
String getCountryCode() {
|
|
|
|
|
|
if (mockCountryCode != null) {
|
|
|
|
|
|
return mockCountryCode!;
|
|
|
|
|
|
}
|
|
|
|
|
|
final currentLocale = Platform.localeName.split('_');
|
|
|
|
|
|
if (currentLocale.length > 1) {
|
|
|
|
|
|
return currentLocale.last.toLowerCase();
|
|
|
|
|
|
}
|
|
|
|
|
|
return "";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-21 09:14:40 +00:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-21 09:14:40 +00:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
|
Future setUserId(String userId) async {
|
2023-12-21 09:14:40 +00:00
|
|
|
|
Log.d("setUserId: $userId");
|
|
|
|
|
|
recordEvents("setUserId", {"userId": userId});
|
|
|
|
|
|
recordProperty("userId", userId);
|
|
|
|
|
|
if (userId.isNotEmpty) {
|
2024-03-07 03:46:50 +00:00
|
|
|
|
await AppProperty.getInstance().setUserId(userId);
|
2023-12-21 09:14:40 +00:00
|
|
|
|
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 = "",
|
2024-03-07 03:46:50 +00:00
|
|
|
|
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");
|
|
|
|
|
|
}
|
2023-12-21 09:14:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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 = ''}) {
|
2024-03-07 03:46:50 +00:00
|
|
|
|
final levelName = GuruApp.instance.protocol.getLevelName();
|
2023-12-21 09:14:40 +00:00
|
|
|
|
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,
|
2024-03-07 03:46:50 +00:00
|
|
|
|
"scene": scene,
|
|
|
|
|
|
"level_name": levelName
|
2023-12-21 09:14:40 +00:00
|
|
|
|
};
|
|
|
|
|
|
Log.d("logEvent: spend_virtual_currency $parameters");
|
|
|
|
|
|
EventLogger.transmit("spend_virtual_currency", parameters);
|
|
|
|
|
|
}
|
|
|
|
|
|
AiBi.instance.spendVirtualCurrency(balance, price.toDouble(), contentType);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
|
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
|
|
|
|
|
|
}));
|
2023-12-21 09:14:40 +00:00
|
|
|
|
AiBi.instance.earnVirtualCurrency(balance, value.toDouble(), method);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
|
String? peekUserProperty(String key) {
|
|
|
|
|
|
return Analytics.userProperties[key];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-12-21 09:14:40 +00:00
|
|
|
|
Future<void> setGuruUserProperty(String key, String value) async {
|
2024-03-07 03:46:50 +00:00
|
|
|
|
recordProperty(key, value);
|
2023-12-21 09:14:40 +00:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|