add facebook
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
84
lib/facebook/facebook_service.dart
Normal file
84
lib/facebook/facebook_service.dart
Normal 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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user