2026-06-02 19:01:08 +08:00
|
|
|
|
import 'dart:developer' as developer;
|
|
|
|
|
|
|
|
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
|
|
import 'package:adjust_sdk/adjust.dart';
|
|
|
|
|
|
import 'package:adjust_sdk/adjust_attribution.dart';
|
|
|
|
|
|
import 'package:adjust_sdk/adjust_config.dart';
|
|
|
|
|
|
import 'package:adjust_sdk/adjust_event.dart';
|
|
|
|
|
|
import 'package:adjust_sdk/adjust_event_failure.dart';
|
|
|
|
|
|
import 'package:adjust_sdk/adjust_event_success.dart';
|
|
|
|
|
|
import 'package:adjust_sdk/adjust_session_failure.dart';
|
|
|
|
|
|
import 'package:adjust_sdk/adjust_session_success.dart';
|
|
|
|
|
|
import 'package:freecell/utils/report.dart';
|
|
|
|
|
|
|
2026-06-05 17:45:10 +08:00
|
|
|
|
import '../facebook/facebook_service.dart';
|
|
|
|
|
|
|
2026-06-02 19:01:08 +08:00
|
|
|
|
typedef EventParams = Map<String, dynamic>;
|
|
|
|
|
|
|
|
|
|
|
|
class AdjustService {
|
|
|
|
|
|
Map<String, String> _eventNameToToken = {};
|
|
|
|
|
|
Map<String, String> get eventNameToToken => _eventNameToToken;
|
|
|
|
|
|
set eventNameToToken(Map<String, String> m) {
|
|
|
|
|
|
_eventNameToToken = Map<String, String>.from(m);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
AdjustService._();
|
|
|
|
|
|
|
|
|
|
|
|
static final AdjustService instance = AdjustService._();
|
|
|
|
|
|
|
|
|
|
|
|
bool _inited = false;
|
|
|
|
|
|
|
|
|
|
|
|
String _toStr(dynamic v) => '$v';
|
|
|
|
|
|
|
|
|
|
|
|
double? _toNum(dynamic v) {
|
|
|
|
|
|
if (v == null) return null;
|
|
|
|
|
|
if (v is num) return v.toDouble();
|
|
|
|
|
|
return double.tryParse('$v');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-05 17:45:10 +08:00
|
|
|
|
Future<void> init(String appToken, [bool isProd = true]) async {
|
2026-06-02 19:01:08 +08:00
|
|
|
|
developer.log(appToken, name: 'Adjust');
|
|
|
|
|
|
|
|
|
|
|
|
if (_inited) {
|
|
|
|
|
|
developer.log('[Adjust] already inited', name: 'Adjust');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (appToken.isEmpty) return;
|
|
|
|
|
|
|
|
|
|
|
|
final environment = isProd
|
|
|
|
|
|
? AdjustEnvironment.production
|
|
|
|
|
|
: AdjustEnvironment.sandbox;
|
|
|
|
|
|
|
|
|
|
|
|
final config = AdjustConfig(appToken, environment);
|
|
|
|
|
|
|
|
|
|
|
|
config.isCostDataInAttributionEnabled = true;
|
2026-06-05 17:45:10 +08:00
|
|
|
|
|
2026-06-02 19:01:08 +08:00
|
|
|
|
config.attributionCallback = (AdjustAttribution a) async {
|
|
|
|
|
|
debugPrint('Adjust attribution: ${a.toString()}');
|
|
|
|
|
|
ReportService.I.attribution = a;
|
|
|
|
|
|
ReportService.I.reportAttribution();
|
|
|
|
|
|
};
|
|
|
|
|
|
config.eventSuccessCallback = (AdjustEventSuccess s) {
|
|
|
|
|
|
debugPrint('Adjust event success: ${s.toString()}');
|
|
|
|
|
|
};
|
|
|
|
|
|
config.eventFailureCallback = (AdjustEventFailure f) {
|
|
|
|
|
|
debugPrint('Adjust event failure: ${f.toString()}');
|
|
|
|
|
|
};
|
|
|
|
|
|
config.sessionSuccessCallback = (AdjustSessionSuccess s) {
|
|
|
|
|
|
debugPrint('Adjust session success: ${s.toString()}');
|
|
|
|
|
|
};
|
|
|
|
|
|
config.sessionFailureCallback = (AdjustSessionFailure f) {
|
|
|
|
|
|
debugPrint('Adjust session failure: ${f.toString()}');
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 可选:如果你希望缓存更多 deduplicationId,可调大
|
|
|
|
|
|
config.eventDeduplicationIdsMaxSize = 20;
|
|
|
|
|
|
|
|
|
|
|
|
config.logLevel = isProd ? AdjustLogLevel.info : AdjustLogLevel.verbose;
|
|
|
|
|
|
|
|
|
|
|
|
Adjust.initSdk(config);
|
|
|
|
|
|
_inited = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Future<void> trackEventWithName(
|
|
|
|
|
|
String eventName, [
|
|
|
|
|
|
EventParams? params,
|
|
|
|
|
|
]) async {
|
|
|
|
|
|
debugPrint('call trackEventWithName eventName $eventName params $params');
|
|
|
|
|
|
// 你可以在这里做个映射,eventName -> eventToken
|
|
|
|
|
|
// 也可以直接让外部传 eventToken 过来
|
2026-06-05 17:45:10 +08:00
|
|
|
|
ReportService.I.report(eventName, params);
|
|
|
|
|
|
await FacebookService.instance.trackEvent(eventName, params);
|
|
|
|
|
|
|
2026-06-02 19:01:08 +08:00
|
|
|
|
final eventToken = _eventNameToToken[eventName];
|
|
|
|
|
|
if (eventToken == null) {
|
|
|
|
|
|
debugPrint('not find eventToken $eventName');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-06-05 17:45:10 +08:00
|
|
|
|
|
2026-06-02 19:01:08 +08:00
|
|
|
|
await trackEvent(eventToken, params);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Future<void> trackEvent(String eventToken, [EventParams? params]) async {
|
|
|
|
|
|
try {
|
|
|
|
|
|
debugPrint("track event eventToken $eventToken params $params");
|
|
|
|
|
|
if (eventToken.isEmpty) return;
|
|
|
|
|
|
|
|
|
|
|
|
final event = AdjustEvent(eventToken);
|
|
|
|
|
|
|
|
|
|
|
|
if (params != null && params.isNotEmpty) {
|
|
|
|
|
|
double? revenue;
|
|
|
|
|
|
String? currency;
|
|
|
|
|
|
|
|
|
|
|
|
// 优先级:deduplicationId > orderId > transactionId
|
|
|
|
|
|
String? dedupId;
|
|
|
|
|
|
|
|
|
|
|
|
for (final entry in params.entries) {
|
|
|
|
|
|
final key = entry.key;
|
|
|
|
|
|
final value = entry.value;
|
|
|
|
|
|
|
|
|
|
|
|
if (value == null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
switch (key) {
|
|
|
|
|
|
case 'revenue':
|
|
|
|
|
|
case 'amount':
|
|
|
|
|
|
revenue = _toNum(value);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'currency':
|
|
|
|
|
|
currency = _toStr(value);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'deduplicationId':
|
|
|
|
|
|
case 'orderId':
|
|
|
|
|
|
dedupId ??= _toStr(value);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'callbackId':
|
|
|
|
|
|
event.callbackId = _toStr(value);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'transactionId':
|
|
|
|
|
|
final tid = _toStr(value);
|
|
|
|
|
|
event.transactionId = tid;
|
|
|
|
|
|
dedupId ??= tid;
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'productId':
|
|
|
|
|
|
event.productId = _toStr(value);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case 'purchaseToken':
|
|
|
|
|
|
event.purchaseToken = _toStr(value);
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
final v = _toStr(value);
|
|
|
|
|
|
|
|
|
|
|
|
if (key.startsWith('partner:')) {
|
|
|
|
|
|
final realKey = key.substring('partner:'.length);
|
|
|
|
|
|
if (realKey.isNotEmpty) {
|
|
|
|
|
|
event.addPartnerParameter(realKey, v);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
event.addCallbackParameter(key, v);
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (dedupId != null && dedupId.isNotEmpty) {
|
|
|
|
|
|
event.deduplicationId = dedupId;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (revenue != null && currency != null && currency.isNotEmpty) {
|
|
|
|
|
|
event.setRevenue(revenue, currency);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Adjust.trackEvent(event);
|
|
|
|
|
|
} catch (e, st) {
|
|
|
|
|
|
developer.log(
|
|
|
|
|
|
'[Adjust] trackEvent error: $e',
|
|
|
|
|
|
name: 'Adjust',
|
|
|
|
|
|
stackTrace: st,
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|