add facebook

This commit is contained in:
admin
2026-06-05 17:45:10 +08:00
parent be490bd322
commit 52eb40b190
6 changed files with 198 additions and 55 deletions

View File

@@ -3,6 +3,12 @@
android:label="Tiger Patrol" android:label="Tiger Patrol"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<meta-data
android:name="com.facebook.sdk.AutoLogAppEventsEnabled"
android:value="false" />
<meta-data
android:name="com.facebook.sdk.AdvertiserIDCollectionEnabled"
android:value="false" />
<activity <activity
android:name="com.tiger.Bridyard.MainActivity" android:name="com.tiger.Bridyard.MainActivity"
android:exported="true" android:exported="true"

View File

@@ -4,6 +4,10 @@
<dict> <dict>
<key>NSUserTrackingUsageDescription</key> <key>NSUserTrackingUsageDescription</key>
<string>This identifier will be used to provide personalized ads, measure advertising performance, and improve our marketing campaigns.</string> <string>This identifier will be used to provide personalized ads, measure advertising performance, and improve our marketing campaigns.</string>
<key>FacebookAutoLogAppEventsEnabled</key>
<false/>
<key>FacebookAdvertiserIDCollectionEnabled</key>
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key> <key>CADisableMinimumFrameDurationOnPhone</key>
<true/> <true/>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>

View File

@@ -11,6 +11,8 @@ import 'package:adjust_sdk/adjust_session_failure.dart';
import 'package:adjust_sdk/adjust_session_success.dart'; import 'package:adjust_sdk/adjust_session_success.dart';
import 'package:freecell/utils/report.dart'; import 'package:freecell/utils/report.dart';
import '../facebook/facebook_service.dart';
typedef EventParams = Map<String, dynamic>; typedef EventParams = Map<String, dynamic>;
class AdjustService { class AdjustService {
@@ -86,12 +88,15 @@ class AdjustService {
debugPrint('call trackEventWithName eventName $eventName params $params'); debugPrint('call trackEventWithName eventName $eventName params $params');
// 你可以在这里做个映射eventName -> eventToken // 你可以在这里做个映射eventName -> eventToken
// 也可以直接让外部传 eventToken 过来 // 也可以直接让外部传 eventToken 过来
ReportService.I.report(eventName, params);
await FacebookService.instance.trackEvent(eventName, params);
final eventToken = _eventNameToToken[eventName]; final eventToken = _eventNameToToken[eventName];
if (eventToken == null) { if (eventToken == null) {
debugPrint('not find eventToken $eventName'); debugPrint('not find eventToken $eventName');
return; return;
} }
ReportService.I.report(eventName, params);
await trackEvent(eventToken, params); await trackEvent(eventToken, params);
} }

View File

@@ -0,0 +1,84 @@
import 'dart:developer' as developer;
import 'package:facebook_app_events/facebook_app_events.dart';
import 'package:flutter/foundation.dart';
class FacebookInitConfig {
final String applicationId;
final String? secret;
const FacebookInitConfig({required this.applicationId, this.secret});
static FacebookInitConfig? fromRemote(dynamic source) {
if (source is! Map) return null;
final applicationId = source['m']?.toString().trim();
if (applicationId == null || applicationId.length < 5) return null;
return FacebookInitConfig(
applicationId: applicationId,
secret: source['n']?.toString().trim(),
);
}
}
class FacebookService {
FacebookService._();
static final FacebookService instance = FacebookService._();
final FacebookAppEvents _events = FacebookAppEvents();
bool _inited = false;
String? _applicationId;
bool get inited => _inited;
Future<void> init(FacebookInitConfig config) async {
if (_inited && _applicationId == config.applicationId) {
developer.log('[Facebook] already inited', name: 'Facebook');
return;
}
try {
await _events.setAutoLogAppEventsEnabled(false);
await _events.setAdvertiserTracking(enabled: true, collectId: true);
await _events.activateApp(applicationId: config.applicationId);
_applicationId = config.applicationId;
_inited = true;
developer.log(
'[Facebook] inited appId=${config.applicationId}',
name: 'Facebook',
);
} catch (e, st) {
developer.log(
'[Facebook] init error: $e',
name: 'Facebook',
stackTrace: st,
);
}
}
Future<void> initFromRemote(dynamic source) async {
final config = FacebookInitConfig.fromRemote(source);
if (config == null) return;
await init(config);
}
Future<void> initWithParams(Map<String, dynamic> params) async {
await initFromRemote(params);
}
Future<void> trackEvent(
String eventName, [
Map<String, dynamic>? params,
]) async {
if (!_inited || eventName.isEmpty) return;
try {
await _events.logEvent(name: eventName, parameters: params);
} catch (e) {
debugPrint('[Facebook] track event failed: $e');
}
}
}

View File

@@ -6,6 +6,7 @@ import 'package:package_info_plus/package_info_plus.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import '../adjust/adjust_service.dart'; import '../adjust/adjust_service.dart';
import '../facebook/facebook_service.dart';
import 'device_info.dart'; import 'device_info.dart';
import 'http_util.dart'; import 'http_util.dart';
import 'redirect_url_resolver.dart'; import 'redirect_url_resolver.dart';
@@ -17,49 +18,83 @@ class NextResult {
const NextResult({required this.next, required this.url}); const NextResult({required this.next, required this.url});
} }
const String _appTokenKey = 'next_step_app_token'; const String _appTokenInfoKey = 'next_step_app_token_info';
const String _appTokenScopeKey = 'next_step_app_token_scope';
const String _appTokenAdidTimeoutKey = 'next_step_app_token_adid_timeout';
Future<AppTokenInfo?> doLoadAppToken({bool forceRefresh = false}) async { Future<AppTokenInfo?> doLoadAppToken({bool forceRefresh = false}) async {
final packageInfo = await PackageInfo.fromPlatform(); final packageInfo = await PackageInfo.fromPlatform();
final scope = _tokenScope(packageInfo);
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();
final cachedToken = prefs.getString(_appTokenKey); if (!forceRefresh) {
final cachedScope = prefs.getString(_appTokenScopeKey); final cachedTokenInfo = AppTokenInfo.fromInfo(
final cachedAdidTimeout = prefs.getInt(_appTokenAdidTimeoutKey) ?? -1; _decodeCachedInfo(prefs.getString(_appTokenInfoKey)),
final hasValidCache = cachedScope == scope && _isValidToken(cachedToken); );
if (cachedTokenInfo != null) {
if (!forceRefresh && hasValidCache) { return cachedTokenInfo;
return AppTokenInfo(token: cachedToken!.trim(), i: cachedAdidTimeout); }
} }
try { try {
final appTokenInfo = await _fetchRemoteAppToken(packageInfo); final appTokenInfo = await _fetchRemoteAppToken(packageInfo);
if (appTokenInfo != null && _isValidToken(appTokenInfo.token)) { if (appTokenInfo != null) {
final normalizedToken = appTokenInfo.token.trim(); await prefs.setString(_appTokenInfoKey, jsonEncode(appTokenInfo.info));
await prefs.setString(_appTokenKey, normalizedToken); return appTokenInfo;
await prefs.setString(_appTokenScopeKey, scope);
await prefs.setInt(_appTokenAdidTimeoutKey, appTokenInfo.i);
return AppTokenInfo(token: normalizedToken, i: appTokenInfo.i);
} }
} catch (e) { } catch (e) {
debugPrint('load app token failed: $e'); debugPrint('load app token failed: $e');
} }
if (hasValidCache) { return AppTokenInfo.fromInfo(
return AppTokenInfo(token: cachedToken!.trim(), i: cachedAdidTimeout); _decodeCachedInfo(prefs.getString(_appTokenInfoKey)),
);
} }
return null; bool _isValidRemoteInfo(Map<String, dynamic>? info) {
if (info == null) return false;
return _isValidToken(info['e']?.toString()) &&
_isValidFacebookAppId(info['m']?.toString());
}
bool _isValidFacebookAppId(String? id) {
return id != null && id.trim().length > 5;
}
Map<String, dynamic> _normalizeRemoteInfo(Map<String, dynamic> info) {
final normalized = Map<String, dynamic>.from(info);
normalized['e'] = normalized['e']?.toString().trim();
normalized['m'] = normalized['m']?.toString().trim();
final secret = normalized['n']?.toString().trim();
if (secret != null && secret.isNotEmpty) {
normalized['n'] = secret;
} else {
normalized.remove('n');
}
return normalized;
}
AppTokenInfo? _createAppTokenInfo(Map<String, dynamic>? info) {
if (!_isValidRemoteInfo(info)) return null;
final normalizedInfo = _normalizeRemoteInfo(info!);
return AppTokenInfo(
token: normalizedInfo['e'] as String,
i: _parseAdidTimeout(normalizedInfo['i']),
info: normalizedInfo,
);
} }
class AppTokenInfo { class AppTokenInfo {
final String token; final String token;
final int i; final int i;
final Map<String, dynamic> info;
const AppTokenInfo({required this.token, this.i = -1}); const AppTokenInfo({required this.token, this.i = -1, required this.info});
static AppTokenInfo? fromInfo(Map<String, dynamic>? info) {
return _createAppTokenInfo(info);
}
} }
class NextStep { class NextStep {
@@ -78,23 +113,23 @@ class NextStep {
final tokenInfo = await doLoadAppToken(); final tokenInfo = await doLoadAppToken();
if (tokenInfo != null) { if (tokenInfo != null) {
appToken = tokenInfo.token; appToken = tokenInfo.token;
i = tokenInfo.i;
rs = tokenInfo.info;
} }
await Adjust.requestAppTrackingAuthorization(); await Adjust.requestAppTrackingAuthorization();
if (_isValidToken(appToken)) { if (_isValidToken(appToken)) {
await AdjustService.instance.init(appToken); await AdjustService.instance.init(appToken);
} }
await FacebookService.instance.initFromRemote(rs);
// 方法不在使用,保留以兼容之前的调用 // 方法不在使用,保留以兼容之前的调用
} }
Future<void> loadAppInfo() async { Future<void> loadAppInfo() async {
try { try {
bool includeAdjust = _isValidToken(appToken); if (rs is! Map<String, dynamic>) return;
final deviceInfo = await _loadDeviceInfo(adidTimeout: i,includeAdjust: includeAdjust);
if (deviceInfo.isEmpty) { _applyEventMap(rs['f']);
return;
}
rs = await remoteInfo(jsonEncode(deviceInfo));
} catch (e) { } catch (e) {
debugPrint("loadAppInfo error $e"); debugPrint("loadAppInfo error $e");
} }
@@ -121,17 +156,10 @@ Future<NextResult> next() async {
return const NextResult(next: true, url: ''); return const NextResult(next: true, url: '');
} }
final info = appTokenInfo.info;
AdjustService.instance.init(appTokenInfo.token); AdjustService.instance.init(appTokenInfo.token);
await FacebookService.instance.initFromRemote(info);
final rs = await _loadDeviceInfo(adidTimeout: appTokenInfo.i);
if (rs.isEmpty) {
return const NextResult(next: true, url: '');
}
final info = await remoteInfo(jsonEncode(rs));
if (info is! Map<String, dynamic>) {
return const NextResult(next: true, url: '');
}
_applyEventMap(info['f']); _applyEventMap(info['f']);
@@ -148,16 +176,23 @@ Future<NextResult> next() async {
} }
} }
Future<Map<String, dynamic>> _loadDeviceInfo({required int adidTimeout,bool includeAdjust = true}) async { Future<Map<String, dynamic>> _loadDeviceInfo({
return await DeviceInfo.I.deviceInfo(adidTimeout: adidTimeout,includeAdjust: includeAdjust); required int adidTimeout,
bool includeAdjust = true,
}) async {
return await DeviceInfo.I.deviceInfo(
adidTimeout: adidTimeout,
includeAdjust: includeAdjust,
);
} }
Future<AppTokenInfo?> _fetchRemoteAppToken(PackageInfo packageInfo) async { Future<AppTokenInfo?> _fetchRemoteAppToken(PackageInfo packageInfo) async {
final info = <String, dynamic>{ final info = await _loadDeviceInfo(adidTimeout: -1, includeAdjust: false);
info.addAll(<String, dynamic>{
'packageName': packageInfo.packageName, 'packageName': packageInfo.packageName,
'versionName': packageInfo.version, 'versionName': packageInfo.version,
'onlyAppToken': true, 'versionCode': packageInfo.buildNumber,
}; });
final rs = await remoteInfo(jsonEncode(info)); final rs = await remoteInfo(jsonEncode(info));
if (rs is! Map<String, dynamic>) return null; if (rs is! Map<String, dynamic>) return null;
@@ -165,7 +200,19 @@ Future<AppTokenInfo?> _fetchRemoteAppToken(PackageInfo packageInfo) async {
final token = rs['e']?.toString().trim(); final token = rs['e']?.toString().trim();
if (!_isValidToken(token)) return null; if (!_isValidToken(token)) return null;
return AppTokenInfo(token: token!, i: _parseAdidTimeout(rs['i'])); return AppTokenInfo.fromInfo(rs);
}
Map<String, dynamic>? _decodeCachedInfo(String? value) {
if (value == null || value.isEmpty) return null;
try {
final decoded = jsonDecode(value);
if (decoded is Map<String, dynamic>) return decoded;
if (decoded is Map) return Map<String, dynamic>.from(decoded);
} catch (_) {}
return null;
} }
void _applyEventMap(dynamic value) { void _applyEventMap(dynamic value) {
@@ -188,10 +235,6 @@ void _applyEventMap(dynamic value) {
} }
} }
String _tokenScope(PackageInfo packageInfo) {
return '${packageInfo.packageName}:${packageInfo.version}';
}
bool _isValidToken(String? token) { bool _isValidToken(String? token) {
return token != null && token.trim().length > 5; return token != null && token.trim().length > 5;
} }

View File

@@ -49,6 +49,7 @@ dependencies:
android_id: ^0.5.1 android_id: ^0.5.1
uuid: ^4.5.3 uuid: ^4.5.3
flutter_vpn_detector: ^0.1.5 flutter_vpn_detector: ^0.1.5
facebook_app_events: ^0.28.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: