feat:"调试穿山甲广告可不影响程序执行成都"
This commit is contained in:
@@ -1,8 +1,19 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<!-- 网络与广告相关权限 -->
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<!-- 定位(可选) -->
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
|
<!-- 访问图片(Android 13+ 使用 READ_MEDIA_IMAGES,兼容旧版使用 READ_EXTERNAL_STORAGE) -->
|
||||||
|
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<application
|
<application
|
||||||
android:label="my_app"
|
android:label="my_app"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:usesCleartextTraffic="true"
|
||||||
|
tools:replace="android:label">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
|||||||
@@ -1,5 +1,81 @@
|
|||||||
package com.example.my_app
|
package com.example.my_app
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
import com.bytedance.sdk.openadsdk.TTAdConfig
|
||||||
|
import com.bytedance.sdk.openadsdk.TTAdSdk
|
||||||
|
|
||||||
class MainActivity : FlutterActivity()
|
class MainActivity : FlutterActivity() {
|
||||||
|
|
||||||
|
private val pangleChannelName = "pangle_mediation"
|
||||||
|
|
||||||
|
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||||
|
super.configureFlutterEngine(flutterEngine)
|
||||||
|
|
||||||
|
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, pangleChannelName)
|
||||||
|
.setMethodCallHandler { call, result ->
|
||||||
|
when (call.method) {
|
||||||
|
"init" -> {
|
||||||
|
initMediationAdSdk(this)
|
||||||
|
result.success(null)
|
||||||
|
}
|
||||||
|
"initWithConfig" -> {
|
||||||
|
val args = call.arguments as? Map<*, *>
|
||||||
|
val appId = args?.get("appId") as? String ?: ""
|
||||||
|
val privacy = args?.get("privacy") as? Map<*, *>
|
||||||
|
initMediationAdSdk(this, appId, privacy)
|
||||||
|
result.success(null)
|
||||||
|
}
|
||||||
|
"showSplash" -> {
|
||||||
|
val args = call.arguments as? Map<*, *>
|
||||||
|
val codeId = args?.get("codeId") as? String ?: ""
|
||||||
|
SplashAdActivity.start(this, codeId)
|
||||||
|
result.success(null)
|
||||||
|
}
|
||||||
|
else -> result.notImplemented()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initMediationAdSdk(context: Context, appIdOverride: String? = null, privacy: Map<*, *>? = null) {
|
||||||
|
TTAdSdk.init(context, buildConfig(context, appIdOverride, privacy))
|
||||||
|
TTAdSdk.start(object : TTAdSdk.Callback {
|
||||||
|
override fun success() {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fail(code: Int, msg: String?) {
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildConfig(context: Context, appIdOverride: String? = null, privacy: Map<*, *>? = null): TTAdConfig {
|
||||||
|
val appName = context.applicationInfo.loadLabel(context.packageManager).toString()
|
||||||
|
val customController = object : com.bytedance.sdk.openadsdk.TTCustomController() {
|
||||||
|
override fun isCanUseLocation(): Boolean {
|
||||||
|
return privacy?.get("canUseLocation") as? Boolean ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isCanUsePhoneState(): Boolean {
|
||||||
|
return privacy?.get("canUsePhoneState") as? Boolean ?: false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isCanUseWifiState(): Boolean {
|
||||||
|
return privacy?.get("canUseWifiState") as? Boolean ?: true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isCanUseWriteExternal(): Boolean {
|
||||||
|
return privacy?.get("canUseWriteExternal") as? Boolean ?: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TTAdConfig.Builder()
|
||||||
|
.appId(appIdOverride ?: "")
|
||||||
|
.appName(appName)
|
||||||
|
.useMediation(true)
|
||||||
|
.debug(true)
|
||||||
|
.supportMultiProcess(false)
|
||||||
|
.customController(customController)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
org.gradle.caching=true
|
org.gradle.caching=true
|
||||||
|
android.enableJetifier=true
|
||||||
@@ -66,5 +66,14 @@
|
|||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
</array>
|
</array>
|
||||||
|
<!-- 权限用途说明:用于广告相关和媒体访问请求 -->
|
||||||
|
<key>NSLocationWhenInUseUsageDescription</key>
|
||||||
|
<string>用于获取位置信息以优化广告投放与推荐</string>
|
||||||
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
|
<string>用于访问相册以支持上传或展示媒体内容</string>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>用于拍照或视频录制以便上传或广告素材使用</string>
|
||||||
|
<key>NSUserTrackingUsageDescription</key>
|
||||||
|
<string>用于广告个性化与跨应用跟踪(App Tracking Transparency)</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
221
lib/main.dart
221
lib/main.dart
@@ -1,9 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'dart:async';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'providers/expense_provider.dart';
|
import 'providers/expense_provider.dart';
|
||||||
import 'screens/home_screen.dart';
|
import 'screens/home_screen.dart';
|
||||||
|
import 'package:flutter_unionad/flutter_unionad.dart';
|
||||||
void main() {
|
void main() {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,7 +24,222 @@ class MyApp extends StatelessWidget {
|
|||||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
),
|
),
|
||||||
home: const HomeScreen(),
|
home: const SplashPage(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SplashPage extends StatefulWidget {
|
||||||
|
const SplashPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SplashPage> createState() => _SplashPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SplashPageState extends State<SplashPage> {
|
||||||
|
bool _showAd = true;
|
||||||
|
Timer? _adWatchdog;
|
||||||
|
bool? _initSuccess; // null=loading, true=success, false=failure
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
// 延后到首帧绘制后再初始化广告 SDK,避免在 Activity/Context 未就绪时调用导致 NPE
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
_initUnionAd();
|
||||||
|
});
|
||||||
|
// 如果广告或 SDK 未在合理时间内返回,使用回退跳转避免白屏
|
||||||
|
_adWatchdog = Timer(const Duration(seconds: 6), () {
|
||||||
|
if (mounted) _goToHome();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _initUnionAd() async {
|
||||||
|
// 注册并初始化 FlutterUnionad SDK
|
||||||
|
try {
|
||||||
|
await FlutterUnionad.register(
|
||||||
|
// 穿山甲广告 Android appid 必填
|
||||||
|
androidAppId: "5779303",
|
||||||
|
// 穿山甲广告 ios appid 必填
|
||||||
|
iosAppId: "5779303",
|
||||||
|
// appname 必填
|
||||||
|
appName: "晴天记账",
|
||||||
|
// 使用聚合功能(true 使用 GroMore 下的广告位)
|
||||||
|
useMediation: false,
|
||||||
|
// 是否为计费用户
|
||||||
|
paid: false,
|
||||||
|
// 用户画像关键词
|
||||||
|
keywords: "",
|
||||||
|
// 是否允许 SDK 展示通知栏提示
|
||||||
|
allowShowNotify: true,
|
||||||
|
// 是否显示 debug 日志
|
||||||
|
debug: true,
|
||||||
|
// 是否支持多进程
|
||||||
|
supportMultiProcess: false,
|
||||||
|
// 允许直接下载的网络状态集合
|
||||||
|
directDownloadNetworkType: [
|
||||||
|
FlutterUnionadNetCode.NETWORK_STATE_2G,
|
||||||
|
FlutterUnionadNetCode.NETWORK_STATE_3G,
|
||||||
|
FlutterUnionadNetCode.NETWORK_STATE_4G,
|
||||||
|
FlutterUnionadNetCode.NETWORK_STATE_WIFI,
|
||||||
|
],
|
||||||
|
androidPrivacy: AndroidPrivacy(
|
||||||
|
isCanUseLocation: false,
|
||||||
|
lat: 0.0,
|
||||||
|
lon: 0.0,
|
||||||
|
isCanUsePhoneState: false,
|
||||||
|
imei: "",
|
||||||
|
isCanUseWifiState: false,
|
||||||
|
macAddress: "",
|
||||||
|
isCanUseWriteExternal: false,
|
||||||
|
oaid: "b69cd3cf68900323",
|
||||||
|
alist: false,
|
||||||
|
isCanUseAndroidId: false,
|
||||||
|
androidId: "",
|
||||||
|
isCanUsePermissionRecordAudio: false,
|
||||||
|
isLimitPersonalAds: false,
|
||||||
|
isProgrammaticRecommend: false,
|
||||||
|
userPrivacyConfig: {"mcod": "0"},
|
||||||
|
),
|
||||||
|
iosPrivacy: IOSPrivacy(
|
||||||
|
limitPersonalAds: false,
|
||||||
|
limitProgrammaticAds: false,
|
||||||
|
forbiddenCAID: false,
|
||||||
|
),
|
||||||
|
// userInfo: UnionadUserInfo(
|
||||||
|
// userId: "unionad_123",
|
||||||
|
// age: 19,
|
||||||
|
// gender: 2,
|
||||||
|
// channel: "flutter",
|
||||||
|
// subChannel: "flutter_unionad",
|
||||||
|
// userValueGroup: "QQ",
|
||||||
|
// customInfos: {"QQ": "123", "WeChat": "456"},
|
||||||
|
// ),
|
||||||
|
// localConfig: "site_config_5098580",
|
||||||
|
);
|
||||||
|
debugPrint('FlutterUnionad.register: success');
|
||||||
|
if (mounted) setState(() => _initSuccess = true);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('FlutterUnionad.register error: $e');
|
||||||
|
if (mounted) setState(() => _initSuccess = false);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
final sdkVersion = await FlutterUnionad.getSDKVersion();
|
||||||
|
debugPrint('FlutterUnionad SDK Version: $sdkVersion');
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('getSDKVersion error: $e');
|
||||||
|
}
|
||||||
|
|
||||||
|
FlutterUnionad.requestPermissionIfNecessary(
|
||||||
|
callBack: FlutterUnionadPermissionCallBack(
|
||||||
|
notDetermined: () {
|
||||||
|
debugPrint('权限未确定');
|
||||||
|
},
|
||||||
|
restricted: () {
|
||||||
|
debugPrint('权限限制');
|
||||||
|
},
|
||||||
|
denied: () {
|
||||||
|
debugPrint('权限拒绝');
|
||||||
|
},
|
||||||
|
authorized: () {
|
||||||
|
debugPrint('权限同意');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
// 如果注册/初始化在合理时间内仍未完成,标记为失败以便调试
|
||||||
|
Future.delayed(const Duration(seconds: 4), () {
|
||||||
|
if (!mounted) return;
|
||||||
|
if (_initSuccess == null) {
|
||||||
|
debugPrint('FlutterUnionad: init did not finish within 4s, marking as failed');
|
||||||
|
setState(() => _initSuccess = false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _goToHome() {
|
||||||
|
if (!mounted) return;
|
||||||
|
_adWatchdog?.cancel();
|
||||||
|
Navigator.of(context).pushReplacement(
|
||||||
|
MaterialPageRoute(builder: (_) => const HomeScreen()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_adWatchdog?.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Stack(
|
||||||
|
children: [
|
||||||
|
// 后端主页面作为备用
|
||||||
|
const Offstage(offstage: true, child: HomeScreen()),
|
||||||
|
// 开屏广告视图
|
||||||
|
Positioned.fill(
|
||||||
|
child: _showAd
|
||||||
|
? FlutterUnionadSplashAdView(
|
||||||
|
androidCodeId: "102729400",
|
||||||
|
iosCodeId: "102729400",
|
||||||
|
supportDeepLink: true,
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
height: MediaQuery.of(context).size.height,
|
||||||
|
hideSkip: false,
|
||||||
|
timeout: 3000,
|
||||||
|
callBack: FlutterUnionadSplashCallBack(
|
||||||
|
onShow: () {
|
||||||
|
debugPrint('开屏广告显示');
|
||||||
|
setState(() => _showAd = true);
|
||||||
|
},
|
||||||
|
onClick: () {
|
||||||
|
debugPrint('开屏广告点击');
|
||||||
|
},
|
||||||
|
onFail: (error) {
|
||||||
|
debugPrint('开屏广告失败 $error');
|
||||||
|
_goToHome();
|
||||||
|
},
|
||||||
|
onFinish: () {
|
||||||
|
debugPrint('开屏广告倒计时结束');
|
||||||
|
_goToHome();
|
||||||
|
},
|
||||||
|
onSkip: () {
|
||||||
|
debugPrint('开屏广告跳过');
|
||||||
|
_goToHome();
|
||||||
|
},
|
||||||
|
onTimeOut: () {
|
||||||
|
debugPrint('开屏广告超时');
|
||||||
|
_goToHome();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
// Debug: init 状态可视化,方便在真机上观察
|
||||||
|
Positioned(
|
||||||
|
left: 12,
|
||||||
|
right: 12,
|
||||||
|
bottom: 24,
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.black.withOpacity(0.6),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
_initSuccess == null
|
||||||
|
? 'SDK init: loading...'
|
||||||
|
: (_initSuccess == true ? 'SDK init: success' : 'SDK init: failed'),
|
||||||
|
style: const TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,6 +107,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_unionad:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_unionad
|
||||||
|
sha256: "550773ff9ae43c8ba3d818d71cd2177259176d3f6c81b275e6537067de1cfa88"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.3"
|
||||||
intl:
|
intl:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ dependencies:
|
|||||||
intl: ^0.19.0
|
intl: ^0.19.0
|
||||||
fl_chart: ^0.66.0
|
fl_chart: ^0.66.0
|
||||||
uuid: ^4.3.3
|
uuid: ^4.3.3
|
||||||
|
flutter_unionad: ^2.2.3
|
||||||
|
|
||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
|
|||||||
Reference in New Issue
Block a user