调整 广告逻辑
This commit is contained in:
@@ -1,18 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.example.union_ad_ssgf">
|
||||
|
||||
<!-- 需要权限 start -->
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
|
||||
<application>
|
||||
<activity android:name=".page.AdSplashActivity"
|
||||
android:configChanges="keyboard|orientation|screenSize"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/SplashTheme"/>
|
||||
<!-- 文件兼容 -->
|
||||
<provider
|
||||
android:name="com.qq.e.comm.GDTFileProvider"
|
||||
android:authorities="${applicationId}.gdt.fileprovider"
|
||||
@@ -20,8 +17,21 @@
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/gdt_file_path" />
|
||||
android:resource="@xml/gdt_file_path"
|
||||
tools:replace="android:resource"
|
||||
/>
|
||||
</provider>
|
||||
|
||||
<activity
|
||||
android:name="com.qq.e.ads.PortraitADActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||
tools:replace="android:screenOrientation" />
|
||||
<activity
|
||||
android:name="com.qq.e.ads.LandscapeADActivity"
|
||||
android:screenOrientation="landscape"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||
tools:replace="android:screenOrientation" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
</manifest>
|
@@ -0,0 +1,198 @@
|
||||
package com.example.union_ad_ssgf;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.Configuration;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.webkit.WebResourceError;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import com.qq.e.comm.compliance.DownloadConfirmCallBack;
|
||||
|
||||
|
||||
public class DownloadApkConfirmDialog extends Dialog implements View.OnClickListener {
|
||||
private static final String TAG = "ConfirmDialogWebView";
|
||||
private Context context;
|
||||
private int orientation;
|
||||
private DownloadConfirmCallBack callBack;
|
||||
private WebView webView;
|
||||
private ImageView close;
|
||||
private Button confirm;
|
||||
|
||||
private ViewGroup contentHolder;
|
||||
private ProgressBar loadingBar;
|
||||
private Button reloadButton;
|
||||
|
||||
private String url;
|
||||
private boolean urlLoadError = false;
|
||||
|
||||
private static final String RELOAD_TEXT = "重新加载";
|
||||
private static final String LOAD_ERROR_TEXT = "抱歉,应用信息获取失败";
|
||||
|
||||
public DownloadApkConfirmDialog(Context context, String infoUrl,
|
||||
DownloadConfirmCallBack callBack) {
|
||||
super(context, R.style.DownloadConfirmDialogFullScreen);//需要全屏显示,同时显示非窗口蒙版
|
||||
this.context = context;
|
||||
this.callBack = callBack;
|
||||
this.url = infoUrl;
|
||||
orientation = context.getResources().getConfiguration().orientation;
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
setCanceledOnTouchOutside(true);
|
||||
initView();
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
setContentView(R.layout.download_confirm_dialog);
|
||||
View root = findViewById(R.id.download_confirm_root);
|
||||
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
root.setBackgroundResource(R.drawable.download_confirm_background_portrait);
|
||||
} else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
root.setBackgroundResource(R.drawable.download_confirm_background_landscape);
|
||||
}
|
||||
close = findViewById(R.id.download_confirm_close);
|
||||
close.setOnClickListener(this);
|
||||
reloadButton = findViewById(R.id.download_confirm_reload_button);
|
||||
reloadButton.setOnClickListener(this);
|
||||
confirm = findViewById(R.id.download_confirm_confirm);
|
||||
confirm.setOnClickListener(this);
|
||||
loadingBar = findViewById(R.id.download_confirm_progress_bar);
|
||||
contentHolder = findViewById(R.id.download_confirm_content);
|
||||
createTextView();
|
||||
}
|
||||
|
||||
private void createTextView(){
|
||||
FrameLayout layout = findViewById(R.id.download_confirm_holder);
|
||||
webView = new WebView(context);
|
||||
webView.getSettings().setJavaScriptEnabled(true);
|
||||
webView.setWebViewClient(new Client());
|
||||
layout.addView(webView);
|
||||
}
|
||||
@Override
|
||||
public void show() {
|
||||
super.show();
|
||||
try {
|
||||
loadUrl(url);
|
||||
} catch (Exception e) {
|
||||
Log.e(DownloadApkConfirmDialog.TAG, "load error url:" + url, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void loadUrl(String url) {
|
||||
if (TextUtils.isEmpty(url)) {
|
||||
loadingBar.setVisibility(View.GONE);
|
||||
contentHolder.setVisibility(View.GONE);
|
||||
reloadButton.setVisibility(View.VISIBLE);
|
||||
reloadButton.setText(LOAD_ERROR_TEXT);
|
||||
reloadButton.setEnabled(false);
|
||||
return;
|
||||
}
|
||||
urlLoadError = false;
|
||||
Log.d(TAG, "download confirm load url:" + url);
|
||||
webView.loadUrl(url);
|
||||
}
|
||||
|
||||
public void setInstallTip() {
|
||||
confirm.setText("立即安装");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
int height = (int) UIUtils.INSTANCE.getScreenHeight(context);
|
||||
int width =(int) UIUtils.INSTANCE.getScreenWidth(context);
|
||||
Window window = getWindow();
|
||||
window.getDecorView().setPadding(0, 0, 0, 0);
|
||||
WindowManager.LayoutParams layoutParams = window.getAttributes();
|
||||
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
layoutParams.height = (int) (height * 0.6);
|
||||
layoutParams.gravity = Gravity.BOTTOM;
|
||||
layoutParams.windowAnimations = R.style.DownloadConfirmDialogAnimationUp;
|
||||
} else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
layoutParams.width = (int) (width * 0.5);
|
||||
layoutParams.height = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
layoutParams.gravity = Gravity.RIGHT;
|
||||
layoutParams.windowAnimations = R.style.DownloadConfirmDialogAnimationRight;
|
||||
}
|
||||
//弹窗外区域蒙版50%透明度
|
||||
layoutParams.dimAmount = 0.5f;
|
||||
|
||||
//resume后动画会重复,在显示出来后重置无动画
|
||||
window.setAttributes(layoutParams);
|
||||
setOnShowListener(new OnShowListener() {
|
||||
@Override
|
||||
public void onShow(DialogInterface dialog) {
|
||||
try {
|
||||
Window window = getWindow();
|
||||
window.setWindowAnimations(0);
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v == close) {
|
||||
if (callBack != null) {
|
||||
callBack.onCancel();
|
||||
}
|
||||
dismiss();
|
||||
} else if (v == confirm) {
|
||||
if (callBack != null) {
|
||||
callBack.onConfirm();
|
||||
}
|
||||
dismiss();
|
||||
} else if (v == reloadButton) {
|
||||
loadUrl(url);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
super.cancel();
|
||||
if (callBack != null) {
|
||||
callBack.onCancel();
|
||||
}
|
||||
}
|
||||
|
||||
class Client extends WebViewClient {
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url) {
|
||||
super.onPageFinished(view, url);
|
||||
if (!urlLoadError) {
|
||||
loadingBar.setVisibility(View.GONE);
|
||||
reloadButton.setVisibility(View.GONE);
|
||||
contentHolder.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
|
||||
super.onReceivedError(view, request, error);
|
||||
Log.d(TAG, "doConfirmWithInfo onReceivedError:" + error + " " + request);
|
||||
urlLoadError = true;
|
||||
loadingBar.setVisibility(View.GONE);
|
||||
contentHolder.setVisibility(View.GONE);
|
||||
reloadButton.setVisibility(View.VISIBLE);
|
||||
reloadButton.setText(RELOAD_TEXT);
|
||||
reloadButton.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,818 @@
|
||||
package com.example.union_ad_ssgf;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.qq.e.comm.compliance.ApkDownloadComplianceInterface;
|
||||
import com.qq.e.comm.compliance.DownloadConfirmCallBack;
|
||||
import com.qq.e.comm.compliance.DownloadConfirmListener;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* created by timfeng 2021/2/2
|
||||
*/
|
||||
public class DownloadConfirmHelper {
|
||||
public static final String TAG = "DownloadConfirmHelper";
|
||||
public static boolean USE_CUSTOM_DIALOG = false;
|
||||
|
||||
private static final String JSON_INFO_PARAMETER = "&resType=api";
|
||||
|
||||
private static final String JSON_RESULT_KEY = "ret";
|
||||
private static final String JSON_DATA_KEY = "data";
|
||||
|
||||
private static final String ICON_URL_KEY = "iconUrl";//App Icon
|
||||
private static final String APP_NAME_KEY = "appName";//App 名称
|
||||
private static final String VERSION_NAME_KEY = "versionName";//版本号
|
||||
private static final String AUTHOR_NAME_KEY = "authorName";//开发者
|
||||
private static final String PERMISSIONS_KEY = "permissions";//权限列表(具体权限信息,非URL)
|
||||
private static final String PRIVACY_AGREEMENT_KEY = "privacyAgreement";//隐私政策(URL)这个url需要使用外部浏览器或者webview展示
|
||||
private static final String UPDATE_TIME_KEY = "apkPublishTime";//版本更新时间
|
||||
private static final String APK_FILE_SIZE_KEY = "fileSize";//apk文件大小,bytes
|
||||
|
||||
private static boolean USE_RAW_PERMISSIONS = false;
|
||||
private static JSONObject PERMISSIONS_MAP_JSON;
|
||||
|
||||
public static class ApkInfo {
|
||||
public String iconUrl;
|
||||
public String appName;
|
||||
public String versionName;
|
||||
public String authorName;
|
||||
public List<String> permissions;
|
||||
public String privacyAgreementUrl;
|
||||
public long apkPublishTime;
|
||||
public long fileSize;
|
||||
}
|
||||
|
||||
public static final DownloadConfirmListener DOWNLOAD_CONFIRM_LISTENER =
|
||||
new DownloadConfirmListener() {
|
||||
|
||||
@Override
|
||||
public void onDownloadConfirm(Activity context, int scenes, String infoUrl,
|
||||
DownloadConfirmCallBack callBack) {
|
||||
Log.d(TAG, "scenes:" + scenes + " info url:" + infoUrl);
|
||||
|
||||
//获取对应的json数据并自定义显示
|
||||
// DownloadApkConfirmDialog dialog = new DownloadApkConfirmDialog(context, getApkJsonInfoUrl(infoUrl), callBack);
|
||||
DownloadApkConfirmDialog dialog = new DownloadApkConfirmDialog(context, infoUrl, callBack);//使用webview显示
|
||||
if((scenes & ApkDownloadComplianceInterface.INSTALL_BITS) != 0){
|
||||
dialog.setInstallTip();
|
||||
scenes &= ~ApkDownloadComplianceInterface.INSTALL_BITS;
|
||||
Log.d(TAG, "real scenes:" + scenes );
|
||||
}
|
||||
dialog.show();
|
||||
}
|
||||
};
|
||||
|
||||
public static String getApkJsonInfoUrl(String infoUrl) {
|
||||
return infoUrl + JSON_INFO_PARAMETER;
|
||||
}
|
||||
|
||||
public static ApkInfo getAppInfoFromJson(String jsonStr) {
|
||||
ApkInfo result = null;
|
||||
|
||||
if (TextUtils.isEmpty(jsonStr)) {
|
||||
Log.d(TAG, "请求应用信息返回值为空");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JSONObject json = new JSONObject(jsonStr);
|
||||
int retCode = json.optInt(JSON_RESULT_KEY, -1);
|
||||
if (retCode != 0) {
|
||||
Log.d(TAG, "请求应用信息返回值错误");
|
||||
return null;
|
||||
}
|
||||
JSONObject dataJson = json.optJSONObject(JSON_DATA_KEY);
|
||||
if (dataJson == null) {
|
||||
Log.d(TAG, "请求应用信息返回值错误" + JSON_DATA_KEY);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (dataJson != null) {
|
||||
result = new ApkInfo();
|
||||
result.iconUrl = dataJson.optString(ICON_URL_KEY);
|
||||
result.appName = dataJson.optString(APP_NAME_KEY);
|
||||
result.versionName = dataJson.optString(VERSION_NAME_KEY);
|
||||
result.authorName = dataJson.optString(AUTHOR_NAME_KEY);
|
||||
//这里获得是原始的权限数组,android.permission.ACCESS_NETWORK_STATE这种
|
||||
JSONArray jsonPermissions = dataJson.optJSONArray(PERMISSIONS_KEY);
|
||||
if (jsonPermissions != null) {
|
||||
result.permissions = new ArrayList<>();
|
||||
for (int i = 0; i < jsonPermissions.length(); i++) {
|
||||
String rawPermission = jsonPermissions.getString(i);
|
||||
if (USE_RAW_PERMISSIONS) {
|
||||
result.permissions.add(jsonPermissions.getString(i));
|
||||
continue;
|
||||
}
|
||||
//字面权限,不会显示所有,只会显示一部分常用权限
|
||||
JSONObject permissionContent = PERMISSIONS_MAP_JSON.optJSONObject(rawPermission);
|
||||
if (permissionContent != null) {
|
||||
String permissionTitle = permissionContent.optString("title");
|
||||
if (!TextUtils.isEmpty(permissionTitle)) {
|
||||
result.permissions.add(permissionTitle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result.privacyAgreementUrl = dataJson.optString(PRIVACY_AGREEMENT_KEY);
|
||||
//后台返回的时间可能是秒也可能是毫秒,这里需要统一下为毫秒
|
||||
//2000年1月1日1时0分0秒对应的 秒是946688401 毫秒是 946688401000
|
||||
long publicTime = dataJson.optLong(UPDATE_TIME_KEY);
|
||||
result.apkPublishTime = publicTime > 946688401000L ? publicTime : publicTime * 1000;
|
||||
result.fileSize = dataJson.optLong(APK_FILE_SIZE_KEY);//单位是字节
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. 权限对照表作为开发者参考使用
|
||||
* 2. level = 1,代表敏感权限,0,代表普通权限,开发者可以按需使用
|
||||
* 3. 权限未穷举部分,需要开发者自行翻译
|
||||
*/
|
||||
|
||||
public static final String PERMISSIONS_MAP_JSON_STR = "{\n" +
|
||||
"\"android.permission.ACCESS_CHECKIN_PROPERTIES\": {\n" +
|
||||
"\"desc\": \"允许应用对登记服务上传的属性拥有读/写权限。普通应用不应使用此权限。\",\n" +
|
||||
"\"title\": \"访问检入属性\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.ACCESS_COARSE_LOCATION\": {\n" +
|
||||
"\"desc\": \"允许该应用获取您的大致位置信息,这类位源信息来自于使用网络位置信息源(例如基站和WLAN)" +
|
||||
"的位置信息服务。您必须在设备上开启这些位置信息服务,应用才能获得位置信息。应用会使用此类服务确定您的大概位置。\",\n" +
|
||||
"\"title\": \"大致位置(基于网络)\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.ACCESS_LOCATION_EXTRA_COMMANDS\": {\n" +
|
||||
"\"desc\": \"允许应用程序访问其他的位置信息提供程序命令。此权限使该应用可以干扰GPS或其他位置信息源的运作。\",\n" +
|
||||
"\"title\": \"获取额外的位置信息提供程序命令。\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.ACCESS_NETWORK_STATE\": {\n" +
|
||||
"\"desc\": \"允许该应用查看网络连接的相关信息,例如存在和连接的网络。\",\n" +
|
||||
"\"title\": \"查看网络连接。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.ACCESS_NOTIFICATION_POLICY\": {\n" +
|
||||
"\"desc\": \"允许访问通知策略的应用程序的标记权限。\",\n" +
|
||||
"\"title\": \"访问通知策略\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.ACCESS_WIFI_STATE\": {\n" +
|
||||
"\"desc\": \"允许该应用查看WLAN网络的相关信息。例如是否启用了WLAN以及连接的WLAN设备的名称。\",\n" +
|
||||
"\"title\": \"查看WLAN连接\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.ACCOUNT_MANAGER\": {\n" +
|
||||
"\"desc\": \"允许应用使用AccountManager的帐户身份验证程序功能,包括创建帐户以及获取和设置复密码。\",\n" +
|
||||
"\"title\": \"创建帐户并设置密码\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.ACTIVITY_RECOGNITION\": {\n" +
|
||||
"\"desc\": \"允许应用程序识别身体活动。\",\n" +
|
||||
"\"title\": \"识别身体活动\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.ANSWER_PHONE_CALLS\": {\n" +
|
||||
"\"desc\": \"允许该应用接听来电。\",\n" +
|
||||
"\"title\": \"接听来电\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.BATTERY_STATS\": {\n" +
|
||||
"\"desc\": \"允许应用读取当前电量使用情况的基础数据。此权限可让应用了解关于您使用了哪些应用的详细信息。\",\n" +
|
||||
"\"title\": \"读取电池使用统计信息。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.BIND_CARRIER_SERVICES\": {\n" +
|
||||
"\"desc\": \"允许应用告知系统哪些小部件可提供哪个应用使用。拥有此权限的应用可向其他应用授予对个人数据的访问权限。普通应用不应使用此权限。\",\n" +
|
||||
"\"title\": \"选择小部件\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.BIND_CONTROLS\": {\n" +
|
||||
"\"desc\": \"允许SystemUI请求第三方控件。\",\n" +
|
||||
"\"title\": \"请求第三方控件\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.BIND_QUICK_SETTINGS_TILE\": {\n" +
|
||||
"\"desc\": \"允许应用程序绑定到第三方快速设置磁贴。\",\n" +
|
||||
"\"title\": \"绑定快速设置磁贴\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.BLUETOOTH\": {\n" +
|
||||
"\"desc\": \"允许该应用查看手机上的蓝牙配置,以及建立和接受与配对设备的连接。\",\n" +
|
||||
"\"title\": \"与蓝牙设备配对。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.BLUETOOTH_ADMIN\": {\n" +
|
||||
"\"desc\": \"允许应用配置本地蓝牙手机,以及发现远程设备并进行配对。\",\n" +
|
||||
"\"title\": \"访问蓝牙设置。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.BLUETOOTH_PRIVILEGED\": {\n" +
|
||||
"\"desc\": \"允许应用程序与蓝牙设备配对,而无需用户交互。\",\n" +
|
||||
"\"title\": \"运行应用进行蓝牙配对。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.BODY_SENSORS\": {\n" +
|
||||
"\"desc\": \"允许应用访问您用于测量身体状况(如心跳速率)的传感器中的数据。\",\n" +
|
||||
"\"title\": \"人体传感器\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.BROADCAST_PACKAGE_REMOVED\": {\n" +
|
||||
"\"desc\": \"允许应用程序广播已删除应用程序的通知。\",\n" +
|
||||
"\"title\": \"发送软件包被移除的广播通知。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.BROADCAST_SMS\": {\n" +
|
||||
"\"desc\": \"允许应用广播一条有关已收到短信的通知。恶意应用可能借此伪造接到的短信。\",\n" +
|
||||
"\"title\": \"发送短信收到的广播\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.BROADCAST_STICKY\": {\n" +
|
||||
"\"desc\": \"允许应用程序广播粘性意图。\",\n" +
|
||||
"\"title\": \"发送持久广播。\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.BROADCAST_WAP_PUSH\": {\n" +
|
||||
"\"desc\": \"允许应用程序广播WAP PUSH接收通知。\",\n" +
|
||||
"\"title\": \"发送WAP-PUSH 收到的广播。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.CALL_COMPANION_APP\": {\n" +
|
||||
"\"desc\": \"允许应用作为呼叫伴随应用启用。\",\n" +
|
||||
"\"title\": \"伴随呼叫启动\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.CALL_PHONE\": {\n" +
|
||||
"\"desc\": \"允许应用程序在不通过拨号界面的情况下发起电话呼叫。\",\n" +
|
||||
"\"title\": \"直接拨打电话号码\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.CALL_PRIVILEGED\": {\n" +
|
||||
"\"desc\": \"允许应用程序拨打任何电话号码,包括紧急号码,而无需通过拨号界面来让用户确认正在拨打的电话。\",\n" +
|
||||
"\"title\": \"直接呼叫任何电话号码\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.CAMERA\": {\n" +
|
||||
"\"desc\": \"允许该应用使用相机拍摄照片和视频。此权限可让该应用随时使用相机,而无需您的确认。\",\n" +
|
||||
"\"title\": \"拍摄照片和视频\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.CAPTURE_AUDIO_OUTPUT\": {\n" +
|
||||
"\"desc\": \"允许该应用捕获和重定向音频输出。\",\n" +
|
||||
"\"title\": \"捕获音频输出。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.CHANGE_COMPONENT_ENABLED_STATE\": {\n" +
|
||||
"\"desc\": \"允许应用启用或停用其他应用的组件。\",\n" +
|
||||
"\"title\": \"启用或停用应用组件\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.CHANGE_CONFIGURATION\": {\n" +
|
||||
"\"desc\": \"允许应用程序修改当前设备配置。\",\n" +
|
||||
"\"title\": \"更改系统显示设置。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.CHANGE_NETWORK_STATE\": {\n" +
|
||||
"\"desc\": \"允许应用程序更改网络连接状态。\",\n" +
|
||||
"\"title\": \"更改网络连接状态。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.CHANGE_WIFI_MULTICAST_STATE\": {\n" +
|
||||
"\"desc\": \"允许该应用使用多播地址接收发送到WLAN网络上所有设备的数据包。该操作的耗电量比非多播模式更大。\",\n" +
|
||||
"\"title\": \"允许接收WLAN多播。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.CHANGE_WIFI_STATE\": {\n" +
|
||||
"\"desc\": \"允许该应用与 WLAN 接入点建立和断开连接,以及更改WLAN网络的设备配置。\",\n" +
|
||||
"\"title\": \"连接WLAN网络和断开连接。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.CLEAR_APP_CACHE\": {\n" +
|
||||
"\"desc\": \"允许应用程序清除设备上所有已安装应用程序的缓存。\",\n" +
|
||||
"\"title\": \"删除所有应用缓存数据\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.CONTROL_LOCATION_UPDATES\": {\n" +
|
||||
"\"desc\": \"允许应用启用/停止来自无线装置的位置更新通知。普通应用应该使用此权限。\",\n" +
|
||||
"\"title\": \"控制位置更新通知。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.DELETE_CACHE_FILES\": {\n" +
|
||||
"\"desc\": \"允许删除应用程序的缓存文件。\",\n" +
|
||||
"\"title\": \"删除其他应用程序的缓存文件。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.DELETE_PACKAGES\": {\n" +
|
||||
"\"desc\": \"允许应用程序删除其他应用。\",\n" +
|
||||
"\"title\": \"删除应用\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.DIAGNOSTIC\": {\n" +
|
||||
"\"desc\": \"允许应用读取/写入诊断拥有的所有资源(例如/dev中的文件)。这可能会影响系统的稳定性和安全性。\",\n" +
|
||||
"\"title\": \"允许应用程序读写诊断资源。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.DISABLE_KEYGUARD\": {\n" +
|
||||
"\"desc\": \"允许该应用停用键锁以及任何关联的密码安全措施。例如,让手机在接听来电时停用键锁,在通话结束后重新启用键。\",\n" +
|
||||
"\"title\": \"停用屏幕锁定\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.DUMP\": {\n" +
|
||||
"\"desc\": \"允许应用程序从系统服务中检索状态转储信息。\",\n" +
|
||||
"\"title\": \"检索系统内部状态\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.EXPAND_STATUS_BAR\": {\n" +
|
||||
"\"desc\": \"允许应用程序展开或折叠状态栏。\",\n" +
|
||||
"\"title\": \"展开/收拢状态栏\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.FACTORY_TEST\": {\n" +
|
||||
"\"desc\": \"允许应用以制造商测试应用程序的身份运行,以root用户身份运行。\",\n" +
|
||||
"\"title\": \"在出厂测试模式下运行\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.FOREGROUND_SERVICE\": {\n" +
|
||||
"\"desc\": \"允许应用程序使用前台服务。\",\n" +
|
||||
"\"title\": \"使用前台服务\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.GET_ACCOUNTS\": {\n" +
|
||||
"\"desc\": \"允许访问帐户服务中的帐户列表。\",\n" +
|
||||
"\"title\": \"查找设备上的账户\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.GET_ACCOUNTS_PRIVILEGED\": {\n" +
|
||||
"\"desc\": \"允许访问帐户服务中的帐户列表。\",\n" +
|
||||
"\"title\": \"访问账户列表\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.GET_PACKAGE_SIZE\": {\n" +
|
||||
"\"desc\": \"允许应用程序查看其他应用使用的空间大小。\",\n" +
|
||||
"\"title\": \"计算应用存储空间\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.GET_TASKS\": {\n" +
|
||||
"\"desc\": \"允许应用程序获取当前的信息或最近运行的任务。\",\n" +
|
||||
"\"title\": \"检索正在运行的应用。\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.INSTALL_LOCATION_PROVIDER\": {\n" +
|
||||
"\"desc\": \"创建用于测试的模拟位置源或安装新的位置提供程序。\",\n" +
|
||||
"\"title\": \"允许安装位置信息提供程序\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.INSTALL_PACKAGES\": {\n" +
|
||||
"\"desc\": \"允许应用程序安装其他应用。\",\n" +
|
||||
"\"title\": \"直接安装应用\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.INSTANT_APP_FOREGROUND_SERVICE\": {\n" +
|
||||
"\"desc\": \"允许即时应用创建前台服务。\",\n" +
|
||||
"\"title\": \"创建前台服务\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.INTERNET\": {\n" +
|
||||
"\"desc\": \"允许该应用创建网络套接字和使用自定义网络协议。浏览器和其他某些应用提供了向互联网发送数据的途径,因此应用无需该权限即可向互联网发送数据。\",\n" +
|
||||
"\"title\": \"完全的网络访问权限\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.KILL_BACKGROUND_PROCESSES\": {\n" +
|
||||
"\"desc\": \"允许应用清理后台应用进程。\",\n" +
|
||||
"\"title\": \"关闭其他应用\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.LOADER_USAGE_STATS\": {\n" +
|
||||
"\"desc\": \"允许数据加载器读取程序包的访问日志。\",\n" +
|
||||
"\"title\": \"允许读取程序包日志\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.MANAGE_DOCUMENTS\": {\n" +
|
||||
"\"desc\": \"允许应用程序管理档存储空间。\",\n" +
|
||||
"\"title\": \"管理文档存储空间\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.MANAGE_EXTERNAL_STORAGE\": {\n" +
|
||||
"\"desc\": \"允许应用程序访问作用域存储中的外部存储。\",\n" +
|
||||
"\"title\": \"允许程序访问APP通知方式\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.MEDIA_CONTENT_CONTROL\": {\n" +
|
||||
"\"desc\": \"允许应用控制媒体播放及使用媒体信息(标题、作者..)。\",\n" +
|
||||
"\"title\": \"控制媒体播放和使用元数据。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.MODIFY_AUDIO_SETTINGS\": {\n" +
|
||||
"\"desc\": \"允许该应用修改全局音频设置,例如音量和用于输出的扬声器。\",\n" +
|
||||
"\"title\": \"更改您的音频设置\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.MODIFY_PHONE_STATE\": {\n" +
|
||||
"\"desc\": \"允许应用控制设备的电话功能。拥有此权限的应用可在不通知您的情况下执行切换网络、开关手机无线装置等此类操。\",\n" +
|
||||
"\"title\": \"修改手机状态\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.MOUNT_FORMAT_FILESYSTEMS\": {\n" +
|
||||
"\"desc\": \"允许格式化可移动存储设备。\",\n" +
|
||||
"\"title\": \"清除USB存储设备内容\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\": {\n" +
|
||||
"\"desc\": \"允许挂载和卸载可移动存储的文件系统。\",\n" +
|
||||
"\"title\": \"访问USB存储设备的文件系统\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.NFC\": {\n" +
|
||||
"\"desc\": \"允许应用与近距离无线通信(NFC)标签、卡和读取器通信。\",\n" +
|
||||
"\"title\": \"控制近距离通信\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.PACKAGE_USAGE_STATS\": {\n" +
|
||||
"\"desc\": \"允许应用收集组件使用情况统计信息。\",\n" +
|
||||
"\"title\": \"更新组件使用情况统计\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.PERSISTENT_ACTIVITY\": {\n" +
|
||||
"\"desc\": \"允许应用程序在内存中持续保留其自身的某些组件。\",\n" +
|
||||
"\"title\": \"让应用始终运行\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.PROCESS_OUTGOING_CALLS\": {\n" +
|
||||
"\"desc\": \"允许应用在拨出电话时查看拨打的电话号码,并选择改为拨打其他号码或完全中止通话。\",\n" +
|
||||
"\"title\": \"重新设置外拨电话的路径\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.QUERY_ALL_PACKAGES\": {\n" +
|
||||
"\"desc\": \"允许查询设备上的任何应用程序。\",\n" +
|
||||
"\"title\": \"查看设备上所有应用\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.READ_CALENDAR\": {\n" +
|
||||
"\"desc\": \"允许应用读取用户的日历数据。\",\n" +
|
||||
"\"title\": \"读取日历活动信息\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.READ_CALL_LOG\": {\n" +
|
||||
"\"desc\": \"允许应用程序读取用户的通话记录。\",\n" +
|
||||
"\"title\": \"读取通话记录\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.READ_CONTACTS\": {\n" +
|
||||
"\"desc\": \"允许该应用读取您手机上存储的联系人的相关数据,包括您通过打电话、发送电子邮件或以其他方式与特定个人通信的频率。\",\n" +
|
||||
"\"title\": \"读取您的通讯录\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.READ_EXTERNAL_STORAGE\": {\n" +
|
||||
"\"desc\": \"允许应用程序从外部存储读取数据。\",\n" +
|
||||
"\"title\": \"读取您的USB存储设备中的内容\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.READ_LOGS\": {\n" +
|
||||
"\"desc\": \"允许应用程序读取系统日志文件。\",\n" +
|
||||
"\"title\": \"查阅敏感日志数据\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.READ_PHONE_NUMBERS\": {\n" +
|
||||
"\"desc\": \"允许读取访问设备的电话号码。\",\n" +
|
||||
"\"title\": \"读取手机电话号码\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.READ_PHONE_STATE\": {\n" +
|
||||
"\"desc\": \"允许该应用访问设备的电话功能,此权限可以让应用确定本机号码和设备ID、是否正处于通话状态以及拨打的号码。\",\n" +
|
||||
"\"title\": \"读取手机状态和身份\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.READ_PRECISE_PHONE_STATE\": {\n" +
|
||||
"\"desc\": \"允许访问精确的电话状态。\",\n" +
|
||||
"\"title\": \"读取确切的手机状态\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.READ_SMS\": {\n" +
|
||||
"\"desc\": \"允许该应用读取您手机或SIM卡上存储的短信。\",\n" +
|
||||
"\"title\": \"读取您的讯息(短信或彩信)\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.READ_SYNC_SETTINGS\": {\n" +
|
||||
"\"desc\": \"许该应用读取某个账户的同步设置。\",\n" +
|
||||
"\"title\": \"读取同步设置。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.READ_SYNC_STATS\": {\n" +
|
||||
"\"desc\": \"允许该应用读取某个账户的同步统计信息,包括同步活动历史记录和同步数据量。\",\n" +
|
||||
"\"title\": \"读取同步统计信息。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.REBOOT\": {\n" +
|
||||
"\"desc\": \"允许应用强行重新启动设备。\",\n" +
|
||||
"\"title\": \"强行重新启动手机。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.RECEIVE_BOOT_COMPLETED\": {\n" +
|
||||
"\"desc\": \"允许应用在系统完成引导后立即自动启动。\",\n" +
|
||||
"\"title\": \"开机启动\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.RECEIVE_MMS\": {\n" +
|
||||
"\"desc\": \"允许应用程序监听彩信消息。\",\n" +
|
||||
"\"title\": \"接收讯息(彩信)\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.RECEIVE_SMS\": {\n" +
|
||||
"\"desc\": \"允许应用程序接收短信消息。\",\n" +
|
||||
"\"title\": \"接收讯息(短信)\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.RECEIVE_WAP_PUSH\": {\n" +
|
||||
"\"desc\": \"允许应用程序接收WAP推送消息。\",\n" +
|
||||
"\"title\": \"接收讯息(WAP)\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.RECORD_AUDIO\": {\n" +
|
||||
"\"desc\": \"允许应用程序录制音频。\",\n" +
|
||||
"\"title\": \"录音\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.REORDER_TASKS\": {\n" +
|
||||
"\"desc\": \"允允许该应用将任务移动到前台和后台。\",\n" +
|
||||
"\"title\": \"对正在运行的应用重新排序\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND\": {\n" +
|
||||
"\"desc\": \"允许合作应用在后台运行。\",\n" +
|
||||
"\"title\": \"合作应用后台运行\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND\": {\n" +
|
||||
"\"desc\": \"允许合作应用在后台使用数据。\",\n" +
|
||||
"\"title\": \"合作应用后台使用数据\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.REQUEST_DELETE_PACKAGES\": {\n" +
|
||||
"\"desc\": \"允许应用程序请求卸载其他应用。\",\n" +
|
||||
"\"title\": \"卸载其他应用\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS\": {\n" +
|
||||
"\"desc\": \"允许用户请求应用忽略电池优化。\",\n" +
|
||||
"\"title\": \"忽略电池优化\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.REQUEST_INSTALL_PACKAGES\": {\n" +
|
||||
"\"desc\": \"允许应用程序请求安装软件包。\",\n" +
|
||||
"\"title\": \"安装软件\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.REQUEST_PASSWORD_COMPLEXITY\": {\n" +
|
||||
"\"desc\": \"允许应用程序请求屏幕锁定复杂性,并提示用户将屏幕锁定更新到特定的复杂性级别。\",\n" +
|
||||
"\"title\": \"获取屏幕锁定复杂性\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.RESTART_PACKAGES\": {\n" +
|
||||
"\"desc\": \"允许应用程序结束其他应用的后台进程。\",\n" +
|
||||
"\"title\": \"关闭其他应用\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.SEND_RESPOND_VIA_MESSAGE\": {\n" +
|
||||
"\"desc\": \"允许应用程序将请求发送到其他应用程序,以便处理来电的“通过信息回复”事件。\",\n" +
|
||||
"\"title\": \"发送“通过信息回复”事件\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.SEND_SMS\": {\n" +
|
||||
"\"desc\": \"允许应用程序发送短信消息。\",\n" +
|
||||
"\"title\": \"发送短信\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.SET_ANIMATION_SCALE\": {\n" +
|
||||
"\"desc\": \"允许修改全局动画缩放比例。\",\n" +
|
||||
"\"title\": \"修改全局动画\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.SET_DEBUG_APP\": {\n" +
|
||||
"\"desc\": \"允许该应用对其他应用程序进行调试。\",\n" +
|
||||
"\"title\": \"启动应用调试\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.SET_PREFERRED_APPLICATIONS\": {\n" +
|
||||
"\"desc\": \"允许程序修改您的首选应用。\",\n" +
|
||||
"\"title\": \"设置首选应用。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.SET_PROCESS_LIMIT\": {\n" +
|
||||
"\"desc\": \"允许应用程序设置可以运行的应用程序进程的最大数量。\",\n" +
|
||||
"\"title\": \"限制运行的进程数量。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.SET_TIME\": {\n" +
|
||||
"\"desc\": \"允许应用程序直接设置系统时间。\",\n" +
|
||||
"\"title\": \"设置时间。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.SET_TIME_ZONE\": {\n" +
|
||||
"\"desc\": \"允许应用程序直接设置系统时区。\",\n" +
|
||||
"\"title\": \"设置时区\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.SET_WALLPAPER\": {\n" +
|
||||
"\"desc\": \"允许应用程序设置墙纸。\",\n" +
|
||||
"\"title\": \"设置壁纸\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.SET_WALLPAPER_HINTS\": {\n" +
|
||||
"\"desc\": \" 允许应用程序设置墙纸大小的提示。\",\n" +
|
||||
"\"title\": \"调整您的壁纸大小\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.SIGNAL_PERSISTENT_PROCESSES\": {\n" +
|
||||
"\"desc\": \"允许应用程序请求将信号发送到所有持久性进程。\",\n" +
|
||||
"\"title\": \"向应用发送 Linux 信号\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.SMS_FINANCIAL_TRANSACTIONS\": {\n" +
|
||||
"\"desc\": \"允许金融应用读取过滤的短信。\",\n" +
|
||||
"\"title\": \"读取过滤短信\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.START_VIEW_PERMISSION_USAGE\": {\n" +
|
||||
"\"desc\": \"允许启动应用程序的权限使用屏幕。\",\n" +
|
||||
"\"title\": \"使用手机屏幕\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.STATUS_BAR\": {\n" +
|
||||
"\"desc\": \"允许应用程序打开,关闭或禁用状态栏及其图标。\",\n" +
|
||||
"\"title\": \"停用或修改状态栏\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.SYSTEM_ALERT_WINDOW\": {\n" +
|
||||
"\"desc\": \"允许应用显示在所有其他应用的顶部。\",\n" +
|
||||
"\"title\": \"在其他应用之上显示内容\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.TRANSMIT_IR\": {\n" +
|
||||
"\"desc\": \"允许使用设备的红外发射器。\",\n" +
|
||||
"\"title\": \"使用红外线发射器\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.UPDATE_DEVICE_STATS\": {\n" +
|
||||
"\"desc\": \"允许应用程序更新设备统计信息。\",\n" +
|
||||
"\"title\": \"修改使用统计信息。\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.USE_BIOMETRIC\": {\n" +
|
||||
"\"desc\": \"允许应用使用设备支持的生物特征识别方式。\",\n" +
|
||||
"\"title\": \"允许使用指纹等识别方式\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.USE_FINGERPRINT\": {\n" +
|
||||
"\"desc\": \"允许应用使用指纹硬件。\",\n" +
|
||||
"\"title\": \"允许应用使用指纹硬件\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.USE_FULL_SCREEN_INTENT\": {\n" +
|
||||
"\"desc\": \"允许应用显示全屏通知。\",\n" +
|
||||
"\"title\": \"允许应用显示全屏通知\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.USE_SIP\": {\n" +
|
||||
"\"desc\": \"允许应用程序使用SIP服务。\",\n" +
|
||||
"\"title\": \"拨打/接听SIP电话\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.VIBRATE\": {\n" +
|
||||
"\"desc\": \"允许使用振动器。\",\n" +
|
||||
"\"title\": \"控制振动\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.WAKE_LOCK\": {\n" +
|
||||
"\"desc\": \"允许应用阻止手机进入休眠状态。\",\n" +
|
||||
"\"title\": \"防止手机休眠\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.WRITE_APN_SETTINGS\": {\n" +
|
||||
"\"desc\": \"允许应用更改网络设置,并拦截和检查所有网络流量。\",\n" +
|
||||
"\"title\": \"更改/拦截网络设置和流量\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.WRITE_CALENDAR\": {\n" +
|
||||
"\"desc\": \"允许应用程序写入、删除、更改用户的日历数据。\",\n" +
|
||||
"\"title\": \"添加或修改日历活动,并在所有者不知情的情况下向邀请对象发送电子邮件\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.WRITE_CALL_LOG\": {\n" +
|
||||
"\"desc\": \"允许应用程序添加/修改用户的通话记录。\",\n" +
|
||||
"\"title\": \"写入通话记录\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.WRITE_CONTACTS\": {\n" +
|
||||
"\"desc\": \"允许应用程序写入用户的联系人数据。\",\n" +
|
||||
"\"title\": \"修改您的通讯录\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.WRITE_EXTERNAL_STORAGE\": {\n" +
|
||||
"\"desc\": \"允许应用程序读取/写入外部存储。\",\n" +
|
||||
"\"title\": \"修改或删除您的USB存储设备中的内容\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.WRITE_SECURE_SETTINGS\": {\n" +
|
||||
"\"desc\": \"允许应用读取或写入安全系统设置。\",\n" +
|
||||
"\"title\": \"修改安全系统设置\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.WRITE_SETTINGS\": {\n" +
|
||||
"\"desc\": \"允许应用读取或写入系统设置。\",\n" +
|
||||
"\"title\": \"修改系统设置\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.WRITE_SYNC_SETTINGS\": {\n" +
|
||||
"\"desc\": \"允许应用程序修改某个账户的同步设置。\",\n" +
|
||||
"\"title\": \"启用和停用同步\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.WRITE_VOICEMAIL\": {\n" +
|
||||
"\"desc\": \"允许应用程序修改和删除系统中现有的语音邮件。\",\n" +
|
||||
"\"title\": \"读取修改语音邮件\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.DOWNLOAD_WITHOUT_NOTIFICATION\": {\n" +
|
||||
"\"desc\": \"允许该应用通过下载管理器下载文件,而不会向用户显示任何通知。\",\n" +
|
||||
"\"title\": \"直接下载文件而不显示通知\",\n" +
|
||||
"\"level\": 1\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.ACCESS_ALL_EXTERNAL_STORAGE\": {\n" +
|
||||
"\"desc\": \"允许该应用访问所有用户的外部存储。\",\n" +
|
||||
"\"title\": \"访问所有用户的外部存储设备\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.ASEC_ACCESS\": {\n" +
|
||||
"\"desc\": \"允许该应用获取有关内部存储的信息。\",\n" +
|
||||
"\"title\": \"获取有关内部存储设备的信息\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.ACCESS_MOCK_LOCATION\": {\n" +
|
||||
"\"desc\": \"允许模拟位置源进行测试。\",\n" +
|
||||
"\"title\": \"停用模拟地点来源进行测试\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"android.permission.AUTHENTICATE_ACCOUNTS\": {\n" +
|
||||
"\"desc\": \"允许创建帐户并设置密码。\",\n" +
|
||||
"\"title\": \"创建账户并设置密码\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"com.android.alarm.permission.SET_ALARM\": {\n" +
|
||||
"\"desc\": \"允许应用程序为用户设置闹钟。\",\n" +
|
||||
"\"title\": \"设置闹钟\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"com.android.voicemail.permission.READ_VOICEMAIL\": {\n" +
|
||||
"\"desc\": \"允许应用程序读取系统中的语音邮件。\",\n" +
|
||||
"\"title\": \"读取语言邮件\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"com.android.launcher.permission.INSTALL_SHORTCUT\": {\n" +
|
||||
"\"desc\": \"允许应用程序在桌面中创建快捷方式。\",\n" +
|
||||
"\"title\": \"安装快捷方式\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"com.android.launcher.permission.UNINSTALL_SHORTCUT\": {\n" +
|
||||
"\"desc\": \"允许删除快捷方式。\",\n" +
|
||||
"\"title\": \"卸载快捷方式\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"com.android.voicemail.permission.ADD_VOICEMAIL\": {\n" +
|
||||
"\"desc\": \"允许该应用向您的语音信箱中添加邮件。\",\n" +
|
||||
"\"title\": \"添加语音邮件\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"com.android.email.permission.READ_ATTACHMENT\": {\n" +
|
||||
"\"desc\": \"允许该应用读取您的电子邮件附件。\",\n" +
|
||||
"\"title\": \"读取电子邮件附件\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"com.android.browser.permission.WRITE_HISTORY_BOOKMARKS\": {\n" +
|
||||
"\"desc\": \"允许该应用修改浏览器的历史记录或手机上存储的书签。\",\n" +
|
||||
"\"title\": \"写入网络书签和历史记录\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"},\n" +
|
||||
"\"com.android.browser.permission.READ_HISTORY_BOOKMARKS\": {\n" +
|
||||
"\"desc\": \"允许该应用读取浏览器访问过的所有URL的历史记录,以及浏览器的所有书签的历史记录。\",\n" +
|
||||
"\"title\": \"读取您的网络书签和历史记录\",\n" +
|
||||
"\"level\": 0\n" +
|
||||
"}\n" +
|
||||
"}";
|
||||
|
||||
static {
|
||||
try {
|
||||
PERMISSIONS_MAP_JSON = new JSONObject(PERMISSIONS_MAP_JSON_STR);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
136
android/src/main/kotlin/com/example/union_ad_ssgf/LogUtil.kt
Normal file
136
android/src/main/kotlin/com/example/union_ad_ssgf/LogUtil.kt
Normal file
@@ -0,0 +1,136 @@
|
||||
package com.example.union_ad_ssgf;
|
||||
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
|
||||
|
||||
/**
|
||||
* @Description: 打印工具类
|
||||
* 可自动把调用位置所在的类名和方法名作为tag,并可设置打印级别Ï
|
||||
*/
|
||||
object LogUtil {
|
||||
// 以下为打印级别,级别从低到高
|
||||
const val LOG_LEVEL_VERBOSE = 1
|
||||
const val LOG_LEVEL_DEBUG = 2
|
||||
const val LOG_LEVEL_INFO = 3
|
||||
const val LOG_LEVEL_WARN = 4
|
||||
const val LOG_LEVEL_ERROR = 5
|
||||
const val LOG_LEVEL_NOLOG = 6
|
||||
private var AppName = ""
|
||||
private var PrintLine = false
|
||||
private var LogLevel = LOG_LEVEL_VERBOSE
|
||||
private var isShow = false
|
||||
|
||||
/**
|
||||
* 可在打印的TAG前添加应用名标识,不设置则不输出
|
||||
*/
|
||||
fun setAppName(appName: String) {
|
||||
AppName = appName
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否输出打印所在的行数,默认不输出
|
||||
*/
|
||||
fun setPrintLine(enable: Boolean) {
|
||||
PrintLine = enable
|
||||
}
|
||||
|
||||
/**
|
||||
* 可在打印的TAG前添加应用名标识,不设置则不输出
|
||||
*/
|
||||
fun setShow(show: Boolean) {
|
||||
isShow = show
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置打印级别,且只有等于或高于该级别的打印才会输出
|
||||
*/
|
||||
fun setLogLevel(logLevel: Int) {
|
||||
LogLevel = logLevel
|
||||
}
|
||||
|
||||
fun v() {
|
||||
log(LOG_LEVEL_VERBOSE, "")
|
||||
}
|
||||
|
||||
fun d() {
|
||||
log(LOG_LEVEL_DEBUG, "")
|
||||
}
|
||||
|
||||
fun i() {
|
||||
log(LOG_LEVEL_INFO, "")
|
||||
}
|
||||
|
||||
fun w() {
|
||||
log(LOG_LEVEL_WARN, "")
|
||||
}
|
||||
|
||||
fun e() {
|
||||
log(LOG_LEVEL_ERROR, "")
|
||||
}
|
||||
|
||||
fun v(msg: String) {
|
||||
if (LogLevel <= LOG_LEVEL_VERBOSE) {
|
||||
log(LOG_LEVEL_VERBOSE, msg)
|
||||
}
|
||||
}
|
||||
|
||||
fun d(msg: String) {
|
||||
if (LogLevel <= LOG_LEVEL_DEBUG) {
|
||||
log(LOG_LEVEL_DEBUG, msg)
|
||||
}
|
||||
}
|
||||
|
||||
fun i(msg: String) {
|
||||
if (LogLevel <= LOG_LEVEL_INFO) {
|
||||
log(LOG_LEVEL_INFO, msg)
|
||||
}
|
||||
}
|
||||
|
||||
fun w(msg: String) {
|
||||
if (LogLevel <= LOG_LEVEL_WARN) {
|
||||
log(LOG_LEVEL_WARN, msg)
|
||||
}
|
||||
}
|
||||
|
||||
fun e(msg: String) {
|
||||
if (LogLevel <= LOG_LEVEL_ERROR) {
|
||||
log(LOG_LEVEL_ERROR, msg)
|
||||
}
|
||||
}
|
||||
|
||||
private fun log(logLevel: Int, msg: String) {
|
||||
if(!isShow){
|
||||
return
|
||||
}
|
||||
val caller = Thread.currentThread().stackTrace[4]
|
||||
var callerClazzName = caller.className
|
||||
if (callerClazzName.contains(".")) {
|
||||
callerClazzName = callerClazzName.substring(callerClazzName.lastIndexOf(".") + 1)
|
||||
}
|
||||
if (callerClazzName.contains("$")) {
|
||||
callerClazzName = callerClazzName.substring(0, callerClazzName.indexOf("$"))
|
||||
}
|
||||
var tag = callerClazzName
|
||||
if (!TextUtils.isEmpty(AppName)) {
|
||||
tag = AppName + "_" + tag
|
||||
}
|
||||
if (PrintLine) {
|
||||
tag += "(Line:%d)"
|
||||
tag = String.format(tag, caller.lineNumber)
|
||||
}
|
||||
tag = String.format(tag, callerClazzName)
|
||||
val message = "---" + caller.methodName + "---" + msg
|
||||
when (logLevel) {
|
||||
LOG_LEVEL_VERBOSE -> Log.v(tag, message)
|
||||
LOG_LEVEL_DEBUG -> Log.d(tag, message)
|
||||
LOG_LEVEL_INFO -> Log.i(tag, message)
|
||||
LOG_LEVEL_WARN -> Log.w(tag, message)
|
||||
LOG_LEVEL_ERROR -> Log.e(tag, message)
|
||||
LOG_LEVEL_NOLOG -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,183 +0,0 @@
|
||||
package com.example.union_ad_ssgf
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import com.example.union_ad_ssgf.config.UnionADConfig
|
||||
import com.example.union_ad_ssgf.page.AdSplashActivity
|
||||
import com.example.union_ad_ssgf.page.RewardVideoPage
|
||||
import com.qq.e.comm.managers.GDTAdSdk
|
||||
import com.qq.e.comm.managers.setting.GlobalSetting.setPersonalizedState
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding
|
||||
import io.flutter.plugin.common.EventChannel
|
||||
import io.flutter.plugin.common.EventChannel.EventSink
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
|
||||
|
||||
/**
|
||||
* 插件代理中心
|
||||
*
|
||||
* @param activity 当前 Activity
|
||||
* @param bind Flutter 插件绑定对象
|
||||
*/
|
||||
class PluginDelegate(var activity: Activity, bind: FlutterPluginBinding) :
|
||||
MethodChannel.MethodCallHandler, EventChannel.StreamHandler {
|
||||
private val s = PluginDelegate::class.java.getSimpleName();
|
||||
|
||||
// 事件通道
|
||||
private var eventSink: EventSink? = null
|
||||
|
||||
/**
|
||||
* 初始化代码块
|
||||
*/
|
||||
init {
|
||||
_instance = this
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* 插件代理对象
|
||||
*/
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private lateinit var _instance: PluginDelegate
|
||||
fun getInstance(): PluginDelegate {
|
||||
return _instance
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||
val method = call.method
|
||||
Log.d(s, "MethodChannel onMethodCall method:" + method + " arguments:" + call.arguments)
|
||||
when (method) {
|
||||
/**
|
||||
* 获取当前版本
|
||||
*/
|
||||
"getPlatformVersion" -> {
|
||||
getPlatformVersion(call, result)
|
||||
}
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
"initAd" -> {
|
||||
initAd(call, result)
|
||||
}
|
||||
/**
|
||||
* 设置广告个性化
|
||||
*/
|
||||
"setPersonalizedState" -> {
|
||||
setPersonalizedState(call, result)
|
||||
}
|
||||
/**
|
||||
* 开屏广告
|
||||
*/
|
||||
"showSplashAd" -> {
|
||||
showSplashAd(call, result)
|
||||
}
|
||||
/**
|
||||
* 激励广告
|
||||
*/
|
||||
"showRewardVideoAd" -> {
|
||||
showRewardVideoAd(call, result);
|
||||
}
|
||||
/**
|
||||
* 错误桥接
|
||||
*/
|
||||
else -> {
|
||||
result.notImplemented()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onListen(arguments: Any, events: EventChannel.EventSink) {
|
||||
Log.d(s, "EventChannel onListen arguments:$arguments")
|
||||
eventSink = events
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消事件通道监听
|
||||
* @param arguments 参数
|
||||
*/
|
||||
override fun onCancel(arguments: Any?) {
|
||||
Log.d(s, "EventChannel onCancel")
|
||||
eventSink = null
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前版本
|
||||
* @param call MethodCall
|
||||
* @param result Result
|
||||
*/
|
||||
private fun getPlatformVersion(call: MethodCall, result: MethodChannel.Result) {
|
||||
result.success(" Union AD Android " + android.os.Build.VERSION.RELEASE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化广告
|
||||
* @param call MethodCall
|
||||
* @param result Result
|
||||
*/
|
||||
private fun initAd(call: MethodCall, result: MethodChannel.Result) {
|
||||
val appId = call.argument<String>("appId")
|
||||
/**
|
||||
* 该接口不会采集用户信息
|
||||
*/
|
||||
GDTAdSdk.initWithoutStart(activity.applicationContext, appId)
|
||||
/**
|
||||
* 调用initWithoutStart后请尽快调用start,否则可能影响广告填充,造成收入下降
|
||||
*/
|
||||
GDTAdSdk.start(object : GDTAdSdk.OnStartListener {
|
||||
/**
|
||||
* 推荐开发者在 onStartSuccess 回调后开始拉广告
|
||||
*/
|
||||
override fun onStartSuccess() {
|
||||
Log.e("gdt onStartSuccess", "加载成功")
|
||||
}
|
||||
override fun onStartFailed(e: Exception) {
|
||||
Log.e("gdt onStartFailed:", e.toString())
|
||||
}
|
||||
})
|
||||
result.success(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置广告个性化
|
||||
* @param call MethodCall
|
||||
* @param result Result
|
||||
*/
|
||||
private fun setPersonalizedState(call: MethodCall, result: MethodChannel.Result) {
|
||||
val state = call.argument<Int>("state")!!
|
||||
setPersonalizedState(state)
|
||||
result.success(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 开屏广告
|
||||
* @param call MethodCall
|
||||
* @param result Result
|
||||
*/
|
||||
private fun showSplashAd(call: MethodCall, result: MethodChannel.Result) {
|
||||
val posId: String? = call.argument(UnionADConfig.KEY_POSID)
|
||||
val logo: String? = call.argument(UnionADConfig.KEY_LOGO)
|
||||
val fetchDelay: Double = call.argument(UnionADConfig.KEY_FETCH_DELAY)!!
|
||||
val intent = Intent(activity, AdSplashActivity::class.java)
|
||||
intent.putExtra(UnionADConfig.KEY_POSID, posId)
|
||||
intent.putExtra(UnionADConfig.KEY_LOGO, logo)
|
||||
intent.putExtra(UnionADConfig.KEY_FETCH_DELAY, fetchDelay)
|
||||
activity.startActivity(intent)
|
||||
result.success(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示激励视频广告
|
||||
* @param call MethodCall
|
||||
* @param result Result
|
||||
*/
|
||||
private fun showRewardVideoAd(call: MethodCall, result: MethodChannel.Result) {
|
||||
val iad = RewardVideoPage()
|
||||
iad.showAd(activity, 1, call)
|
||||
result.success(true)
|
||||
}
|
||||
|
||||
}
|
249
android/src/main/kotlin/com/example/union_ad_ssgf/UIUtils.kt
Normal file
249
android/src/main/kotlin/com/example/union_ad_ssgf/UIUtils.kt
Normal file
@@ -0,0 +1,249 @@
|
||||
package com.example.union_ad_ssgf;
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
|
||||
object UIUtils {
|
||||
|
||||
fun getScreenWidth(context: Context): Int {
|
||||
return context.resources.displayMetrics.widthPixels
|
||||
}
|
||||
|
||||
fun getScreenHeight(context: Context): Int {
|
||||
return getRealHeight(context)
|
||||
}
|
||||
|
||||
fun getScreenWidthDp(context: Context): Float {
|
||||
val scale = context.resources.displayMetrics.density.toFloat()
|
||||
val width = context.resources.displayMetrics.widthPixels.toFloat()
|
||||
val num = if (scale <= 0) 1f else scale
|
||||
return width / num + 0.5f
|
||||
}
|
||||
|
||||
//全面屏、刘海屏适配
|
||||
fun getHeightDp(activity: Activity): Float {
|
||||
hideBottomUIMenu(activity)
|
||||
val height: Float
|
||||
val realHeight = getRealHeight(activity)
|
||||
height = if (hasNotchScreen(activity)) {
|
||||
px2dip(activity, realHeight - getStatusBarHeight(activity))
|
||||
} else {
|
||||
px2dip(activity, realHeight.toFloat())
|
||||
}
|
||||
return height
|
||||
}
|
||||
|
||||
fun hideBottomUIMenu(activity: Activity?) {
|
||||
if (activity == null) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
//隐藏虚拟按键,并且全屏
|
||||
if (Build.VERSION.SDK_INT > 11 && Build.VERSION.SDK_INT < 19) { // lower api
|
||||
val v = activity.window.decorView
|
||||
v.systemUiVisibility = View.GONE
|
||||
} else if (Build.VERSION.SDK_INT >= 19) {
|
||||
//for new api versions.
|
||||
val decorView = activity.window.decorView
|
||||
val uiOptions = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
|
||||
// | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
|
||||
or View.SYSTEM_UI_FLAG_IMMERSIVE)
|
||||
decorView.systemUiVisibility = uiOptions
|
||||
activity.window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
//获取屏幕真实高度,不包含下方虚拟导航栏
|
||||
fun getRealHeight(context: Context): Int {
|
||||
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||
val display = windowManager.defaultDisplay
|
||||
val dm = DisplayMetrics()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
display.getRealMetrics(dm)
|
||||
} else {
|
||||
display.getMetrics(dm)
|
||||
}
|
||||
return dm.heightPixels
|
||||
}
|
||||
|
||||
//获取状态栏高度
|
||||
fun getStatusBarHeight(context: Context): Float {
|
||||
var height = 0f
|
||||
val resourceId = context.applicationContext.resources.getIdentifier(
|
||||
"status_bar_height",
|
||||
"dimen",
|
||||
"android"
|
||||
)
|
||||
if (resourceId > 0) {
|
||||
height =
|
||||
context.applicationContext.resources.getDimensionPixelSize(resourceId).toFloat()
|
||||
}
|
||||
return height
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
|
||||
*/
|
||||
fun px2dip(context: Context, pxValue: Float): Float {
|
||||
val scale = context.resources.displayMetrics.density
|
||||
var num = if (scale <= 0) 1f else scale
|
||||
return pxValue / num + 0.5f
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
|
||||
*/
|
||||
fun dip2px(context: Context, dpValue: Float): Float {
|
||||
val scale = context.resources.displayMetrics.density
|
||||
return dpValue * scale + 0.5f
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是刘海屏
|
||||
* @return
|
||||
*/
|
||||
fun hasNotchScreen(activity: Activity): Boolean {
|
||||
return if (getInt(
|
||||
"ro.miui.notch",
|
||||
activity
|
||||
) == 1 || hasNotchAtHuawei(activity) || hasNotchAtOPPO(activity)
|
||||
|| hasNotchAtVivo(activity) || isAndroidPHasNotch(activity)
|
||||
) {
|
||||
true
|
||||
} else false
|
||||
}
|
||||
|
||||
/**
|
||||
* Android P 刘海屏判断
|
||||
* @param activity
|
||||
* @return
|
||||
*/
|
||||
fun isAndroidPHasNotch(activity: Activity?): Boolean {
|
||||
var ret = false
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
try {
|
||||
val windowInsets = Class.forName("android.view.WindowInsets")
|
||||
val method = windowInsets.getMethod("getDisplayCutout")
|
||||
val displayCutout = method.invoke(windowInsets)
|
||||
if (displayCutout != null) {
|
||||
ret = true
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
/**
|
||||
* 小米刘海屏判断.
|
||||
* @return 0 if it is not notch ; return 1 means notch
|
||||
* @throws IllegalArgumentException if the key exceeds 32 characters
|
||||
*/
|
||||
fun getInt(key: String?, activity: Activity): Int {
|
||||
var result = 0
|
||||
if (isMiui()) {
|
||||
try {
|
||||
val classLoader = activity.classLoader
|
||||
val SystemProperties = classLoader.loadClass("android.os.SystemProperties")
|
||||
//参数类型
|
||||
val paramTypes = arrayOfNulls<Class<*>?>(2)
|
||||
paramTypes[0] = String::class.java
|
||||
paramTypes[1] = Int::class.javaPrimitiveType
|
||||
val getInt = SystemProperties.getMethod("getInt", *paramTypes)
|
||||
//参数
|
||||
val params = arrayOfNulls<Any>(2)
|
||||
params[0] = String
|
||||
params[1] = Int
|
||||
result = getInt.invoke(SystemProperties, *params) as Int
|
||||
} catch (e: ClassNotFoundException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: NoSuchMethodException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalAccessException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
e.printStackTrace()
|
||||
} catch (e: InvocationTargetException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 华为刘海屏判断
|
||||
* @return
|
||||
*/
|
||||
fun hasNotchAtHuawei(context: Context): Boolean {
|
||||
var ret = false
|
||||
try {
|
||||
val classLoader = context.classLoader
|
||||
val HwNotchSizeUtil = classLoader.loadClass("com.huawei.android.util.HwNotchSizeUtil")
|
||||
val get = HwNotchSizeUtil.getMethod("hasNotchInScreen")
|
||||
ret = get.invoke(HwNotchSizeUtil) as Boolean
|
||||
} catch (e: ClassNotFoundException) {
|
||||
} catch (e: NoSuchMethodException) {
|
||||
} catch (e: Exception) {
|
||||
} finally {
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
val VIVO_NOTCH = 0x00000020 //是否有刘海
|
||||
|
||||
val VIVO_FILLET = 0x00000008 //是否有圆角
|
||||
|
||||
|
||||
/**
|
||||
* VIVO刘海屏判断
|
||||
* @return
|
||||
*/
|
||||
fun hasNotchAtVivo(context: Context): Boolean {
|
||||
var ret = false
|
||||
try {
|
||||
val classLoader = context.classLoader
|
||||
val FtFeature = classLoader.loadClass("android.util.FtFeature")
|
||||
val method = FtFeature.getMethod("isFeatureSupport", Int::class.javaPrimitiveType)
|
||||
ret = method.invoke(FtFeature, VIVO_NOTCH) as Boolean
|
||||
} catch (e: ClassNotFoundException) {
|
||||
} catch (e: NoSuchMethodException) {
|
||||
} catch (e: Exception) {
|
||||
} finally {
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* OPPO刘海屏判断
|
||||
* @return
|
||||
*/
|
||||
fun hasNotchAtOPPO(context: Context): Boolean {
|
||||
return context.packageManager.hasSystemFeature("com.oppo.feature.screen.heteromorphism")
|
||||
}
|
||||
|
||||
fun isMiui(): Boolean {
|
||||
var sIsMiui = false
|
||||
try {
|
||||
val clz = Class.forName("miui.os.Build")
|
||||
if (clz != null) {
|
||||
sIsMiui = true
|
||||
return sIsMiui
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// ignore
|
||||
}
|
||||
return sIsMiui
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package com.example.union_ad_ssgf;
|
||||
|
||||
class UnionAdSsgfConfig {
|
||||
companion object {
|
||||
// event事件
|
||||
const val adevent = "com.example.union_ad_ssgf/adevent"
|
||||
|
||||
// BannerAdView
|
||||
const val bannerAdView = "com.example.union_ad_ssgf/BannerAdView"
|
||||
|
||||
// SplashAdView
|
||||
const val splashAdView = "com.example.union_ad_ssgf/SplashAdView"
|
||||
|
||||
// NativeExpressADView
|
||||
const val nativeAdView = "com.example.union_ad_ssgf/NativeExpressAdView"
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
package com.example.union_ad_ssgf;
|
||||
|
||||
import android.content.Context
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||
import io.flutter.plugin.common.EventChannel
|
||||
|
||||
class UnionAdSsgfEventPlugin : FlutterPlugin, EventChannel.StreamHandler {
|
||||
companion object {
|
||||
private var eventChannel: EventChannel? = null
|
||||
|
||||
private var eventSink: EventChannel.EventSink? = null
|
||||
|
||||
private var context: Context? = null
|
||||
|
||||
fun sendContent(content: MutableMap<String, Any?>) {
|
||||
eventSink?.success(content);
|
||||
}
|
||||
}
|
||||
|
||||
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
|
||||
eventSink = events
|
||||
}
|
||||
|
||||
override fun onCancel(arguments: Any?) {
|
||||
eventSink = null
|
||||
}
|
||||
|
||||
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
||||
eventChannel = EventChannel(binding.binaryMessenger, UnionAdSsgfConfig.adevent)
|
||||
eventChannel!!.setStreamHandler(this)
|
||||
context = binding.applicationContext
|
||||
}
|
||||
|
||||
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
||||
eventChannel = null
|
||||
eventChannel!!.setStreamHandler(null)
|
||||
}
|
||||
}
|
@@ -1,87 +1,117 @@
|
||||
package com.example.union_ad_ssgf
|
||||
package com.example.union_ad_ssgf;
|
||||
|
||||
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
|
||||
import io.flutter.plugin.common.EventChannel;
|
||||
import io.flutter.plugin.common.MethodChannel;
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.annotation.NonNull
|
||||
import com.example.union_ad_ssgf.interstitialad.InterstitialAd
|
||||
import com.example.union_ad_ssgf.rewardvideoad.RewardVideoAd
|
||||
import com.qq.e.comm.DownloadService
|
||||
import com.qq.e.comm.managers.GDTAdSdk
|
||||
import com.qq.e.comm.managers.setting.GlobalSetting
|
||||
import com.qq.e.comm.managers.status.SDKStatus
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||
import io.flutter.plugin.common.MethodChannel.Result
|
||||
import java.lang.Exception
|
||||
|
||||
/**
|
||||
* 三商共富 - 三方插件
|
||||
* 腾讯优量汇插件
|
||||
*/
|
||||
class UnionAdSsgfPlugin : FlutterPlugin, ActivityAware {
|
||||
/**
|
||||
* 方法通道:
|
||||
*
|
||||
* 用于Flutter与原生Android之间通信的 MethodChannel
|
||||
* 此本地引用用于向Flutter引擎注册插件,并在Flutter引擎与Activity分离时注销它
|
||||
*/
|
||||
private lateinit var methodChannel: MethodChannel
|
||||
class UnionAdSsgfPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
|
||||
|
||||
/**
|
||||
* 事件通道
|
||||
*
|
||||
* 用于Flutter与原生Android之间通信的 EventChannel
|
||||
* 此本地引用用于向Flutter引擎注册插件,并在Flutter引擎与Activity分离时注销它
|
||||
*/
|
||||
private lateinit var eventChannel: EventChannel
|
||||
private lateinit var channel: MethodChannel
|
||||
private var applicationContext: Context? = null
|
||||
private var mActivity: Activity? = null
|
||||
private var mFlutterPluginBinding: FlutterPlugin.FlutterPluginBinding? = null
|
||||
|
||||
|
||||
/**
|
||||
* 插件代理
|
||||
*/
|
||||
var delegate: PluginDelegate? = null
|
||||
|
||||
|
||||
/**
|
||||
* 插件连接器
|
||||
*/
|
||||
private lateinit var bind: FlutterPlugin.FlutterPluginBinding
|
||||
|
||||
/**
|
||||
* 初始化方法通道和事件通道
|
||||
*/
|
||||
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||
bind = flutterPluginBinding
|
||||
|
||||
// 注册 - 方法通道
|
||||
methodChannel = MethodChannel(flutterPluginBinding.binaryMessenger, "union_ad_ssgf_method")
|
||||
|
||||
// 注册 - 监听通道
|
||||
eventChannel = EventChannel(flutterPluginBinding.getBinaryMessenger(), "union_ad_ssgf_event")
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销通道
|
||||
*/
|
||||
override fun onDetachedFromEngine(p0: FlutterPlugin.FlutterPluginBinding) {
|
||||
// 解除 - 方法通道
|
||||
methodChannel.setMethodCallHandler(null)
|
||||
|
||||
// 解除 - 事件通道
|
||||
eventChannel.setStreamHandler(null)
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定activity
|
||||
*/
|
||||
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
|
||||
delegate = PluginDelegate(binding.activity, bind)
|
||||
methodChannel.setMethodCallHandler(delegate)
|
||||
eventChannel.setStreamHandler(delegate)
|
||||
}
|
||||
|
||||
|
||||
override fun onDetachedFromActivityForConfigChanges() {
|
||||
onDetachedFromActivity()
|
||||
mActivity = binding.activity
|
||||
Log.e("UnionAdSsgfPlugin->", "onAttachedToActivity");
|
||||
UnionAdSsgfViewPlugin.registerWith(mFlutterPluginBinding!!, mActivity!!)
|
||||
}
|
||||
|
||||
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
|
||||
onAttachedToActivity(binding)
|
||||
mActivity = binding.activity
|
||||
Log.e("UnionAdSsgfPlugin->", "onReattachedToActivityForConfigChanges")
|
||||
}
|
||||
|
||||
override fun onDetachedFromActivityForConfigChanges() {
|
||||
mActivity = null
|
||||
Log.e("UnionAdSsgfPlugin->", "onDetachedFromActivityForConfigChanges")
|
||||
}
|
||||
|
||||
override fun onDetachedFromActivity() {
|
||||
this.delegate = null
|
||||
mActivity = null
|
||||
Log.e("UnionAdSsgfPlugin->", "onDetachedFromActivity")
|
||||
}
|
||||
|
||||
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter_union_ad")
|
||||
channel.setMethodCallHandler(this)
|
||||
applicationContext = flutterPluginBinding.applicationContext
|
||||
mFlutterPluginBinding = flutterPluginBinding
|
||||
UnionAdSsgfEventPlugin().onAttachedToEngine(flutterPluginBinding)
|
||||
}
|
||||
|
||||
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
|
||||
//注册初始化
|
||||
if (call.method == "register") {
|
||||
val appId = call.argument<String>("androidId")
|
||||
val debug = call.argument<Boolean>("debug")
|
||||
val channelId = call.argument<Int>("channelId")
|
||||
val personalized = call.argument<Int>("personalized")
|
||||
// 日志
|
||||
LogUtil.setAppName("flutter_union_ad")
|
||||
LogUtil.setShow(debug!!)
|
||||
// 是否开启个性化
|
||||
GlobalSetting.setPersonalizedState(personalized!!)
|
||||
// 设置渠道id
|
||||
GlobalSetting.setChannel(channelId!!)
|
||||
GDTAdSdk.initWithoutStart(applicationContext, appId)
|
||||
GDTAdSdk.start(object : GDTAdSdk.OnStartListener {
|
||||
override fun onStartSuccess() {
|
||||
result.success(true)
|
||||
}
|
||||
|
||||
override fun onStartFailed(p0: Exception?) {
|
||||
result.success(false)
|
||||
}
|
||||
|
||||
})
|
||||
// 获取sdk版本
|
||||
} else if (call.method == "getSDKVersion") {
|
||||
result.success(SDKStatus.getIntegrationSDKVersion())
|
||||
// 预加载激励广告
|
||||
} else if (call.method == "loadRewardVideoAd") {
|
||||
RewardVideoAd.init(applicationContext!!, call.arguments as Map<*, *>)
|
||||
result.success(true)
|
||||
// 展示激励广告
|
||||
} else if (call.method == "showRewardVideoAd") {
|
||||
RewardVideoAd.showAd(call.arguments as Map<*, *>)
|
||||
result.success(true)
|
||||
// 预加载插屏广告
|
||||
} else if (call.method == "loadInterstitialAD") {
|
||||
InterstitialAd.init(mActivity!!, call.arguments as Map<*, *>)
|
||||
result.success(true)
|
||||
// 展示插屏广告
|
||||
} else if (call.method == "showInterstitialAD") {
|
||||
InterstitialAd.showAd(call.arguments as Map<*, *>)
|
||||
result.success(true)
|
||||
// 进入下载列表
|
||||
} else if (call.method == "enterAPPDownloadListPage") {
|
||||
DownloadService.enterAPPDownloadListPage(mActivity)
|
||||
result.success(true)
|
||||
} else {
|
||||
result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
|
||||
channel.setMethodCallHandler(null)
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,27 @@
|
||||
package com.example.union_ad_ssgf;
|
||||
|
||||
import android.app.Activity
|
||||
import com.example.union_ad_ssgf.bannerad.BannerAdViewFactory
|
||||
import com.example.union_ad_ssgf.expressad.NativeExpressAdViewFactory
|
||||
import com.example.union_ad_ssgf.splashad.SplashAdViewFactory
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||
|
||||
object UnionAdSsgfViewPlugin {
|
||||
fun registerWith(binding: FlutterPlugin.FlutterPluginBinding, activity: Activity) {
|
||||
// 注册banner广告
|
||||
binding.platformViewRegistry.registerViewFactory(
|
||||
UnionAdSsgfConfig.bannerAdView,
|
||||
BannerAdViewFactory(binding.binaryMessenger, activity)
|
||||
)
|
||||
// 注册splash广告
|
||||
binding.platformViewRegistry.registerViewFactory(
|
||||
UnionAdSsgfConfig.splashAdView,
|
||||
SplashAdViewFactory(binding.binaryMessenger, activity)
|
||||
)
|
||||
// 注册Express广告
|
||||
binding.platformViewRegistry.registerViewFactory(
|
||||
UnionAdSsgfConfig.nativeAdView,
|
||||
NativeExpressAdViewFactory(binding.binaryMessenger, activity)
|
||||
)
|
||||
}
|
||||
}
|
@@ -0,0 +1,175 @@
|
||||
package com.example.union_ad_ssgf.bannerad
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import com.example.union_ad_ssgf.*
|
||||
import com.qq.e.ads.banner2.UnifiedBannerADListener
|
||||
import com.qq.e.ads.banner2.UnifiedBannerView
|
||||
import com.qq.e.comm.pi.IBidding
|
||||
import com.qq.e.comm.util.AdError
|
||||
import io.flutter.plugin.common.BinaryMessenger
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugin.platform.PlatformView
|
||||
|
||||
/**
|
||||
* @Description: 平台模板Banner广告
|
||||
**/
|
||||
|
||||
internal class BannerAdView(
|
||||
var activity: Activity,
|
||||
messenger: BinaryMessenger,
|
||||
id: Int,
|
||||
params: Map<String?, Any?>
|
||||
) :
|
||||
PlatformView, UnifiedBannerADListener, MethodChannel.MethodCallHandler {
|
||||
|
||||
private val TAG = "BannerAdView"
|
||||
|
||||
private var mContainer: FrameLayout? = null
|
||||
|
||||
//广告所需参数
|
||||
private var codeId: String
|
||||
private var viewWidth: Float
|
||||
private var viewHeight: Float
|
||||
|
||||
private var unifiedBannerView: UnifiedBannerView? = null
|
||||
|
||||
private var channel: MethodChannel?
|
||||
|
||||
private var downloadConfirm: Boolean
|
||||
//是否开启竞价
|
||||
private var isBidding: Boolean = params["isBidding"] as Boolean
|
||||
|
||||
|
||||
init {
|
||||
codeId = params["androidId"] as String
|
||||
var width = params["viewWidth"] as Double
|
||||
var height = params["viewHeight"] as Double
|
||||
downloadConfirm = params["downloadConfirm"] as Boolean
|
||||
viewWidth = width.toFloat()
|
||||
viewHeight = height.toFloat()
|
||||
mContainer = FrameLayout(activity)
|
||||
mContainer?.layoutParams?.width = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
mContainer?.layoutParams?.height = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
channel = MethodChannel(messenger, UnionAdSsgfConfig.bannerAdView + "_" + id)
|
||||
channel?.setMethodCallHandler(this)
|
||||
loadBannerAd()
|
||||
}
|
||||
|
||||
private fun loadBannerAd() {
|
||||
unifiedBannerView = UnifiedBannerView(activity, codeId, this)
|
||||
unifiedBannerView?.loadAD()
|
||||
}
|
||||
|
||||
override fun getView(): View {
|
||||
return mContainer!!
|
||||
}
|
||||
|
||||
|
||||
//广告加载失败,error 对象包含了错误码和错误信息
|
||||
override fun onNoAD(p0: AdError?) {
|
||||
LogUtil.e("$TAG Banner广告加载失败 ${p0?.errorCode} ${p0?.errorMsg}")
|
||||
var map: MutableMap<String, Any?> =
|
||||
mutableMapOf("code" to p0?.errorCode, "message" to p0?.errorMsg)
|
||||
channel?.invokeMethod("onFail", map)
|
||||
}
|
||||
|
||||
//广告加载成功回调,表示广告相关的资源已经加载完毕,Ready To Show
|
||||
override fun onADReceive() {
|
||||
mContainer?.removeAllViews()
|
||||
if (unifiedBannerView == null) {
|
||||
LogUtil.e("$TAG Banner广告加载失败 unifiedBannerView不存在或已销毁")
|
||||
var map: MutableMap<String, Any?> =
|
||||
mutableMapOf("code" to 0, "message" to "BannerView不存在或已销毁")
|
||||
channel?.invokeMethod("onFail", map)
|
||||
return
|
||||
}
|
||||
if (downloadConfirm) {
|
||||
unifiedBannerView?.setDownloadConfirmListener(DownloadConfirmHelper.DOWNLOAD_CONFIRM_LISTENER)
|
||||
}
|
||||
//竞价 则返回价格 不直接加载
|
||||
if (isBidding) {
|
||||
channel?.invokeMethod("onECPM", mutableMapOf(
|
||||
"ecpmLevel" to unifiedBannerView?.ecpmLevel,
|
||||
"ecpm" to unifiedBannerView?.ecpm
|
||||
))
|
||||
} else {
|
||||
LogUtil.e("$TAG Banner广告加载成功回调")
|
||||
mContainer?.addView(unifiedBannerView)
|
||||
val map: MutableMap<String, Any?> = mutableMapOf(
|
||||
"width" to UIUtils.px2dip(activity, unifiedBannerView?.width!!.toFloat()),
|
||||
"height" to UIUtils.px2dip(activity, unifiedBannerView?.height!!.toFloat())
|
||||
)
|
||||
channel?.invokeMethod("onShow", map)
|
||||
}
|
||||
}
|
||||
|
||||
//当广告曝光时发起的回调
|
||||
override fun onADExposure() {
|
||||
LogUtil.e("$TAG Banner广告曝光")
|
||||
channel?.invokeMethod("onExpose", "")
|
||||
}
|
||||
|
||||
//当广告关闭时调用
|
||||
override fun onADClosed() {
|
||||
LogUtil.e("$TAG Banner广告关闭")
|
||||
channel?.invokeMethod("onClose", "")
|
||||
}
|
||||
|
||||
//当广告点击时发起的回调,由于点击去重等原因可能和平台最终的统计数据有差异
|
||||
override fun onADClicked() {
|
||||
LogUtil.e("$TAG Banner广告点击")
|
||||
channel?.invokeMethod("onClick", "")
|
||||
}
|
||||
|
||||
//由于广告点击离开 APP 时调用
|
||||
override fun onADLeftApplication() {
|
||||
LogUtil.e("$TAG Banner广告点击离开 APP")
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
unifiedBannerView?.destroy()
|
||||
unifiedBannerView = null
|
||||
}
|
||||
|
||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||
when (call.method) {
|
||||
//竞价成功
|
||||
"biddingSucceeded" -> {
|
||||
var arguments = call.arguments as Map<*, *>
|
||||
val map: MutableMap<String, Any?> = mutableMapOf(
|
||||
//对应值为竞胜出价,类型为Integer
|
||||
IBidding.EXPECT_COST_PRICE to arguments["expectCostPrice"],
|
||||
//对应值为最大竞败方出价,类型为Integer
|
||||
IBidding.HIGHEST_LOSS_PRICE to arguments["highestLossPrice"],
|
||||
)
|
||||
unifiedBannerView?.sendWinNotification(map)
|
||||
//展示banner
|
||||
mContainer?.addView(unifiedBannerView)
|
||||
val map2: MutableMap<String, Any?> = mutableMapOf(
|
||||
"width" to UIUtils.px2dip(activity, unifiedBannerView?.width!!.toFloat()),
|
||||
"height" to UIUtils.px2dip(activity, unifiedBannerView?.height!!.toFloat())
|
||||
)
|
||||
channel?.invokeMethod("onShow", map2)
|
||||
}
|
||||
//竞价失败
|
||||
"biddingFail" -> {
|
||||
var arguments = call.arguments as Map<*, *>
|
||||
val map: MutableMap<String, Any?> = mutableMapOf(
|
||||
//值为本次竞胜方出价(单位:分),类型为Integer。选填
|
||||
IBidding.WIN_PRICE to arguments["winPrice"],
|
||||
//值为优量汇广告竞败原因,类型为Integer。必填
|
||||
IBidding.LOSS_REASON to arguments["lossReason"],
|
||||
//值为本次竞胜方渠道ID,类型为Integer。必填。
|
||||
IBidding.ADN_ID to arguments["adnId"],
|
||||
)
|
||||
unifiedBannerView?.sendLossNotification(map)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package com.example.union_ad_ssgf.bannerad
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import io.flutter.plugin.common.BinaryMessenger
|
||||
import io.flutter.plugin.common.StandardMessageCodec
|
||||
import io.flutter.plugin.platform.PlatformView
|
||||
import io.flutter.plugin.platform.PlatformViewFactory
|
||||
|
||||
internal class BannerAdViewFactory(private val messenger: BinaryMessenger, private val activity: Activity) : PlatformViewFactory(
|
||||
StandardMessageCodec.INSTANCE) {
|
||||
|
||||
override fun create(context: Context?, viewId: Int, args: Any?): PlatformView {
|
||||
val params = args as Map<String?, Any?>
|
||||
return BannerAdView(activity,messenger, viewId, params)
|
||||
}
|
||||
}
|
@@ -1,40 +0,0 @@
|
||||
package com.example.union_ad_ssgf.config
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
|
||||
class UnionADConfig {
|
||||
|
||||
/*初始化代码块*/
|
||||
init {
|
||||
_instance = this
|
||||
}
|
||||
|
||||
companion object {
|
||||
// 插件代理对象
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private lateinit var _instance: UnionADConfig
|
||||
|
||||
fun getInstance(): UnionADConfig {
|
||||
return _instance
|
||||
}
|
||||
|
||||
// Banner View
|
||||
const val KEY_BANNER_VIEW = "flutter_qq_ads_banner"
|
||||
|
||||
// Feed View
|
||||
const val KEY_FEED_VIEW = "flutter_qq_ads_feed"
|
||||
|
||||
// 广告参数
|
||||
const val KEY_POSID = "posId"
|
||||
|
||||
// 广告参数
|
||||
const val KEY_ADID = "adId"
|
||||
|
||||
// logo 参数
|
||||
const val KEY_LOGO = "logo"
|
||||
|
||||
// fetchDelay 参数
|
||||
const val KEY_FETCH_DELAY = "fetchDelay"
|
||||
}
|
||||
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
package com.example.union_ad_ssgf.event
|
||||
|
||||
|
||||
class AdErrorEvent(posId: String?, adId: String?, viewId: Int?, errCode: Int, errMsg: String?) :
|
||||
AdEvent(posId, adId, viewId, AdEventAction.onAdError) {
|
||||
// 错误码
|
||||
private var errCode = 0
|
||||
|
||||
// 错误信息
|
||||
private var errMsg: String? = null
|
||||
|
||||
// 错误事件实体
|
||||
init {
|
||||
this.errMsg = errMsg
|
||||
this.errCode = errCode
|
||||
}
|
||||
|
||||
/**
|
||||
* 重写 toMap 方法
|
||||
* @return 返回错误事件的map
|
||||
*/
|
||||
override fun toMap(): HashMap<String, Any?> {
|
||||
val newMap = super.toMap()
|
||||
newMap["errCode"] = errCode
|
||||
newMap["errMsg"] = errMsg
|
||||
return newMap
|
||||
}
|
||||
}
|
@@ -1,36 +0,0 @@
|
||||
package com.example.union_ad_ssgf.event
|
||||
|
||||
import java.util.HashMap
|
||||
|
||||
open class AdEvent(posId: String?, adId: String?, viewId: Int?, action: String?) {
|
||||
// 广告位 id
|
||||
private var posId: String? = null
|
||||
|
||||
// 广告 id
|
||||
private var adId: String? = null
|
||||
|
||||
private var viewId: Int? = null
|
||||
|
||||
// 操作
|
||||
private var action: String? = null
|
||||
|
||||
init {
|
||||
this.adId = adId
|
||||
this.posId = posId
|
||||
this.action = action
|
||||
this.viewId = viewId
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为 Map 方面传输
|
||||
* @return 转换后的 Map 对象
|
||||
*/
|
||||
open fun toMap(): HashMap<String, Any?> {
|
||||
val map = HashMap<String, Any?>()
|
||||
map["adId"] = adId
|
||||
map["posId"] = posId
|
||||
map["action"] = action
|
||||
map["viewId"] = viewId
|
||||
return map
|
||||
}
|
||||
}
|
@@ -1,32 +0,0 @@
|
||||
package com.example.union_ad_ssgf.event
|
||||
|
||||
class AdEventAction {
|
||||
companion object {
|
||||
// 广告错误
|
||||
val onAdError = "onAdError"
|
||||
|
||||
// 广告加载成功
|
||||
val onAdLoaded = "onAdLoaded"
|
||||
|
||||
// 广告填充
|
||||
val onAdPresent = "onAdPresent"
|
||||
|
||||
// 广告曝光
|
||||
val onAdExposure = "onAdExposure"
|
||||
|
||||
// 广告关闭
|
||||
val onAdClosed = "onAdClosed"
|
||||
|
||||
// 广告点击
|
||||
val onAdClicked = "onAdClicked"
|
||||
|
||||
// 广告跳过
|
||||
val onAdSkip = "onAdSkip"
|
||||
|
||||
// 广告播放或计时完毕
|
||||
val onAdComplete = "onAdComplete"
|
||||
|
||||
// 获得广告激励
|
||||
val onAdReward = "onAdReward"
|
||||
}
|
||||
}
|
@@ -1,31 +0,0 @@
|
||||
package com.example.union_ad_ssgf.event
|
||||
|
||||
|
||||
class AdEventHandler {
|
||||
companion object {
|
||||
|
||||
// 广告事件处理对象
|
||||
@Volatile private var _instance: AdEventHandler? = null
|
||||
|
||||
/**
|
||||
* 获取广告事件处理类
|
||||
* @return 广告事件处理对象
|
||||
*/
|
||||
fun getInstance(): AdEventHandler? {
|
||||
if (_instance == null) {
|
||||
synchronized(AdEventHandler::class.java) { _instance = AdEventHandler() }
|
||||
}
|
||||
return _instance
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加广告事件
|
||||
* @param event 广告事件
|
||||
*/
|
||||
fun sendEvent(event: AdEvent?) {
|
||||
if (event != null) {
|
||||
// PluginDelegate.getInstance().addEvent(event.toMap())
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,25 +0,0 @@
|
||||
package com.example.union_ad_ssgf.event
|
||||
|
||||
|
||||
/**
|
||||
* @param transId 服务端验证唯一id
|
||||
* @param customData 服务端验证的自定义信息
|
||||
* @param userId 服务端验证的用户信息
|
||||
*/
|
||||
class AdRewardEvent(
|
||||
posId: String?,
|
||||
adId: String?,
|
||||
viewId: Int?,
|
||||
action: String?,
|
||||
var transId: String,
|
||||
var customData: String,
|
||||
var userId: String,
|
||||
) : AdEvent(posId, adId, viewId, action) {
|
||||
override fun toMap(): HashMap<String, Any?> {
|
||||
val newMap = super.toMap()
|
||||
newMap["transId"] = transId
|
||||
newMap["customData"] = customData
|
||||
newMap["userId"] = userId
|
||||
return newMap
|
||||
}
|
||||
}
|
@@ -0,0 +1,267 @@
|
||||
package com.example.union_ad_ssgf.expressad
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import com.example.union_ad_ssgf.*
|
||||
import com.qq.e.ads.cfg.VideoOption
|
||||
import com.qq.e.ads.nativ.ADSize
|
||||
import com.qq.e.ads.nativ.NativeExpressAD
|
||||
import com.qq.e.ads.nativ.NativeExpressADView
|
||||
import com.qq.e.ads.nativ.NativeExpressMediaListener
|
||||
import com.qq.e.comm.constants.AdPatternType
|
||||
import com.qq.e.comm.pi.IBidding
|
||||
import com.qq.e.comm.util.AdError
|
||||
import io.flutter.plugin.common.BinaryMessenger
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugin.platform.PlatformView
|
||||
|
||||
/**
|
||||
* @Description: 平台模板NativeExpressAdView广告
|
||||
**/
|
||||
internal class NativeExpressAdView(
|
||||
var activity: Activity,
|
||||
messenger: BinaryMessenger,
|
||||
id: Int,
|
||||
params: Map<String?, Any?>
|
||||
) :
|
||||
PlatformView, NativeExpressAD.NativeExpressADListener, NativeExpressMediaListener ,
|
||||
MethodChannel.MethodCallHandler {
|
||||
|
||||
private var mContainer: FrameLayout? = null
|
||||
|
||||
//广告所需参数
|
||||
private var codeId: String = params["androidId"] as String
|
||||
private var viewWidth: Int = params["viewWidth"] as Int
|
||||
private var viewHeight: Int = params["viewHeight"] as Int
|
||||
private var downloadConfirm: Boolean = params["downloadConfirm"] as Boolean
|
||||
|
||||
//是否开启竞价
|
||||
private var isBidding: Boolean = params["isBidding"] as Boolean
|
||||
|
||||
private var nativeExpressAD: NativeExpressAD? = null
|
||||
private var nativeExpressAdView: NativeExpressADView? = null
|
||||
|
||||
private var channel: MethodChannel?
|
||||
|
||||
|
||||
init {
|
||||
mContainer = FrameLayout(activity)
|
||||
mContainer?.layoutParams?.width = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
mContainer?.layoutParams?.height = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
channel = MethodChannel(messenger, UnionAdSsgfConfig.nativeAdView + "_" + id)
|
||||
channel?.setMethodCallHandler(this)
|
||||
loadExpressAd()
|
||||
}
|
||||
|
||||
private fun loadExpressAd() {
|
||||
val adSize: ADSize = when {
|
||||
viewWidth == 0 -> {
|
||||
ADSize(ADSize.FULL_WIDTH, viewHeight)
|
||||
}
|
||||
viewHeight == 0 -> {
|
||||
ADSize(viewWidth, ADSize.AUTO_HEIGHT)
|
||||
}
|
||||
else -> {
|
||||
ADSize(viewWidth, viewHeight)
|
||||
}
|
||||
}
|
||||
nativeExpressAD = NativeExpressAD(activity, adSize, codeId, this)
|
||||
nativeExpressAD?.setVideoOption(
|
||||
VideoOption.Builder()
|
||||
.setAutoPlayPolicy(VideoOption.AutoPlayPolicy.ALWAYS)
|
||||
.setAutoPlayMuted(true)
|
||||
.build()
|
||||
)
|
||||
nativeExpressAD?.loadAD(1)
|
||||
}
|
||||
|
||||
override fun getView(): View {
|
||||
return mContainer!!
|
||||
}
|
||||
|
||||
/**************平台模板广告加载、渲染、点击状态的回调。****************/
|
||||
|
||||
//无广告填充
|
||||
override fun onNoAD(p0: AdError?) {
|
||||
LogUtil.e("广告加载失败 ${p0?.errorCode} ${p0?.errorMsg}")
|
||||
val map: MutableMap<String, Any?> =
|
||||
mutableMapOf("code" to p0?.errorCode, "message" to p0?.errorMsg)
|
||||
channel?.invokeMethod("onFail", map)
|
||||
}
|
||||
|
||||
//广告数据加载成功,返回了可以用来展示广告的 NativeExpressADView,
|
||||
// 但是想让广告曝光还需要调用 NativeExpressADView 的 render 方法
|
||||
override fun onADLoaded(p0: MutableList<NativeExpressADView>?) {
|
||||
// 释放前一个 NativeExpressADView 的资源
|
||||
if (nativeExpressAdView != null) {
|
||||
nativeExpressAdView?.destroy()
|
||||
nativeExpressAdView = null
|
||||
}
|
||||
if (p0?.size == 0) {
|
||||
LogUtil.e("未拉取到广告")
|
||||
val map: MutableMap<String, Any?> = mutableMapOf("code" to 0, "message" to "未拉取到广告")
|
||||
channel?.invokeMethod("onFail", map)
|
||||
}
|
||||
LogUtil.e("广告数据加载成功")
|
||||
nativeExpressAdView = p0!![0]
|
||||
if (nativeExpressAdView?.boundData?.adPatternType == AdPatternType.NATIVE_VIDEO) {
|
||||
nativeExpressAdView?.setMediaListener(this)
|
||||
}
|
||||
if (downloadConfirm) {
|
||||
nativeExpressAdView?.setDownloadConfirmListener(DownloadConfirmHelper.DOWNLOAD_CONFIRM_LISTENER)
|
||||
}
|
||||
//竞价 则返回价格 不直接加载
|
||||
if (isBidding) {
|
||||
channel?.invokeMethod("onECPM", mutableMapOf(
|
||||
"ecpmLevel" to nativeExpressAdView?.ecpmLevel,
|
||||
"ecpm" to nativeExpressAdView?.ecpm
|
||||
))
|
||||
} else {
|
||||
if (nativeExpressAdView != null) {
|
||||
if (mContainer?.childCount!! > 0) {
|
||||
mContainer?.removeAllViews()
|
||||
}
|
||||
mContainer?.addView(nativeExpressAdView!!.rootView)
|
||||
nativeExpressAdView!!.render()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//NativeExpressADView 渲染广告失败
|
||||
override fun onRenderFail(p0: NativeExpressADView?) {
|
||||
LogUtil.e("渲染广告失败")
|
||||
val map: MutableMap<String, Any?> = mutableMapOf("code" to 0, "message" to "渲染广告失败")
|
||||
channel?.invokeMethod("onFail", map)
|
||||
nativeExpressAdView?.destroy()
|
||||
}
|
||||
|
||||
//NativeExpressADView 渲染广告成功
|
||||
override fun onRenderSuccess(p0: NativeExpressADView?) {
|
||||
val map: MutableMap<String, Any?> = mutableMapOf(
|
||||
"width" to UIUtils.px2dip(activity, p0?.width!!.toFloat()),
|
||||
"height" to UIUtils.px2dip(activity, p0?.height!!.toFloat())
|
||||
)
|
||||
channel?.invokeMethod("onShow", map)
|
||||
}
|
||||
|
||||
//广告曝光
|
||||
override fun onADExposure(p0: NativeExpressADView?) {
|
||||
LogUtil.e("广告曝光")
|
||||
channel?.invokeMethod("onExpose", "")
|
||||
}
|
||||
|
||||
//广告点击
|
||||
override fun onADClicked(p0: NativeExpressADView?) {
|
||||
LogUtil.e("广告点击")
|
||||
channel?.invokeMethod("onClick", "")
|
||||
}
|
||||
|
||||
//广告被关闭,将不再显示广告,此时广告对象已经释放资源,不可以再次用来展示了
|
||||
override fun onADClosed(p0: NativeExpressADView?) {
|
||||
LogUtil.e("广告被关闭")
|
||||
channel?.invokeMethod("onClose", "")
|
||||
p0?.destroy()
|
||||
}
|
||||
|
||||
//因为广告点击等原因离开当前 app 时调用
|
||||
override fun onADLeftApplication(p0: NativeExpressADView?) {
|
||||
LogUtil.e("因为广告点击等原因离开当前 app")
|
||||
}
|
||||
|
||||
/*****************平台模板视频广告播放状态回调接口,专用于带有视频素材的广告对象**************/
|
||||
|
||||
//视频播放 View 初始化完成
|
||||
override fun onVideoInit(p0: NativeExpressADView?) {
|
||||
LogUtil.e("视频播放 View 初始化完成")
|
||||
}
|
||||
|
||||
//视频下载中
|
||||
override fun onVideoLoading(p0: NativeExpressADView?) {
|
||||
LogUtil.e("视频下载中")
|
||||
}
|
||||
|
||||
//视频下载完成
|
||||
override fun onVideoCached(p0: NativeExpressADView?) {
|
||||
LogUtil.e("视频下载完成")
|
||||
// nativeExpressAdView?.
|
||||
}
|
||||
|
||||
//视频播放器初始化完成,准备好可以播放了,videoDuration 是视频素材的时间长度,单位为 ms
|
||||
override fun onVideoReady(p0: NativeExpressADView?, p1: Long) {
|
||||
LogUtil.e("视频播放器初始化完成")
|
||||
// nativeExpressAdView?
|
||||
}
|
||||
|
||||
//视频开始播放
|
||||
override fun onVideoStart(p0: NativeExpressADView?) {
|
||||
LogUtil.e("视频开始播放")
|
||||
}
|
||||
|
||||
//视频暂停
|
||||
override fun onVideoPause(p0: NativeExpressADView?) {
|
||||
LogUtil.e("视频暂停")
|
||||
}
|
||||
|
||||
//视频播放结束,手动调用 stop 或者自然播放到达最后一帧时都会触发
|
||||
override fun onVideoComplete(p0: NativeExpressADView?) {
|
||||
LogUtil.e("视频播放结束")
|
||||
}
|
||||
|
||||
//视频播放时出现错误,error 对象包含了错误码和错误信息,错误码的详细内容可以参考文档第5章
|
||||
override fun onVideoError(p0: NativeExpressADView?, p1: AdError?) {
|
||||
LogUtil.e("视频播放时出现错误 ${p1?.errorCode} ${p1?.errorMsg}")
|
||||
}
|
||||
|
||||
//进入视频落地页
|
||||
override fun onVideoPageOpen(p0: NativeExpressADView?) {
|
||||
LogUtil.e("进入视频落地页")
|
||||
}
|
||||
|
||||
//退出视频落地页
|
||||
override fun onVideoPageClose(p0: NativeExpressADView?) {
|
||||
LogUtil.e("退出视频落地页")
|
||||
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
nativeExpressAdView?.destroy()
|
||||
mContainer?.removeAllViews()
|
||||
}
|
||||
|
||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||
when (call.method){
|
||||
//竞价成功
|
||||
"biddingSucceeded" -> {
|
||||
var arguments = call.arguments as Map<*, *>
|
||||
val map: MutableMap<String, Any?> = mutableMapOf(
|
||||
//对应值为竞胜出价,类型为Integer
|
||||
IBidding.EXPECT_COST_PRICE to arguments["expectCostPrice"],
|
||||
//对应值为最大竞败方出价,类型为Integer
|
||||
IBidding.HIGHEST_LOSS_PRICE to arguments["highestLossPrice"],
|
||||
)
|
||||
nativeExpressAdView?.sendWinNotification(map)
|
||||
//展示信息流
|
||||
mContainer?.addView(nativeExpressAdView!!.rootView)
|
||||
nativeExpressAdView!!.render()
|
||||
}
|
||||
//竞价失败
|
||||
"biddingFail" -> {
|
||||
var arguments = call.arguments as Map<*, *>
|
||||
val map: MutableMap<String, Any?> = mutableMapOf(
|
||||
//值为本次竞胜方出价(单位:分),类型为Integer。选填
|
||||
IBidding.WIN_PRICE to arguments["winPrice"],
|
||||
//值为优量汇广告竞败原因,类型为Integer。必填
|
||||
IBidding.LOSS_REASON to arguments["lossReason"],
|
||||
//值为本次竞胜方渠道ID,类型为Integer。必填。
|
||||
IBidding.ADN_ID to arguments["adnId"],
|
||||
)
|
||||
nativeExpressAdView?.sendLossNotification(map)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package com.example.union_ad_ssgf.expressad
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import io.flutter.plugin.common.BinaryMessenger
|
||||
import io.flutter.plugin.common.StandardMessageCodec
|
||||
import io.flutter.plugin.platform.PlatformView
|
||||
import io.flutter.plugin.platform.PlatformViewFactory
|
||||
|
||||
internal class NativeExpressAdViewFactory(private val messenger: BinaryMessenger, private val activity: Activity) : PlatformViewFactory(
|
||||
StandardMessageCodec.INSTANCE) {
|
||||
|
||||
override fun create(context: Context?, viewId: Int, args: Any?): PlatformView {
|
||||
val params = args as Map<String?, Any?>
|
||||
return NativeExpressAdView(activity,messenger, viewId, params)
|
||||
}
|
||||
}
|
@@ -0,0 +1,227 @@
|
||||
package com.example.union_ad_ssgf.interstitialad
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import com.example.union_ad_ssgf.DownloadConfirmHelper
|
||||
import com.example.union_ad_ssgf.LogUtil
|
||||
import com.qq.e.ads.interstitial2.UnifiedInterstitialAD
|
||||
import com.qq.e.ads.interstitial2.UnifiedInterstitialADListener
|
||||
import com.example.union_ad_ssgf.UnionAdSsgfEventPlugin
|
||||
import com.qq.e.ads.interstitial2.ADRewardListener
|
||||
import com.qq.e.comm.pi.IBidding
|
||||
import com.qq.e.comm.util.AdError
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
object InterstitialAd {
|
||||
private val TAG = "InterstitialAd"
|
||||
|
||||
private lateinit var context: Activity
|
||||
private var unifiedInterstitialAD: UnifiedInterstitialAD? = null
|
||||
|
||||
private var codeId: String? = null
|
||||
private var isFullScreen: Boolean? = false
|
||||
private var downloadConfirm: Boolean = false
|
||||
|
||||
//是否开启竞价
|
||||
private var isBidding: Boolean = false
|
||||
|
||||
fun init(context: Activity, params: Map<*, *>) {
|
||||
this.context = context
|
||||
this.codeId = params["androidId"] as String
|
||||
this.isFullScreen = params["isFullScreen"] as Boolean
|
||||
this.downloadConfirm = params["downloadConfirm"] as Boolean
|
||||
this.isBidding = params["isBidding"] as Boolean
|
||||
loadInterstitialAD()
|
||||
}
|
||||
|
||||
private fun loadInterstitialAD() {
|
||||
unifiedInterstitialAD = UnifiedInterstitialAD(
|
||||
context,
|
||||
codeId,
|
||||
interstitialADListener
|
||||
)
|
||||
if (isFullScreen!!) {
|
||||
unifiedInterstitialAD?.setRewardListener(adRewardListener)
|
||||
unifiedInterstitialAD?.loadFullScreenAD()
|
||||
} else {
|
||||
unifiedInterstitialAD?.loadAD()
|
||||
}
|
||||
}
|
||||
|
||||
fun showAd(params: Map<*, *>) {
|
||||
if (unifiedInterstitialAD == null) {
|
||||
val map: MutableMap<String, Any?> =
|
||||
mutableMapOf("adType" to "interactAd", "onAdMethod" to "onUnReady")
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
LogUtil.e("$TAG 插屏全屏视频广告显示失败,无广告")
|
||||
return
|
||||
}
|
||||
// 广告是否有效,无效广告将无法展示
|
||||
if (!unifiedInterstitialAD?.isValid!!) {
|
||||
val map: MutableMap<String, Any?> = mutableMapOf(
|
||||
"adType" to "interactAd",
|
||||
"onAdMethod" to "onFail",
|
||||
"code" to 1,
|
||||
"message" to "插屏全屏视频视频广告显示失败,无效广告"
|
||||
)
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
LogUtil.e("$TAG 插屏全屏视频广告显示失败,无效广告")
|
||||
unifiedInterstitialAD?.close()
|
||||
unifiedInterstitialAD?.destroy()
|
||||
unifiedInterstitialAD = null
|
||||
return
|
||||
}
|
||||
//是否为竞价模式
|
||||
if (isBidding) {
|
||||
val isSuccess: Boolean = params["isSuccess"] as Boolean
|
||||
//是否成功
|
||||
if (isSuccess) {
|
||||
unifiedInterstitialAD?.sendWinNotification(
|
||||
mutableMapOf(
|
||||
//对应值为竞胜出价,类型为Integer
|
||||
IBidding.EXPECT_COST_PRICE to params["expectCostPrice"],
|
||||
//对应值为最大竞败方出价,类型为Integer
|
||||
IBidding.HIGHEST_LOSS_PRICE to params["highestLossPrice"],
|
||||
)
|
||||
)
|
||||
if (isFullScreen!!) {
|
||||
unifiedInterstitialAD?.showFullScreenAD(context)
|
||||
} else {
|
||||
unifiedInterstitialAD?.show()
|
||||
}
|
||||
} else {
|
||||
unifiedInterstitialAD?.sendLossNotification(
|
||||
mutableMapOf(
|
||||
//值为本次竞胜方出价(单位:分),类型为Integer。选填
|
||||
IBidding.WIN_PRICE to params["winPrice"],
|
||||
//值为优量汇广告竞败原因,类型为Integer。必填
|
||||
IBidding.LOSS_REASON to params["lossReason"],
|
||||
//值为本次竞胜方渠道ID,类型为Integer。必填。
|
||||
IBidding.ADN_ID to params["adnId"],
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (isFullScreen!!) {
|
||||
unifiedInterstitialAD?.showFullScreenAD(context)
|
||||
} else {
|
||||
unifiedInterstitialAD?.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var interstitialADListener = object : UnifiedInterstitialADListener {
|
||||
//插屏全屏视频广告加载完毕,此回调后才可以调用 show 方法
|
||||
override fun onADReceive() {
|
||||
LogUtil.e("$TAG 插屏全屏视频广告加载完毕 $downloadConfirm")
|
||||
if (downloadConfirm) {
|
||||
unifiedInterstitialAD?.setDownloadConfirmListener(DownloadConfirmHelper.DOWNLOAD_CONFIRM_LISTENER)
|
||||
}
|
||||
}
|
||||
|
||||
//插屏全屏视频视频广告,视频素材下载完成
|
||||
override fun onVideoCached() {
|
||||
LogUtil.e("$TAG 插屏全屏视频视频广告,视频素材下载完成")
|
||||
}
|
||||
|
||||
//广告加载失败,error 对象包含了错误码和错误信息
|
||||
override fun onNoAD(p0: AdError?) {
|
||||
LogUtil.e("$TAG 插屏全屏视频视频广告,加载失败 ${p0?.errorCode} ${p0?.errorMsg}")
|
||||
val map: MutableMap<String, Any?> = mutableMapOf(
|
||||
"adType" to "interactAd",
|
||||
"onAdMethod" to "onFail",
|
||||
"code" to p0?.errorCode,
|
||||
"message" to p0?.errorMsg
|
||||
)
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
}
|
||||
|
||||
//插屏全屏视频广告展开时回调
|
||||
override fun onADOpened() {
|
||||
LogUtil.e("$TAG 插屏全屏视频广告展开时回调")
|
||||
val map: MutableMap<String, Any?> =
|
||||
mutableMapOf("adType" to "interactAd", "onAdMethod" to "onShow")
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
}
|
||||
|
||||
//插屏全屏视频广告曝光时回调
|
||||
override fun onADExposure() {
|
||||
LogUtil.e("$TAG 插屏全屏视频广告曝光时回调")
|
||||
val map: MutableMap<String, Any?> =
|
||||
mutableMapOf("adType" to "interactAd", "onAdMethod" to "onExpose")
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
}
|
||||
|
||||
//插屏全屏视频广告点击时回调
|
||||
override fun onADClicked() {
|
||||
LogUtil.e("$TAG 插屏全屏视频广告点击时回调")
|
||||
val map: MutableMap<String, Any?> =
|
||||
mutableMapOf("adType" to "interactAd", "onAdMethod" to "onClick")
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
}
|
||||
|
||||
override fun onADLeftApplication() {
|
||||
LogUtil.e("$TAG 插屏全屏视频视频广告,渲染成功")
|
||||
}
|
||||
|
||||
//插屏全屏视频广告关闭时回调
|
||||
override fun onADClosed() {
|
||||
LogUtil.e("$TAG 插屏全屏视频广告关闭时回调")
|
||||
val map: MutableMap<String, Any?> =
|
||||
mutableMapOf("adType" to "interactAd", "onAdMethod" to "onClose")
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
unifiedInterstitialAD?.close()
|
||||
unifiedInterstitialAD?.destroy()
|
||||
unifiedInterstitialAD = null
|
||||
}
|
||||
|
||||
// 插屏全屏视频视频广告,渲染成功
|
||||
override fun onRenderSuccess() {
|
||||
LogUtil.e("$TAG 插屏全屏视频视频广告,渲染成功")
|
||||
if (isBidding) {
|
||||
val map: MutableMap<String, Any?> =
|
||||
mutableMapOf(
|
||||
"adType" to "interactAd",
|
||||
"onAdMethod" to "onECPM",
|
||||
"ecpmLevel" to unifiedInterstitialAD?.ecpmLevel,
|
||||
"ecpm" to unifiedInterstitialAD?.ecpm
|
||||
)
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
} else {
|
||||
val map: MutableMap<String, Any?> =
|
||||
mutableMapOf("adType" to "interactAd", "onAdMethod" to "onReady")
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
}
|
||||
}
|
||||
|
||||
//插屏全屏视频视频广告,渲染失败
|
||||
override fun onRenderFail() {
|
||||
LogUtil.e("$TAG 插屏全屏视频视频广告,渲染失败")
|
||||
val map: MutableMap<String, Any?> = mutableMapOf(
|
||||
"adType" to "interactAd",
|
||||
"onAdMethod" to "onFail",
|
||||
"code" to 0,
|
||||
"message" to "插屏全屏视频视频广告渲染失败"
|
||||
)
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
unifiedInterstitialAD?.close()
|
||||
unifiedInterstitialAD?.destroy()
|
||||
unifiedInterstitialAD = null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private var adRewardListener = object : ADRewardListener {
|
||||
override fun onReward(p0: MutableMap<String, Any>?) {
|
||||
LogUtil.e("$TAG 激励奖励 $p0")
|
||||
val map: MutableMap<String, Any?> = mutableMapOf(
|
||||
"adType" to "interactAd",
|
||||
"onAdMethod" to "onVerify",
|
||||
"transId" to p0!!["transId"]
|
||||
)
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -1,109 +0,0 @@
|
||||
package com.example.union_ad_ssgf.load
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.example.union_ad_ssgf.config.UnionADConfig
|
||||
import com.example.union_ad_ssgf.event.AdEventAction
|
||||
import com.example.union_ad_ssgf.page.BaseAdPage
|
||||
import com.qq.e.ads.nativ.ADSize
|
||||
import com.qq.e.ads.nativ.NativeExpressAD
|
||||
import com.qq.e.ads.nativ.NativeExpressADView
|
||||
import com.qq.e.comm.util.AdError
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import java.util.Locale
|
||||
|
||||
/** 信息流加载对象 */
|
||||
class FeedAdLoad : BaseAdPage(), NativeExpressAD.NativeExpressADListener {
|
||||
private val TAG = FeedAdLoad::class.java.getSimpleName()
|
||||
private var result: MethodChannel.Result? = null
|
||||
|
||||
/**
|
||||
* 加载信息流广告列表
|
||||
* @param call
|
||||
* @param result
|
||||
*/
|
||||
fun loadFeedAdList(activity: Activity?, call: MethodCall, result: MethodChannel.Result) {
|
||||
this.result = result
|
||||
showAd(activity, null, call)
|
||||
}
|
||||
|
||||
override fun onNoAD(error: AdError) {
|
||||
val msg =
|
||||
String.format(
|
||||
Locale.getDefault(),
|
||||
"onError, error code: %d, error msg: %s",
|
||||
error.getErrorCode(),
|
||||
error.getErrorMsg()
|
||||
)
|
||||
Log.i(TAG, "onError, adError=$msg")
|
||||
sendErrorEvent(error.getErrorCode(), error.getErrorMsg())
|
||||
this.result!!.success(ArrayList<Int>())
|
||||
}
|
||||
|
||||
override fun onADLoaded(list: MutableList<NativeExpressADView>) {
|
||||
Log.i(TAG, "onADLoaded")
|
||||
val adResultList: MutableList<Int> = ArrayList()
|
||||
|
||||
if (list.isEmpty()) {
|
||||
result!!.success(adResultList)
|
||||
return
|
||||
}
|
||||
for (adItem in list) {
|
||||
val key = adItem.hashCode()
|
||||
adResultList.add(key)
|
||||
FeedAdManager.getInstance()?.putAd(key, adItem)
|
||||
}
|
||||
// 添加广告事件
|
||||
sendEvent(AdEventAction.onAdLoaded)
|
||||
result!!.success(adResultList)
|
||||
}
|
||||
|
||||
override fun onRenderFail(nativeExpressADView: NativeExpressADView) {
|
||||
Log.i(TAG, "onRenderFail")
|
||||
sendErrorEvent(-100, "onRenderFail")
|
||||
sendBroadcastEvent(nativeExpressADView, AdEventAction.onAdError)
|
||||
}
|
||||
|
||||
private fun sendBroadcastEvent(adView: NativeExpressADView, event: String) {
|
||||
val intent = Intent()
|
||||
intent.setAction(UnionADConfig.KEY_FEED_VIEW + "_" + adView.hashCode())
|
||||
intent.putExtra("event", event)
|
||||
LocalBroadcastManager.getInstance(activity!!).sendBroadcast(intent)
|
||||
}
|
||||
|
||||
override fun onRenderSuccess(nativeExpressADView: NativeExpressADView) {
|
||||
Log.i(TAG, "onRenderSuccess")
|
||||
sendEvent(AdEventAction.onAdPresent)
|
||||
sendBroadcastEvent(nativeExpressADView, AdEventAction.onAdPresent)
|
||||
}
|
||||
|
||||
override fun onADExposure(nativeExpressADView: NativeExpressADView?) {
|
||||
Log.i(TAG, "onADExposure")
|
||||
sendEvent(AdEventAction.onAdExposure)
|
||||
}
|
||||
|
||||
override fun onADClicked(nativeExpressADView: NativeExpressADView?) {
|
||||
Log.i(TAG, "onADClicked")
|
||||
sendEvent(AdEventAction.onAdClicked)
|
||||
}
|
||||
|
||||
override fun onADClosed(nativeExpressADView: NativeExpressADView) {
|
||||
Log.i(TAG, "onADClosed")
|
||||
sendEvent(AdEventAction.onAdClosed)
|
||||
sendBroadcastEvent(nativeExpressADView, AdEventAction.onAdClosed)
|
||||
}
|
||||
|
||||
override fun onADLeftApplication(p0: NativeExpressADView?) {}
|
||||
|
||||
override fun loadAd(call: MethodCall?) {
|
||||
// 获取请求模板广告素材的尺寸
|
||||
val width: Int = call!!.argument("width")!!
|
||||
val height: Int = call.argument("height")!!
|
||||
val count: Int = call.argument("count")!!
|
||||
val ad = NativeExpressAD(activity, ADSize(width, height), posId, this)
|
||||
ad.loadAD(count)
|
||||
}
|
||||
}
|
@@ -1,57 +0,0 @@
|
||||
package com.example.union_ad_ssgf.load
|
||||
|
||||
import com.qq.e.ads.nativ.NativeExpressADView
|
||||
|
||||
|
||||
/**
|
||||
* 信息流广告管理
|
||||
*/
|
||||
class FeedAdManager {
|
||||
private val TAG = FeedAdManager::class.java.getSimpleName()
|
||||
|
||||
companion object {
|
||||
// 信息流广告管理类对象
|
||||
private var _instance: FeedAdManager? = null
|
||||
|
||||
@Synchronized
|
||||
fun getInstance(): FeedAdManager? {
|
||||
if (_instance == null) {
|
||||
synchronized(FeedAdManager::class.java) { _instance = FeedAdManager() }
|
||||
}
|
||||
return _instance
|
||||
}
|
||||
}
|
||||
|
||||
// 信息流广告列表
|
||||
private val feedAdList: MutableMap<Int, NativeExpressADView> = HashMap()
|
||||
|
||||
/**
|
||||
* 添加广告渲染对象
|
||||
*
|
||||
* @param key 广告缓存id
|
||||
* @param ad 广告渲染对象
|
||||
*/
|
||||
fun putAd(key: Int, ad: NativeExpressADView) {
|
||||
feedAdList[key] = ad
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取广告渲染对象
|
||||
*
|
||||
* @param key 广告缓存 id
|
||||
* @return 广告渲染对象
|
||||
*/
|
||||
fun getAd(key: Int): NativeExpressADView? {
|
||||
return feedAdList[key]
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除广告渲染对象
|
||||
*
|
||||
* @param key 广告缓存id
|
||||
* @return 广告渲染对象
|
||||
*/
|
||||
fun removeAd(key: Int): NativeExpressADView? {
|
||||
return feedAdList.remove(key)
|
||||
}
|
||||
}
|
@@ -1,97 +0,0 @@
|
||||
package com.example.union_ad_ssgf.page
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import com.qq.e.ads.banner2.UnifiedBannerADListener
|
||||
import com.qq.e.ads.banner2.UnifiedBannerView
|
||||
import com.qq.e.comm.util.AdError
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.platform.PlatformView
|
||||
import java.util.Locale
|
||||
|
||||
import android.util.Log;
|
||||
import com.example.union_ad_ssgf.PluginDelegate
|
||||
import com.example.union_ad_ssgf.event.AdEventAction
|
||||
|
||||
class AdBannerView(
|
||||
context: Context,
|
||||
private var id: Int,
|
||||
creationParams: Map<String, Any>?,
|
||||
private var pluginDelegate: PluginDelegate?
|
||||
) : BaseAdPage(), PlatformView, UnifiedBannerADListener {
|
||||
private val TAG = AdBannerView::class.java.getSimpleName()
|
||||
private var frameLayout = FrameLayout(context)
|
||||
private var bv: UnifiedBannerView? = null
|
||||
private var params = creationParams
|
||||
|
||||
init {
|
||||
val call = MethodCall("AdBannerView", creationParams)
|
||||
showAd(this.pluginDelegate!!.activity, id, call);
|
||||
}
|
||||
|
||||
override fun loadAd(call: MethodCall?) {
|
||||
// 获取轮播时间间隔参数
|
||||
val interval = params!!["interval"] as Int
|
||||
// 加载广告 Banner
|
||||
bv = UnifiedBannerView(activity, posId, this)
|
||||
frameLayout!!.addView(bv)
|
||||
// 设置轮播时间间隔
|
||||
bv!!.setRefresh(interval)
|
||||
bv!!.loadAD()
|
||||
}
|
||||
|
||||
override fun getView(): View? {
|
||||
return frameLayout;
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
disposeAd();
|
||||
}
|
||||
|
||||
override fun onNoAD(error: AdError) {
|
||||
val msg = java.lang.String.format(
|
||||
Locale.getDefault(), "onNoAD, error code: %d, error msg: %s",
|
||||
error.getErrorCode(), error.getErrorMsg()
|
||||
)
|
||||
Log.e(TAG, msg)
|
||||
sendErrorEvent(error.getErrorCode(), error.getErrorMsg())
|
||||
disposeAd()
|
||||
}
|
||||
|
||||
override fun onADReceive() {
|
||||
Log.i(TAG, "onADReceive");
|
||||
sendEvent(AdEventAction.onAdLoaded);
|
||||
}
|
||||
|
||||
override fun onADExposure() {
|
||||
Log.i(TAG, "onADExposure");
|
||||
sendEvent(AdEventAction.onAdExposure);
|
||||
}
|
||||
|
||||
override fun onADClosed() {
|
||||
Log.i(TAG, "onADClosed");
|
||||
sendEvent(AdEventAction.onAdClosed);
|
||||
disposeAd();
|
||||
}
|
||||
|
||||
override fun onADClicked() {
|
||||
Log.i(TAG, "onADClicked");
|
||||
sendEvent(AdEventAction.onAdClicked);
|
||||
}
|
||||
|
||||
override fun onADLeftApplication() {
|
||||
Log.i(TAG, "onADLeftApplication");
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁广告
|
||||
*/
|
||||
private fun disposeAd() {
|
||||
frameLayout!!.removeAllViews()
|
||||
if (bv != null) {
|
||||
bv!!.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,91 +0,0 @@
|
||||
package com.example.union_ad_ssgf.page
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import com.example.union_ad_ssgf.PluginDelegate
|
||||
import com.example.union_ad_ssgf.config.UnionADConfig
|
||||
import com.example.union_ad_ssgf.event.AdEventAction
|
||||
import com.example.union_ad_ssgf.load.FeedAdManager
|
||||
import com.qq.e.ads.nativ.NativeExpressADView
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.platform.PlatformView
|
||||
|
||||
class AdFeedView(
|
||||
context: Context,
|
||||
private var id: Int,
|
||||
private var creationParams: Map<String, Any>?,
|
||||
private var pluginDelegate: PluginDelegate?
|
||||
) : BaseAdPage(), PlatformView {
|
||||
private var frameLayout = FrameLayout(context)
|
||||
private var fad: NativeExpressADView? = null
|
||||
private var receiver: BroadcastReceiver? = null
|
||||
|
||||
init {
|
||||
val call = MethodCall("AdFeedView", creationParams)
|
||||
showAd(this.pluginDelegate!!.activity, id, call)
|
||||
}
|
||||
|
||||
override fun loadAd(call: MethodCall?) {
|
||||
val key = adId!!.toInt()
|
||||
regReceiver(key)
|
||||
fad = FeedAdManager.getInstance()!!.getAd(key)
|
||||
if (fad != null) {
|
||||
if (frameLayout.childCount > 0) {
|
||||
frameLayout.removeAllViews()
|
||||
}
|
||||
fad!!.render()
|
||||
frameLayout.addView(fad)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getView(): View? {
|
||||
return frameLayout
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
removeAd()
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册广播
|
||||
* @param key key
|
||||
*/
|
||||
private fun regReceiver(key: Int) {
|
||||
// 注册广播
|
||||
receiver =
|
||||
object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val event = intent.getStringExtra("event")
|
||||
sendEvent(event)
|
||||
if (AdEventAction.onAdClosed == event || AdEventAction.onAdError == event) {
|
||||
this@AdFeedView.disposeAd()
|
||||
}
|
||||
}
|
||||
}
|
||||
val intentFilter = IntentFilter(UnionADConfig.KEY_FEED_VIEW + "_" + key)
|
||||
LocalBroadcastManager.getInstance(activity!!).registerReceiver(receiver!!, intentFilter)
|
||||
}
|
||||
|
||||
/** 移除广告 */
|
||||
private fun removeAd() {
|
||||
frameLayout!!.removeAllViews()
|
||||
// 注销广播
|
||||
if (receiver != null) {
|
||||
LocalBroadcastManager.getInstance(activity!!).unregisterReceiver(receiver!!)
|
||||
}
|
||||
}
|
||||
|
||||
/** 销毁广告 */
|
||||
private fun disposeAd() {
|
||||
removeAd()
|
||||
FeedAdManager.getInstance()!!.removeAd(adId!!.toInt())
|
||||
if (fad != null) {
|
||||
fad!!.destroy()
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,187 +0,0 @@
|
||||
package com.example.union_ad_ssgf.page
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import com.example.union_ad_ssgf.R
|
||||
import com.example.union_ad_ssgf.config.UnionADConfig
|
||||
import com.example.union_ad_ssgf.event.AdErrorEvent
|
||||
import com.example.union_ad_ssgf.event.AdEvent
|
||||
import com.example.union_ad_ssgf.event.AdEventAction
|
||||
import com.example.union_ad_ssgf.event.AdEventHandler
|
||||
import com.qq.e.ads.splash.SplashAD
|
||||
import com.qq.e.ads.splash.SplashADListener
|
||||
import com.qq.e.comm.util.AdError
|
||||
|
||||
|
||||
/**
|
||||
* 开屏广告
|
||||
*/
|
||||
class AdSplashActivity() : AppCompatActivity(), SplashADListener {
|
||||
private val TAG = AdSplashActivity::class.java.getSimpleName()
|
||||
/**
|
||||
* 广告容器
|
||||
*/
|
||||
private var ad_container: FrameLayout? = null
|
||||
|
||||
/**
|
||||
* 自定义品牌 logo
|
||||
*/
|
||||
private var ad_logo: AppCompatImageView? = null
|
||||
|
||||
/**
|
||||
* 广告位 id
|
||||
*/
|
||||
private var posId: String? = null
|
||||
|
||||
/**
|
||||
* 是否全屏
|
||||
*/
|
||||
private var isFullScreen = false
|
||||
|
||||
/**
|
||||
* 开屏广告
|
||||
*/
|
||||
private var splashAD: SplashAD? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_ad_splash);
|
||||
initView();
|
||||
initData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化View
|
||||
*/
|
||||
private fun initView() {
|
||||
ad_container = findViewById<FrameLayout>(R.id.splash_ad_container)
|
||||
ad_logo = findViewById<AppCompatImageView>(R.id.splash_ad_logo)
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化数据
|
||||
*/
|
||||
private fun initData() {
|
||||
// 获取参数
|
||||
posId = intent.getStringExtra(UnionADConfig.KEY_POSID)
|
||||
val logo = intent.getStringExtra(UnionADConfig.KEY_LOGO)
|
||||
val fetchDelay = intent.getDoubleExtra(UnionADConfig.KEY_FETCH_DELAY, 0.0)
|
||||
val absFetchDelay = (fetchDelay * 1000).toInt()
|
||||
isFullScreen = TextUtils.isEmpty(logo)
|
||||
// 创建开屏广告
|
||||
splashAD = SplashAD(this, posId, this, absFetchDelay)
|
||||
if (isFullScreen) {
|
||||
// logo 为空则加载全屏广告
|
||||
ad_logo!!.setVisibility(View.GONE)
|
||||
splashAD!!.fetchFullScreenAdOnly()
|
||||
} else {
|
||||
// 加载 logo
|
||||
val resId: Int = getMipmapId(logo!!)
|
||||
if (resId > 0) {
|
||||
ad_logo!!.setVisibility(View.VISIBLE)
|
||||
ad_logo!!.setImageResource(resId)
|
||||
}
|
||||
// 加载广告
|
||||
splashAD!!.fetchAdOnly()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 广告关闭时调用,可能是用户关闭或者展示时间到。此时一般需要跳过开屏的 Activity,进入应用内容页面
|
||||
*/
|
||||
override fun onADDismissed() {
|
||||
Log.d(TAG, "onADDismissed")
|
||||
finishPage()
|
||||
AdEventHandler.getInstance()?.sendEvent(AdEvent(posId, "", 1, AdEventAction.onAdClosed));
|
||||
}
|
||||
|
||||
/**
|
||||
* 广告加载或展示过程中出错,AdError中包含了错误码和错误描述,具体错误码内容可参考错误码部分
|
||||
*/
|
||||
override fun onNoAD(adError: AdError) {
|
||||
Log.d(TAG, "onNoAD adError:" + adError.errorMsg);
|
||||
finishPage()
|
||||
AdEventHandler.getInstance()!!
|
||||
.sendEvent(AdErrorEvent(posId, "", 1, adError.errorCode, adError.errorMsg));
|
||||
}
|
||||
|
||||
/**
|
||||
* 广告成功展示时调用,成功展示不等于有效展示(比如广告容器高度不够)
|
||||
*/
|
||||
override fun onADPresent() {
|
||||
Log.d(TAG, "onADPresent")
|
||||
AdEventHandler.getInstance()!!.sendEvent(AdEvent(posId, "", 1, AdEventAction.onAdPresent))
|
||||
}
|
||||
|
||||
/**
|
||||
* 广告被点击时调用,不代表满足计费条件(如点击时网络异常)
|
||||
*/
|
||||
override fun onADClicked() {
|
||||
Log.d(TAG, "onADClicked")
|
||||
AdEventHandler.getInstance()!!.sendEvent(AdEvent(posId, "", 1, AdEventAction.onAdClicked))
|
||||
}
|
||||
|
||||
/**
|
||||
* 倒计时回调,返回广告还将被展示的剩余时间,单位是 ms
|
||||
*/
|
||||
override fun onADTick(millisUntilFinished: Long) {
|
||||
Log.d(TAG, "onADTick millisUntilFinished:$millisUntilFinished");
|
||||
}
|
||||
|
||||
/**
|
||||
* 广告曝光时调用
|
||||
*/
|
||||
override fun onADExposure() {
|
||||
Log.d(TAG, "onADExposure")
|
||||
AdEventHandler.getInstance()!!.sendEvent(AdEvent(posId, "", 1, AdEventAction.onAdExposure))
|
||||
}
|
||||
|
||||
/**
|
||||
* 广告加载成功的回调,在fetchAdOnly的情况下,表示广告拉取成功可以显示了。
|
||||
* 广告需要在SystemClock.elapsedRealtime <expireTimestamp前展示,否则在showAd时会返回广告超时错误。
|
||||
*/
|
||||
override fun onADLoaded(expireTimestamp: Long) {
|
||||
Log.d(TAG, "onADLoaded expireTimestamp:$expireTimestamp")
|
||||
AdEventHandler.getInstance()!!.sendEvent(AdEvent(posId, "", 1, AdEventAction.onAdLoaded))
|
||||
if (isFullScreen) {
|
||||
splashAD!!.showFullScreenAd(ad_container)
|
||||
} else {
|
||||
splashAD!!.showAd(ad_container)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成广告,退出开屏页面
|
||||
*/
|
||||
private fun finishPage() {
|
||||
finish()
|
||||
// 设置退出动画
|
||||
overridePendingTransition(0, android.R.anim.fade_out)
|
||||
}
|
||||
|
||||
/**
|
||||
* 开屏页一定要禁止用户对返回按钮的控制,否则将可能导致用户手动退出了App而广告无法正常曝光和计费
|
||||
*/
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
return if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_HOME) {
|
||||
true
|
||||
} else super.onKeyDown(keyCode, event)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片资源的id
|
||||
* @param resName 资源名称,不带后缀
|
||||
* @return 返回资源id
|
||||
*/
|
||||
@SuppressLint("DiscouragedApi")
|
||||
private fun getMipmapId(resName: String): Int {
|
||||
return getResources().getIdentifier(resName, "mipmap", packageName)
|
||||
}
|
||||
}
|
@@ -1,69 +0,0 @@
|
||||
package com.example.union_ad_ssgf.page
|
||||
|
||||
import android.app.Activity
|
||||
import com.example.union_ad_ssgf.config.UnionADConfig
|
||||
import com.example.union_ad_ssgf.event.AdErrorEvent
|
||||
import com.example.union_ad_ssgf.event.AdEvent
|
||||
import com.example.union_ad_ssgf.event.AdEventHandler
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
|
||||
abstract class BaseAdPage {
|
||||
// 上下文
|
||||
protected var activity: Activity? = null
|
||||
|
||||
// 广告位 id
|
||||
protected var posId: String? = null
|
||||
|
||||
// 广告位 id
|
||||
protected var adId: String? = null
|
||||
|
||||
// 广告定位ID
|
||||
protected var viewId: Int? = null
|
||||
|
||||
/**
|
||||
* 显示广告
|
||||
*
|
||||
* @param activity 上下文
|
||||
* @param call 方法调用
|
||||
*/
|
||||
fun showAd(activity: Activity?, viewId: Int?, call: MethodCall) {
|
||||
this.activity = activity
|
||||
this.viewId = viewId
|
||||
this.posId = call.argument<String>(UnionADConfig.KEY_POSID)
|
||||
this.adId = call.argument<String>(UnionADConfig.KEY_ADID)
|
||||
loadAd(call)
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载广告
|
||||
* @param call 方法调用
|
||||
*/
|
||||
abstract fun loadAd(call: MethodCall?)
|
||||
|
||||
/**
|
||||
* 发送广告事件
|
||||
* @param event 广告事件
|
||||
*/
|
||||
protected fun sendEvent(event: AdEvent?) {
|
||||
AdEventHandler.getInstance()?.sendEvent(event)
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送广告事件
|
||||
*
|
||||
* @param action 操作
|
||||
*/
|
||||
protected fun sendEvent(action: String?) {
|
||||
sendEvent(AdEvent(posId, adId, viewId, action))
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送错误事件
|
||||
*
|
||||
* @param errCode 错误码
|
||||
* @param errMsg 错误事件
|
||||
*/
|
||||
protected fun sendErrorEvent(errCode: Int, errMsg: String?) {
|
||||
sendEvent(AdErrorEvent(posId, adId, viewId, errCode, errMsg))
|
||||
}
|
||||
}
|
@@ -1,29 +0,0 @@
|
||||
package com.example.union_ad_ssgf.page
|
||||
|
||||
import android.content.Context
|
||||
import com.example.union_ad_ssgf.PluginDelegate
|
||||
import com.example.union_ad_ssgf.config.UnionADConfig
|
||||
import io.flutter.plugin.common.StandardMessageCodec
|
||||
import io.flutter.plugin.platform.PlatformView
|
||||
import io.flutter.plugin.platform.PlatformViewFactory
|
||||
|
||||
/**
|
||||
* View 名字
|
||||
* @param viewName: 插件代理类
|
||||
*/
|
||||
class NativeViewFactory(
|
||||
private val viewName: String,
|
||||
private val pluginDelegate: PluginDelegate
|
||||
) :
|
||||
PlatformViewFactory(StandardMessageCodec.INSTANCE) {
|
||||
|
||||
override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
|
||||
val creationParams = args as Map<String, Object>
|
||||
return if (viewName == UnionADConfig.KEY_BANNER_VIEW) {
|
||||
AdBannerView(context, viewId, creationParams, pluginDelegate)
|
||||
} else {
|
||||
AdFeedView(context, viewId, creationParams, pluginDelegate)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -1,138 +0,0 @@
|
||||
package com.example.union_ad_ssgf.page
|
||||
|
||||
import android.util.Log
|
||||
import com.example.union_ad_ssgf.event.AdEventAction
|
||||
import com.example.union_ad_ssgf.event.AdRewardEvent
|
||||
import com.qq.e.ads.rewardvideo.RewardVideoAD
|
||||
import com.qq.e.ads.rewardvideo.RewardVideoADListener
|
||||
import com.qq.e.ads.rewardvideo.ServerSideVerificationOptions
|
||||
import com.qq.e.comm.util.AdError
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* 激励视频页面
|
||||
*/
|
||||
class RewardVideoPage() : BaseAdPage(), RewardVideoADListener {
|
||||
private val TAG = RewardVideoPage::class.java.getSimpleName()
|
||||
/**
|
||||
* 激励广告对象
|
||||
*/
|
||||
private lateinit var rewardVideoAD: RewardVideoAD
|
||||
|
||||
/**
|
||||
* 设置激励视频服务端验证的自定义信息
|
||||
*/
|
||||
private lateinit var customData: String
|
||||
|
||||
/**
|
||||
* 设置服务端验证的用户信息
|
||||
*/
|
||||
private lateinit var userId: String
|
||||
|
||||
/**
|
||||
* 加载激励视频广告,加载成功则调用回调RewardVideoADListener.onADLoad(),
|
||||
* 加载失败则会调用RewardVideoADListener.onError(AdError error)
|
||||
*/
|
||||
override fun loadAd(call: MethodCall?) {
|
||||
/**
|
||||
* 获取对应参数
|
||||
*/
|
||||
val playMuted: Boolean = call!!.argument("playMuted")!!
|
||||
customData = call.argument("customData")!!
|
||||
userId = call.argument("userId")!!
|
||||
|
||||
// 1. 初始化激励视频广告
|
||||
rewardVideoAD = RewardVideoAD(activity, posId, this, !playMuted)
|
||||
|
||||
// 2. 设置服务端验证信息
|
||||
val options = ServerSideVerificationOptions.Builder()
|
||||
.setCustomData(customData)
|
||||
.setUserId(userId) // 设置服务端验证的用户信息
|
||||
.build()
|
||||
|
||||
// 3. 服务端验证信息
|
||||
rewardVideoAD.setServerSideVerificationOptions(options)
|
||||
|
||||
// 4. 加载激励视频广告
|
||||
rewardVideoAD.loadAD()
|
||||
}
|
||||
|
||||
/**
|
||||
* 广告加载成功,可在此回调后进行广告展示
|
||||
**/
|
||||
override fun onADLoad() {
|
||||
Log.i(TAG, "onADLoad")
|
||||
rewardVideoAD.showAD()
|
||||
sendEvent(AdEventAction.onAdLoaded)
|
||||
}
|
||||
|
||||
/**
|
||||
* 视频素材缓存成功,可在此回调后进行广告展示
|
||||
*/
|
||||
override fun onVideoCached() {
|
||||
Log.i(TAG, "onVideoCached")
|
||||
}
|
||||
|
||||
/**
|
||||
* 激励视频广告页面展示
|
||||
*/
|
||||
override fun onADShow() {
|
||||
Log.i(TAG, "onADShow")
|
||||
sendEvent(AdEventAction.onAdPresent)
|
||||
}
|
||||
|
||||
/**
|
||||
* 激励视频广告曝光
|
||||
*/
|
||||
override fun onADExpose() {
|
||||
Log.i(TAG, "onADExpose")
|
||||
sendEvent(AdEventAction.onAdExposure)
|
||||
}
|
||||
|
||||
/**
|
||||
* 激励视频触发激励(观看视频大于一定时长或者视频播放完毕)
|
||||
* @param map 若选择了服务端验证,可以通过 ServerSideVerificationOptions#TRANS_ID 键从 map 中获取此次交易的 id;若未选择服务端验证,则不需关注 map 参数。
|
||||
*/
|
||||
override fun onReward(map: MutableMap<String, Any>?) {
|
||||
val transId = map!![ServerSideVerificationOptions.TRANS_ID] as String?
|
||||
Log.i(TAG, "onReward $transId") // 获取服务端验证的唯一 ID
|
||||
sendEvent(AdRewardEvent(posId,"",1,AdEventAction.onAdReward, transId!!, customData, userId))
|
||||
}
|
||||
|
||||
/**
|
||||
* 激励视频广告被点击
|
||||
*/
|
||||
override fun onADClick() {
|
||||
Log.i(TAG, "onADClick")
|
||||
sendEvent(AdEventAction.onAdClicked)
|
||||
}
|
||||
|
||||
/**
|
||||
* 激励视频广告被关闭
|
||||
*/
|
||||
override fun onVideoComplete() {
|
||||
Log.i(TAG, "onVideoComplete")
|
||||
}
|
||||
|
||||
/**
|
||||
* 激励视频广告被关闭
|
||||
*/
|
||||
override fun onADClose() {
|
||||
Log.i(TAG, "onADClose")
|
||||
sendEvent(AdEventAction.onAdClosed)
|
||||
}
|
||||
|
||||
/**
|
||||
* 广告流程出错
|
||||
*/
|
||||
override fun onError(error: AdError) {
|
||||
val msg = java.lang.String.format(
|
||||
Locale.getDefault(),
|
||||
"onError, error code: %d, error msg: %s",
|
||||
error.errorCode, error.errorMsg,
|
||||
)
|
||||
Log.i(TAG, "onError, adError=$msg")
|
||||
sendErrorEvent(error.errorCode, error.errorMsg)
|
||||
}
|
||||
}
|
@@ -0,0 +1,182 @@
|
||||
package com.example.union_ad_ssgf.rewardvideoad
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import com.example.union_ad_ssgf.DownloadConfirmHelper
|
||||
import com.example.union_ad_ssgf.LogUtil
|
||||
import com.example.union_ad_ssgf.UnionAdSsgfEventPlugin
|
||||
import com.qq.e.ads.rewardvideo.RewardVideoAD
|
||||
import com.qq.e.ads.rewardvideo.RewardVideoADListener
|
||||
import com.qq.e.ads.rewardvideo.ServerSideVerificationOptions
|
||||
import com.qq.e.comm.pi.IBidding
|
||||
import com.qq.e.comm.util.AdError
|
||||
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
object RewardVideoAd {
|
||||
private val TAG = "RewardVideoAd"
|
||||
|
||||
private lateinit var context: Context
|
||||
private var rewardVideoAD: RewardVideoAD? = null
|
||||
|
||||
private var codeId: String? = null
|
||||
private var userID: String = ""
|
||||
private var rewardName: String = ""
|
||||
private var rewardAmount: Int = 0
|
||||
private var customData: String = ""
|
||||
private var downloadConfirm: Boolean = false
|
||||
private var videoMuted: Boolean = false
|
||||
|
||||
//是否开启竞价
|
||||
private var isBidding: Boolean = false
|
||||
|
||||
fun init(context: Context, params: Map<*, *>) {
|
||||
this.context = context
|
||||
this.codeId = params["androidId"] as String
|
||||
this.userID = params["userID"] as String
|
||||
this.rewardName = params["rewardName"] as String
|
||||
this.rewardAmount = params["rewardAmount"] as Int
|
||||
this.customData = params["customData"] as String
|
||||
this.downloadConfirm = params["downloadConfirm"] as Boolean
|
||||
this.isBidding = params["isBidding"] as Boolean
|
||||
this.videoMuted = params["videoMuted"] as Boolean
|
||||
loadRewardVideoAd()
|
||||
}
|
||||
|
||||
private fun loadRewardVideoAd() {
|
||||
rewardVideoAD = RewardVideoAD(context, codeId, rewardVideoADListener,videoMuted)
|
||||
val options = ServerSideVerificationOptions.Builder()
|
||||
.setUserId(userID)
|
||||
.setCustomData(customData)
|
||||
.build()
|
||||
rewardVideoAD?.setServerSideVerificationOptions(options)
|
||||
rewardVideoAD?.loadAD()
|
||||
}
|
||||
|
||||
fun showAd(params: Map<*, *>) {
|
||||
if (rewardVideoAD == null) {
|
||||
val map: MutableMap<String, Any?> =
|
||||
mutableMapOf("adType" to "rewardAd", "onAdMethod" to "onUnReady")
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
return
|
||||
}
|
||||
//是否为竞价模式
|
||||
if (isBidding) {
|
||||
val isSuccess: Boolean = params["isSuccess"] as Boolean
|
||||
//是否成功
|
||||
if (isSuccess) {
|
||||
rewardVideoAD?.sendWinNotification(
|
||||
mutableMapOf(
|
||||
//对应值为竞胜出价,类型为Integer
|
||||
IBidding.EXPECT_COST_PRICE to params["expectCostPrice"],
|
||||
//对应值为最大竞败方出价,类型为Integer
|
||||
IBidding.HIGHEST_LOSS_PRICE to params["highestLossPrice"],
|
||||
)
|
||||
)
|
||||
rewardVideoAD?.showAD()
|
||||
} else {
|
||||
rewardVideoAD?.sendLossNotification(
|
||||
mutableMapOf(
|
||||
//值为本次竞胜方出价(单位:分),类型为Integer。选填
|
||||
IBidding.WIN_PRICE to params["winPrice"],
|
||||
//值为优量汇广告竞败原因,类型为Integer。必填
|
||||
IBidding.LOSS_REASON to params["lossReason"],
|
||||
//值为本次竞胜方渠道ID,类型为Integer。必填。
|
||||
IBidding.ADN_ID to params["adnId"],
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
rewardVideoAD?.showAD()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private var rewardVideoADListener = object : RewardVideoADListener {
|
||||
override fun onADLoad() {
|
||||
LogUtil.e("$TAG 激励广告加载成功")
|
||||
if (downloadConfirm) {
|
||||
rewardVideoAD?.setDownloadConfirmListener(DownloadConfirmHelper.DOWNLOAD_CONFIRM_LISTENER)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onVideoCached() {
|
||||
LogUtil.e("$TAG 激励广告视频素材缓存成功")
|
||||
if (isBidding) {
|
||||
val map: MutableMap<String, Any?> =
|
||||
mutableMapOf(
|
||||
"adType" to "rewardAd",
|
||||
"onAdMethod" to "onECPM",
|
||||
"ecpmLevel" to rewardVideoAD?.ecpmLevel,
|
||||
"ecpm" to rewardVideoAD?.ecpm
|
||||
)
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
} else {
|
||||
val map: MutableMap<String, Any?> =
|
||||
mutableMapOf("adType" to "rewardAd", "onAdMethod" to "onReady")
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onADShow() {
|
||||
LogUtil.e("$TAG 激励视频广告页面展示")
|
||||
val map: MutableMap<String, Any?> =
|
||||
mutableMapOf("adType" to "rewardAd", "onAdMethod" to "onShow")
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
}
|
||||
|
||||
override fun onADExpose() {
|
||||
LogUtil.e("$TAG 激励视频广告曝光")
|
||||
val map: MutableMap<String, Any?> =
|
||||
mutableMapOf("adType" to "rewardAd", "onAdMethod" to "onExpose")
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
}
|
||||
|
||||
override fun onReward(p0: MutableMap<String, Any>?) {
|
||||
LogUtil.e("$TAG 激励视频广告激励发放 $p0")
|
||||
|
||||
val map: MutableMap<String, Any?> = mutableMapOf(
|
||||
"adType" to "rewardAd",
|
||||
"onAdMethod" to "onVerify",
|
||||
"transId" to p0!!["transId"],
|
||||
"rewardName" to rewardName,
|
||||
"rewardAmount" to rewardAmount
|
||||
)
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
}
|
||||
|
||||
override fun onADClick() {
|
||||
LogUtil.e("$TAG 激励视频广告被点击")
|
||||
val map: MutableMap<String, Any?> =
|
||||
mutableMapOf("adType" to "rewardAd", "onAdMethod" to "onClick")
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
}
|
||||
|
||||
override fun onVideoComplete() {
|
||||
LogUtil.e("$TAG 激励视频广告视频素材播放完毕")
|
||||
val map: MutableMap<String, Any?> =
|
||||
mutableMapOf("adType" to "rewardAd", "onAdMethod" to "onFinish")
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
}
|
||||
|
||||
override fun onADClose() {
|
||||
LogUtil.e("$TAG 激励视频广告被关闭")
|
||||
val map: MutableMap<String, Any?> =
|
||||
mutableMapOf("adType" to "rewardAd", "onAdMethod" to "onClose")
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
rewardVideoAD = null
|
||||
}
|
||||
|
||||
override fun onError(p0: AdError?) {
|
||||
LogUtil.e("$TAG 广告流程出错 ${p0?.errorCode} ${p0?.errorMsg}")
|
||||
val map: MutableMap<String, Any?> = mutableMapOf(
|
||||
"adType" to "rewardAd",
|
||||
"onAdMethod" to "onFail",
|
||||
"code" to p0?.errorCode,
|
||||
"message" to p0?.errorMsg
|
||||
)
|
||||
UnionAdSsgfEventPlugin.sendContent(map)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,151 @@
|
||||
package com.example.union_ad_ssgf.splashad
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import com.example.union_ad_ssgf.DownloadConfirmHelper
|
||||
import com.example.union_ad_ssgf.UnionAdSsgfConfig
|
||||
import com.example.union_ad_ssgf.LogUtil
|
||||
import com.qq.e.ads.splash.SplashAD
|
||||
import com.qq.e.ads.splash.SplashADListener
|
||||
import com.qq.e.comm.pi.IBidding
|
||||
import com.qq.e.comm.util.AdError
|
||||
import io.flutter.plugin.common.BinaryMessenger
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugin.platform.PlatformView
|
||||
|
||||
|
||||
internal class SplashAdView(
|
||||
var activity: Activity,
|
||||
messenger: BinaryMessenger,
|
||||
id: Int,
|
||||
params: Map<String?, Any?>
|
||||
) :
|
||||
PlatformView, SplashADListener , MethodChannel.MethodCallHandler {
|
||||
|
||||
private var mContainer: FrameLayout? = null
|
||||
private var channel: MethodChannel?
|
||||
|
||||
private var splashAD: SplashAD? = null
|
||||
|
||||
//广告所需参数
|
||||
private var codeId: String = params["androidId"] as String
|
||||
private var fetchDelay: Int = params["fetchDelay"] as Int
|
||||
private var downloadConfirm: Boolean = params["downloadConfirm"] as Boolean
|
||||
//是否开启竞价
|
||||
private var isBidding: Boolean = params["isBidding"] as Boolean
|
||||
|
||||
init {
|
||||
mContainer = FrameLayout(activity)
|
||||
mContainer?.layoutParams?.width = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
mContainer?.layoutParams?.height = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
channel = MethodChannel(messenger, UnionAdSsgfConfig.splashAdView + "_" + id)
|
||||
channel?.setMethodCallHandler(this)
|
||||
loadSplashAd()
|
||||
}
|
||||
|
||||
private fun loadSplashAd() {
|
||||
splashAD = SplashAD(activity, codeId, this, fetchDelay)
|
||||
mContainer?.removeAllViews()
|
||||
splashAD?.fetchAdOnly()
|
||||
}
|
||||
|
||||
|
||||
override fun getView(): View {
|
||||
return mContainer!!
|
||||
}
|
||||
|
||||
/*************开屏广告回调******************/
|
||||
//广告关闭时调用,可能是用户关闭或者展示时间到。此时一般需要跳过开屏的 Activity,进入应用内容页面
|
||||
override fun onADDismissed() {
|
||||
LogUtil.e("开屏广告关闭")
|
||||
channel?.invokeMethod("onClose", "")
|
||||
}
|
||||
|
||||
//广告加载失败,error 对象包含了错误码和错误信息,错误码的详细内容可以参考文档第5章
|
||||
override fun onNoAD(p0: AdError?) {
|
||||
LogUtil.e("开屏广告加载失败 ${p0?.errorCode} ${p0?.errorMsg}")
|
||||
var map: MutableMap<String, Any?> = mutableMapOf("code" to p0?.errorCode, "message" to p0?.errorMsg)
|
||||
channel?.invokeMethod("onFail", map)
|
||||
}
|
||||
|
||||
//广告成功展示时调用,成功展示不等于有效展示(比如广告容器高度不够)
|
||||
override fun onADPresent() {
|
||||
LogUtil.e("开屏广告成功展示")
|
||||
channel?.invokeMethod("onShow", "")
|
||||
}
|
||||
|
||||
//广告被点击时调用,不代表满足计费条件(如点击时网络异常)
|
||||
override fun onADClicked() {
|
||||
LogUtil.e("开屏广告被点击")
|
||||
channel?.invokeMethod("onClick", "")
|
||||
}
|
||||
|
||||
//倒计时回调,返回广告还将被展示的剩余时间,单位是 ms
|
||||
override fun onADTick(p0: Long) {
|
||||
LogUtil.e("开屏广告倒计时回调 $p0")
|
||||
channel?.invokeMethod("onADTick", p0)
|
||||
}
|
||||
|
||||
//广告曝光时调用
|
||||
override fun onADExposure() {
|
||||
LogUtil.e("开屏广告曝光")
|
||||
channel?.invokeMethod("onExpose", "")
|
||||
}
|
||||
|
||||
//广告加载成功的回调,在fetchAdOnly的情况下,
|
||||
// 表示广告拉取成功可以显示了。广告需要在SystemClock.elapsedRealtime <expireTimestamp前展示,
|
||||
// 否则在showAd时会返回广告超时错误。
|
||||
override fun onADLoaded(p0: Long) {
|
||||
LogUtil.e("开屏广告加载成功 $p0")
|
||||
if(downloadConfirm){
|
||||
splashAD?.setDownloadConfirmListener(DownloadConfirmHelper.DOWNLOAD_CONFIRM_LISTENER)
|
||||
}
|
||||
if(isBidding){
|
||||
channel?.invokeMethod("onECPM", mutableMapOf(
|
||||
"ecpmLevel" to splashAD?.ecpmLevel,
|
||||
"ecpm" to splashAD?.ecpm
|
||||
))
|
||||
}else{
|
||||
splashAD?.showAd(mContainer)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun dispose() {
|
||||
mContainer?.removeAllViews()
|
||||
mContainer = null
|
||||
}
|
||||
|
||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||
when (call.method) {
|
||||
//竞价成功
|
||||
"biddingSucceeded" -> {
|
||||
var arguments = call.arguments as Map<*, *>
|
||||
splashAD?.sendWinNotification(mutableMapOf(
|
||||
//对应值为竞胜出价,类型为Integer
|
||||
IBidding.EXPECT_COST_PRICE to arguments["expectCostPrice"],
|
||||
//对应值为最大竞败方出价,类型为Integer
|
||||
IBidding.HIGHEST_LOSS_PRICE to arguments["highestLossPrice"],
|
||||
))
|
||||
//展示banner
|
||||
splashAD?.showAd(mContainer)
|
||||
}
|
||||
//竞价失败
|
||||
"biddingFail" -> {
|
||||
var arguments = call.arguments as Map<*, *>
|
||||
splashAD?.sendLossNotification(mutableMapOf(
|
||||
//值为本次竞胜方出价(单位:分),类型为Integer。选填
|
||||
IBidding.WIN_PRICE to arguments["winPrice"],
|
||||
//值为优量汇广告竞败原因,类型为Integer。必填
|
||||
IBidding.LOSS_REASON to arguments["lossReason"],
|
||||
//值为本次竞胜方渠道ID,类型为Integer。必填。
|
||||
IBidding.ADN_ID to arguments["adnId"],
|
||||
))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
package com.example.union_ad_ssgf.splashad
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import com.example.union_ad_ssgf.bannerad.BannerAdView
|
||||
import io.flutter.plugin.common.BinaryMessenger
|
||||
import io.flutter.plugin.common.StandardMessageCodec
|
||||
import io.flutter.plugin.platform.PlatformView
|
||||
import io.flutter.plugin.platform.PlatformViewFactory
|
||||
|
||||
class SplashAdViewFactory (private val messenger: BinaryMessenger, private val activity: Activity) : PlatformViewFactory(
|
||||
StandardMessageCodec.INSTANCE) {
|
||||
|
||||
override fun create(context: Context?, viewId: Int, args: Any?): PlatformView {
|
||||
val params = args as Map<String?, Any?>
|
||||
return SplashAdView(activity,messenger, viewId, params)
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate
|
||||
android:duration="@android:integer/config_mediumAnimTime"
|
||||
android:fromXDelta="100%"
|
||||
android:interpolator="@android:anim/accelerate_interpolator"
|
||||
android:toXDelta="0">
|
||||
</translate>
|
||||
</set>
|
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate
|
||||
android:duration="@android:integer/config_mediumAnimTime"
|
||||
android:fromYDelta="100%"
|
||||
android:interpolator="@android:anim/accelerate_interpolator"
|
||||
android:toYDelta="0">
|
||||
</translate>
|
||||
</set>
|
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#FF3185FC"/>
|
||||
<corners
|
||||
android:bottomLeftRadius="22dp"
|
||||
android:bottomRightRadius="22dp"
|
||||
android:topLeftRadius="22dp"
|
||||
android:topRightRadius="22dp"/>
|
||||
</shape>
|
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@android:color/white"/>
|
||||
<corners
|
||||
android:bottomLeftRadius="8dp"
|
||||
android:bottomRightRadius="0dp"
|
||||
android:topLeftRadius="8dp"
|
||||
android:topRightRadius="0dp"/>
|
||||
</shape>
|
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@android:color/white"/>
|
||||
<corners
|
||||
android:bottomLeftRadius="0dp"
|
||||
android:bottomRightRadius="0dp"
|
||||
android:topLeftRadius="8dp"
|
||||
android:topRightRadius="8dp"/>
|
||||
</shape>
|
12
android/src/main/res/drawable/ic_download_confirm_close.xml
Normal file
12
android/src/main/res/drawable/ic_download_confirm_close.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="25dp"
|
||||
android:height="25dp"
|
||||
android:viewportWidth="25"
|
||||
android:viewportHeight="25">
|
||||
<path
|
||||
android:pathData="M24.1673,0.8327C24.753,1.4185 24.753,2.3683 24.1673,2.9541L14.6213,12.5L24.1673,22.0459C24.753,22.6317 24.753,23.5815 24.1673,24.1673C23.5815,24.753 22.6317,24.753 22.0459,24.1673L12.5,14.6213L2.9541,24.1673C2.3683,24.753 1.4185,24.753 0.8327,24.1673C0.2469,23.5815 0.247,22.6317 0.8327,22.0459L10.3787,12.5L0.8327,2.9541C0.2469,2.3683 0.247,1.4185 0.8327,0.8327C1.4185,0.247 2.3683,0.2469 2.9541,0.8327L12.5,10.3787L22.0459,0.8327C22.6317,0.247 23.5815,0.2469 24.1673,0.8327Z"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#B3B3B3"
|
||||
android:fillType="evenOdd"
|
||||
android:strokeColor="#00000000"/>
|
||||
</vector>
|
82
android/src/main/res/layout/download_confirm_dialog.xml
Normal file
82
android/src/main/res/layout/download_confirm_dialog.xml
Normal file
@@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/download_confirm_root"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingRight="20dp"
|
||||
android:background="@drawable/download_confirm_background_portrait"
|
||||
android:paddingBottom="15dp">
|
||||
|
||||
<!--顶部标题布局-->
|
||||
<RelativeLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
tools:text="Demo-APP详情"
|
||||
android:textColor="@android:color/black"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
<ImageView
|
||||
android:id="@+id/download_confirm_close"
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="15dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:src="@drawable/ic_download_confirm_close"
|
||||
/>
|
||||
</RelativeLayout>
|
||||
|
||||
<!---->
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="20dp">
|
||||
<LinearLayout
|
||||
android:id="@+id/download_confirm_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/download_confirm_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
</FrameLayout>
|
||||
<Button
|
||||
android:id="@+id/download_confirm_confirm"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="44dp"
|
||||
android:text="立即下载"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@android:color/white"
|
||||
android:background="@drawable/download_confirm_background_confirm"
|
||||
android:textStyle="bold"
|
||||
/>
|
||||
</LinearLayout>
|
||||
<ProgressBar
|
||||
android:id="@+id/download_confirm_progress_bar"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center"/>
|
||||
<Button
|
||||
android:id="@+id/download_confirm_reload_button"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:text="重新加载"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
13
android/src/main/res/values/styles.xml
Normal file
13
android/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="DownloadConfirmDialogFullScreen" parent="android:Theme.Dialog">
|
||||
<item name="android:windowFullscreen">true</item>
|
||||
</style>
|
||||
|
||||
<style name="DownloadConfirmDialogAnimationUp">
|
||||
<item name="android:windowEnterAnimation">@anim/download_confirm_dialog_slide_up</item>
|
||||
</style>
|
||||
<style name="DownloadConfirmDialogAnimationRight">
|
||||
<item name="android:windowEnterAnimation">@anim/download_confirm_dialog_slide_right_in</item>
|
||||
</style>
|
||||
</resources>
|
@@ -6,20 +6,17 @@
|
||||
// For more information about Flutter integration tests, please see
|
||||
// https://docs.flutter.dev/cookbook/testing/integration/introduction
|
||||
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import 'package:union_ad_ssgf/flutter_union_ad.dart';
|
||||
|
||||
import 'package:union_ad_ssgf/union_ad_ssgf.dart';
|
||||
// import 'package:union_ad_ssgf/f.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
testWidgets('getPlatformVersion test', (WidgetTester tester) async {
|
||||
final UnionAdSsgf plugin = UnionAdSsgf();
|
||||
final String? version = await plugin.getPlatformVersion();
|
||||
// The version string depends on the host platform running the test, so
|
||||
// just assert that some non-empty string is returned.
|
||||
expect(version?.isNotEmpty, true);
|
||||
final String version = await FlutterUnionAd.getSDKVersion();
|
||||
expect(version.isNotEmpty, true);
|
||||
});
|
||||
}
|
||||
|
@@ -2,9 +2,7 @@ import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:union_ad_ssgf/event/ad_event_handler.dart';
|
||||
import 'package:union_ad_ssgf_example/ads_config.dart';
|
||||
import 'package:union_ad_ssgf/union_ad_ssgf.dart';
|
||||
|
||||
// 结果信息
|
||||
String _result = '';
|
||||
@@ -170,24 +168,11 @@ class _HomePageState extends State<HomePage> {
|
||||
setState(() {
|
||||
_adEvent = '设置成功';
|
||||
});
|
||||
UnionAdSsgf.onEventListener((event) {
|
||||
_adEvent = 'adId:${event.adId} action:${event.action}';
|
||||
if (event is AdErrorEvent) {
|
||||
// 错误事件
|
||||
_adEvent += ' errCode:${event.errCode} errMsg:${event.errMsg}';
|
||||
} else if (event is AdRewardEvent) {
|
||||
// 激励事件
|
||||
_adEvent +=
|
||||
' transId:${event.transId} customData:${event.customData} userId:${event.userId}';
|
||||
}
|
||||
print('onEventListener:$_adEvent');
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
/// 请求应用跟踪透明度授权
|
||||
Future<void> requestIDFA() async {
|
||||
bool result = await UnionAdSsgf.requestIDFA;
|
||||
bool result = false;
|
||||
_adEvent = '请求广告标识符:$result';
|
||||
setState(() {});
|
||||
}
|
||||
@@ -195,7 +180,7 @@ class _HomePageState extends State<HomePage> {
|
||||
/// 设置个性化广告
|
||||
/// [state] 0:不限制 1:限制
|
||||
Future<void> setPersonalizedAd(int state) async {
|
||||
bool result = await UnionAdSsgf.setPersonalizedState(state);
|
||||
bool result = false;
|
||||
_adEvent = '设置个性化广告:$result';
|
||||
setState(() {});
|
||||
}
|
||||
@@ -206,33 +191,13 @@ class _HomePageState extends State<HomePage> {
|
||||
bool showFullScreenVideo = false,
|
||||
bool showRewardVideo = false,
|
||||
}) async {
|
||||
// try {
|
||||
// bool result = await UnionAdSsgf.showInterstitialAd(
|
||||
// posId,
|
||||
// showPopup: false,
|
||||
// showFullScreenVideo: showFullScreenVideo,
|
||||
// showRewardVideo: showRewardVideo,
|
||||
// autoPlayMuted: false,
|
||||
// autoPlayOnWifi: false,
|
||||
// userId: 'userId',
|
||||
// customData: 'showInterstitialAd customData',
|
||||
// );
|
||||
// _result = "展示插屏广告${result ? '成功' : '失败'}";
|
||||
// } on PlatformException catch (e) {
|
||||
// _result = "展示插屏广告失败 code:${e.code} msg:${e.message} details:${e.details}";
|
||||
// }
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
/// 展示激励视频广告
|
||||
Future<void> showRewardVideoAd() async {
|
||||
try {
|
||||
bool result = await UnionAdSsgf.showRewardVideoAd(
|
||||
AdsConfig.rewardVideoId,
|
||||
playMuted: false,
|
||||
customData: 'showRewardVideoAd customData',
|
||||
userId: 'userId',
|
||||
);
|
||||
bool result = false;
|
||||
_result = "展示激励视频广告${result ? '成功' : '失败'}";
|
||||
} on PlatformException catch (e) {
|
||||
_result =
|
||||
@@ -246,11 +211,7 @@ class _HomePageState extends State<HomePage> {
|
||||
/// [logo] 展示如果传递则展示logo,不传递不展示
|
||||
Future<void> showSplashAd([String? logo]) async {
|
||||
try {
|
||||
bool result = await UnionAdSsgf.showSplashAd(
|
||||
AdsConfig.splashId,
|
||||
logo: logo,
|
||||
fetchDelay: 3,
|
||||
);
|
||||
bool result = false;
|
||||
_result = "展示开屏广告${result ? '成功' : '失败'}";
|
||||
} on PlatformException catch (e) {
|
||||
_result = "展示开屏广告失败 code:${e.code} msg:${e.message} details:${e.details}";
|
||||
|
@@ -1,10 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:union_ad_ssgf/flutter_union_ad.dart';
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:union_ad_ssgf/union_ad_ssgf.dart';
|
||||
import 'package:union_ad_ssgf/event/ad_error_event.dart';
|
||||
import 'package:union_ad_ssgf/event/ad_reward_event.dart';
|
||||
import 'package:union_ad_ssgf_example/ads_config.dart';
|
||||
import 'package:union_ad_ssgf_example/home_page.dart';
|
||||
|
||||
void main() {
|
||||
@@ -30,31 +26,27 @@ class _MyAppState extends State<MyApp> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
return const MaterialApp(
|
||||
home: HomePage(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 初始化广告 SDK
|
||||
Future<bool> init() async {
|
||||
bool result = await UnionAdSsgf.initAd(AdsConfig.appId);
|
||||
bool result = await FlutterUnionAd.register(
|
||||
androidId: "1200310001",
|
||||
// androidId
|
||||
iosId: "1202937154",
|
||||
// iosId
|
||||
debug: true,
|
||||
// 是否显示日志log
|
||||
personalized: FlutterTencentadPersonalized.show,
|
||||
// 是否显示个性化推荐广告
|
||||
channelId: FlutterTencentadChannel.tencent, //渠道id
|
||||
);
|
||||
print("------>>>> $result");
|
||||
print("----->> result: $result");
|
||||
debugPrint("广告SDK 初始化${result ? '成功' : '失败'}");
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 设置广告监听
|
||||
Future<void> setAdEvent() async {
|
||||
UnionAdSsgf.onEventListener((event) {
|
||||
debugPrint('adId:${event.adId} action:${event.action}');
|
||||
if (event is AdErrorEvent) {
|
||||
// 错误事件
|
||||
debugPrint(' errCode:${event.errCode} errMsg:${event.errMsg}');
|
||||
} else if (event is AdRewardEvent) {
|
||||
// 激励事件
|
||||
debugPrint(
|
||||
' transId:${event.transId} userId:${event.userId} customData:${event.customData}');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import Cocoa
|
||||
import FlutterMacOS
|
||||
|
||||
@NSApplicationMain
|
||||
@main
|
||||
class AppDelegate: FlutterAppDelegate {
|
||||
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||
return true
|
||||
|
150
lib/banner/banner_ad_view.dart
Normal file
150
lib/banner/banner_ad_view.dart
Normal file
@@ -0,0 +1,150 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:union_ad_ssgf/flutter_union_ad.dart';
|
||||
|
||||
class BannerAdView extends StatefulWidget {
|
||||
final String androidId;
|
||||
final String iosId;
|
||||
final double viewWidth;
|
||||
final double viewHeight;
|
||||
final FlutterUnionAdBannerCallBack? callBack;
|
||||
final bool downloadConfirm;
|
||||
final bool isBidding;
|
||||
final FlutterUnioAdBiddingController? bidding;
|
||||
|
||||
const BannerAdView({
|
||||
Key? key,
|
||||
required this.androidId,
|
||||
required this.iosId,
|
||||
required this.viewWidth,
|
||||
required this.viewHeight,
|
||||
this.callBack,
|
||||
required this.downloadConfirm,
|
||||
required this.isBidding,
|
||||
this.bidding,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<BannerAdView> createState() => _BannerAdViewState();
|
||||
}
|
||||
|
||||
class _BannerAdViewState extends State<BannerAdView> {
|
||||
final String _viewType = "com.example.union_ad_ssgf/BannerAdView";
|
||||
|
||||
MethodChannel? _channel;
|
||||
|
||||
//广告是否显示
|
||||
bool _isShowAd = true;
|
||||
|
||||
double _width = 0;
|
||||
double _height = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_width = widget.viewWidth;
|
||||
_height = widget.viewHeight;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!_isShowAd) {
|
||||
return Container();
|
||||
}
|
||||
if (defaultTargetPlatform == TargetPlatform.android) {
|
||||
return SizedBox(
|
||||
width: _width,
|
||||
height: _height,
|
||||
child: AndroidView(
|
||||
viewType: _viewType,
|
||||
creationParams: {
|
||||
"androidId": widget.androidId,
|
||||
"viewWidth": widget.viewWidth,
|
||||
"viewHeight": widget.viewHeight,
|
||||
"downloadConfirm": widget.downloadConfirm,
|
||||
"isBidding": widget.isBidding,
|
||||
},
|
||||
onPlatformViewCreated: _registerChannel,
|
||||
creationParamsCodec: const StandardMessageCodec(),
|
||||
),
|
||||
);
|
||||
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
|
||||
return SizedBox(
|
||||
width: _width,
|
||||
height: _height,
|
||||
child: UiKitView(
|
||||
viewType: _viewType,
|
||||
creationParams: {
|
||||
"iosId": widget.iosId,
|
||||
"viewWidth": widget.viewWidth,
|
||||
"viewHeight": widget.viewHeight,
|
||||
"isBidding": widget.isBidding,
|
||||
},
|
||||
onPlatformViewCreated: _registerChannel,
|
||||
creationParamsCodec: const StandardMessageCodec(),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
|
||||
//注册cannel
|
||||
void _registerChannel(int id) {
|
||||
_channel = MethodChannel("${_viewType}_$id");
|
||||
_channel?.setMethodCallHandler(_platformCallHandler);
|
||||
widget.bidding?.init(_channel);
|
||||
}
|
||||
|
||||
//监听原生view传值
|
||||
Future<dynamic> _platformCallHandler(MethodCall call) async {
|
||||
print("${call.method} ==== ${call.arguments}");
|
||||
switch (call.method) {
|
||||
//显示广告
|
||||
case FlutterTencentadMethod.onShow:
|
||||
Map map = call.arguments;
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_width = map["width"];
|
||||
_height = map["height"];
|
||||
_isShowAd = true;
|
||||
});
|
||||
}
|
||||
widget.callBack?.onShow!();
|
||||
break;
|
||||
//广告加载失败
|
||||
case FlutterTencentadMethod.onFail:
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isShowAd = false;
|
||||
});
|
||||
}
|
||||
Map map = call.arguments;
|
||||
widget.callBack?.onFail!(map["code"], map["message"]);
|
||||
break;
|
||||
//点击
|
||||
case FlutterTencentadMethod.onClick:
|
||||
widget.callBack?.onClick!();
|
||||
break;
|
||||
//曝光
|
||||
case FlutterTencentadMethod.onExpose:
|
||||
widget.callBack?.onExpose!();
|
||||
break;
|
||||
//关闭
|
||||
case FlutterTencentadMethod.onClose:
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isShowAd = false;
|
||||
});
|
||||
}
|
||||
widget.callBack?.onClose!();
|
||||
break;
|
||||
//竞价
|
||||
case FlutterTencentadMethod.onECPM:
|
||||
Map map = call.arguments;
|
||||
widget.callBack?.onECPM!(map["ecpmLevel"], map["ecpm"]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
import 'ad_event.dart';
|
||||
|
||||
/// 广告错误事件
|
||||
class AdErrorEvent extends AdEvent {
|
||||
AdErrorEvent(
|
||||
{required this.errCode,
|
||||
required this.errMsg,
|
||||
required String adId,
|
||||
required String action})
|
||||
: super(adId: adId, action: action);
|
||||
// 错误码
|
||||
final int errCode;
|
||||
// 错误信息
|
||||
final String errMsg;
|
||||
// 解析 json 为错误事件对象
|
||||
factory AdErrorEvent.fromJson(Map<dynamic, dynamic> json) {
|
||||
return AdErrorEvent(
|
||||
errCode: json['errCode'],
|
||||
errMsg: json['errMsg'],
|
||||
adId: json['adId'],
|
||||
action: json['action'],
|
||||
);
|
||||
}
|
||||
}
|
@@ -1,30 +0,0 @@
|
||||
import 'ad_error_event.dart';
|
||||
import 'ad_event_action.dart';
|
||||
import 'ad_reward_event.dart';
|
||||
export 'ad_error_event.dart';
|
||||
export 'ad_event_action.dart';
|
||||
export 'ad_reward_event.dart';
|
||||
|
||||
/// 广告事件
|
||||
class AdEvent {
|
||||
AdEvent({required this.adId, required this.action});
|
||||
// 广告 id
|
||||
final String adId;
|
||||
// 操作
|
||||
final String action;
|
||||
|
||||
/// 解析 AdEvent
|
||||
factory AdEvent.fromJson(Map<dynamic, dynamic> json) {
|
||||
String action = json['action'];
|
||||
if (action == AdEventAction.onAdError) {
|
||||
return AdErrorEvent.fromJson(json);
|
||||
} else if (action == AdEventAction.onAdReward) {
|
||||
return AdRewardEvent.fromJson(json);
|
||||
} else {
|
||||
return AdEvent(
|
||||
adId: json['adId'],
|
||||
action: action,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,21 +0,0 @@
|
||||
/// 广告事件操作
|
||||
class AdEventAction {
|
||||
// 广告错误
|
||||
static final String onAdError = "onAdError";
|
||||
// 广告加载成功
|
||||
static final String onAdLoaded = "onAdLoaded";
|
||||
// 广告填充
|
||||
static final String onAdPresent = "onAdPresent";
|
||||
// 广告曝光
|
||||
static final String onAdExposure = "onAdExposure";
|
||||
// 广告关闭(开屏计时结束或者用户点击关闭)
|
||||
static final String onAdClosed = "onAdClosed";
|
||||
// 广告点击
|
||||
static final String onAdClicked = "onAdClicked";
|
||||
// 广告跳过
|
||||
static final String onAdSkip = "onAdSkip";
|
||||
// 广告播放或计时完毕
|
||||
static final String onAdComplete = "onAdComplete";
|
||||
// 获得广告激励
|
||||
static final String onAdReward = "onAdReward";
|
||||
}
|
@@ -1,13 +0,0 @@
|
||||
import 'ad_event.dart';
|
||||
export 'ad_event.dart';
|
||||
|
||||
/// 广告事件回调监听
|
||||
typedef OnAdEventListener = void Function(AdEvent event);
|
||||
|
||||
/// 处理广告事件
|
||||
void hanleAdEvent(dynamic data, OnAdEventListener listener) {
|
||||
if (data != null) {
|
||||
AdEvent adEvent = AdEvent.fromJson(data);
|
||||
listener.call(adEvent);
|
||||
}
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
import 'ad_event.dart';
|
||||
|
||||
/// 广告激励事件
|
||||
class AdRewardEvent extends AdEvent {
|
||||
AdRewardEvent(
|
||||
{required this.transId,
|
||||
this.customData,
|
||||
this.userId,
|
||||
required String adId,
|
||||
required String action})
|
||||
: super(adId: adId, action: action);
|
||||
// 激励服务端验证的唯一 ID
|
||||
final String transId;
|
||||
// 服务端验证的自定义信息
|
||||
final String? customData;
|
||||
// 服务端验证的用户信息
|
||||
final String? userId;
|
||||
// 解析 json 为激励事件对象
|
||||
factory AdRewardEvent.fromJson(Map<dynamic, dynamic> json) {
|
||||
return AdRewardEvent(
|
||||
adId: json['adId'],
|
||||
action: json['action'],
|
||||
transId: json['transId'],
|
||||
customData: json['customData'],
|
||||
userId: json['userId'],
|
||||
);
|
||||
}
|
||||
}
|
150
lib/express/express_ad_view.dart
Normal file
150
lib/express/express_ad_view.dart
Normal file
@@ -0,0 +1,150 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:union_ad_ssgf/flutter_union_ad.dart';
|
||||
|
||||
class ExpressAdView extends StatefulWidget {
|
||||
final String androidId;
|
||||
final String iosId;
|
||||
final int viewWidth;
|
||||
final int viewHeight;
|
||||
final FlutterUnionAdExpressCallBack? callBack;
|
||||
final bool downloadConfirm;
|
||||
final bool isBidding;
|
||||
final FlutterUnioAdBiddingController? bidding;
|
||||
|
||||
const ExpressAdView({
|
||||
Key? key,
|
||||
required this.androidId,
|
||||
required this.iosId,
|
||||
required this.viewWidth,
|
||||
required this.viewHeight,
|
||||
this.callBack,
|
||||
required this.downloadConfirm,
|
||||
required this.isBidding,
|
||||
this.bidding,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<ExpressAdView> createState() => _ExpressAdViewState();
|
||||
}
|
||||
|
||||
class _ExpressAdViewState extends State<ExpressAdView> {
|
||||
final String _viewType = "com.example.union_ad_ssgf/NativeExpressAdView";
|
||||
|
||||
MethodChannel? _channel;
|
||||
|
||||
//广告是否显示
|
||||
bool _isShowAd = true;
|
||||
|
||||
double _width = 0;
|
||||
double _height = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_width = widget.viewWidth.toDouble();
|
||||
_height = widget.viewHeight.toDouble();
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!_isShowAd) {
|
||||
return Container();
|
||||
}
|
||||
if (defaultTargetPlatform == TargetPlatform.android) {
|
||||
return SizedBox(
|
||||
width: _width,
|
||||
height: _height,
|
||||
child: AndroidView(
|
||||
viewType: _viewType,
|
||||
creationParams: {
|
||||
"androidId": widget.androidId,
|
||||
"viewWidth": widget.viewWidth,
|
||||
"viewHeight": widget.viewHeight,
|
||||
"downloadConfirm": widget.downloadConfirm,
|
||||
"isBidding": widget.isBidding,
|
||||
},
|
||||
onPlatformViewCreated: _registerChannel,
|
||||
creationParamsCodec: const StandardMessageCodec(),
|
||||
),
|
||||
);
|
||||
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
|
||||
return SizedBox(
|
||||
width: _width,
|
||||
height: _height,
|
||||
child: UiKitView(
|
||||
viewType: _viewType,
|
||||
creationParams: {
|
||||
"iosId": widget.iosId,
|
||||
"viewWidth": widget.viewWidth,
|
||||
"viewHeight": widget.viewHeight,
|
||||
"isBidding": widget.isBidding,
|
||||
},
|
||||
onPlatformViewCreated: _registerChannel,
|
||||
creationParamsCodec: const StandardMessageCodec(),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
|
||||
//注册cannel
|
||||
void _registerChannel(int id) {
|
||||
_channel = MethodChannel("${_viewType}_$id");
|
||||
_channel?.setMethodCallHandler(_platformCallHandler);
|
||||
widget.bidding?.init(_channel);
|
||||
}
|
||||
|
||||
//监听原生view传值
|
||||
Future<dynamic> _platformCallHandler(MethodCall call) async {
|
||||
switch (call.method) {
|
||||
//显示广告
|
||||
case FlutterTencentadMethod.onShow:
|
||||
Map map = call.arguments;
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_width = map["width"];
|
||||
_height = map["height"];
|
||||
_isShowAd = true;
|
||||
});
|
||||
}
|
||||
widget.callBack?.onShow!();
|
||||
break;
|
||||
//广告加载失败
|
||||
case FlutterTencentadMethod.onFail:
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isShowAd = false;
|
||||
});
|
||||
}
|
||||
Map map = call.arguments;
|
||||
widget.callBack?.onFail!(map["code"], map["message"]);
|
||||
break;
|
||||
//点击
|
||||
case FlutterTencentadMethod.onClick:
|
||||
widget.callBack?.onClick!();
|
||||
break;
|
||||
//曝光
|
||||
case FlutterTencentadMethod.onExpose:
|
||||
widget.callBack?.onExpose!();
|
||||
break;
|
||||
//关闭
|
||||
case FlutterTencentadMethod.onClose:
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isShowAd = false;
|
||||
});
|
||||
}
|
||||
widget.callBack?.onClose!();
|
||||
break;
|
||||
//竞价
|
||||
case FlutterTencentadMethod.onECPM:
|
||||
Map map = call.arguments;
|
||||
widget.callBack?.onECPM!(map["ecpmLevel"], map["ecpm"]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
87
lib/flutter_unio_ad_bidding_controller.dart
Normal file
87
lib/flutter_unio_ad_bidding_controller.dart
Normal file
@@ -0,0 +1,87 @@
|
||||
part of 'flutter_union_ad.dart';
|
||||
|
||||
/// @Description: 优量汇竞价
|
||||
class FlutterUnioAdBiddingController {
|
||||
late MethodChannel? _methodChannel;
|
||||
|
||||
init(MethodChannel? method) {
|
||||
_methodChannel = method;
|
||||
}
|
||||
|
||||
//回传竞价结果
|
||||
void biddingResult(FlutterTencentBiddingResult result) {
|
||||
//竞价成功
|
||||
if (result.isSuccess) {
|
||||
_methodChannel?.invokeMethod('biddingSucceeded', {
|
||||
'expectCostPrice': result.expectCostPrice,
|
||||
'highestLossPrice': result.highestLossPrice,
|
||||
});
|
||||
//竞价失败
|
||||
} else {
|
||||
_methodChannel?.invokeMethod('biddingSucceeded', {
|
||||
'winPrice': result.winPrice,
|
||||
'lossReason': result.lossReason,
|
||||
'adnId': result.adnId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FlutterTencentBiddingResult {
|
||||
int? expectCostPrice;
|
||||
int? highestLossPrice;
|
||||
int? winPrice;
|
||||
int? lossReason;
|
||||
String? adnId;
|
||||
|
||||
bool isSuccess = true;
|
||||
|
||||
FlutterTencentBiddingResult();
|
||||
|
||||
///竞价成功
|
||||
///
|
||||
///[expectCostPrice] 竞胜出价,类型为Integer
|
||||
///
|
||||
///[highestLossPrice] 最大竞败方出价,类型为Integer
|
||||
FlutterTencentBiddingResult success(
|
||||
int expectCostPrice, int highestLossPrice) {
|
||||
this.isSuccess = true;
|
||||
this.expectCostPrice = expectCostPrice;
|
||||
this.highestLossPrice = highestLossPrice;
|
||||
return this;
|
||||
}
|
||||
|
||||
///竞价失败
|
||||
///
|
||||
/// [winPrice] 本次竞胜方出价(单位:分),类型为Integer。选填
|
||||
///
|
||||
/// [lossReason] 优量汇广告竞败原因,类型为Integer。必填 [FlutterTencentAdBiddingLossReason]
|
||||
///
|
||||
/// [adnId] 本次竞胜方渠道ID,类型为Integer。必填。 [FlutterTencentAdADNID]
|
||||
FlutterTencentBiddingResult fail(int winPrice, int lossReason, String adnId) {
|
||||
this.isSuccess = false;
|
||||
this.winPrice = winPrice;
|
||||
this.lossReason = lossReason;
|
||||
this.adnId = adnId;
|
||||
return this;
|
||||
}
|
||||
|
||||
FlutterTencentBiddingResult.fromJson(Map<String, dynamic> json) {
|
||||
expectCostPrice = json['expectCostPrice'];
|
||||
highestLossPrice = json['highestLossPrice'];
|
||||
winPrice = json['winPrice'];
|
||||
lossReason = json['lossReason'];
|
||||
adnId = json['adnId'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['isSuccess'] = this.isSuccess;
|
||||
data['expectCostPrice'] = this.expectCostPrice;
|
||||
data['highestLossPrice'] = this.highestLossPrice;
|
||||
data['winPrice'] = this.winPrice;
|
||||
data['lossReason'] = this.lossReason;
|
||||
data['adnId'] = this.adnId;
|
||||
return data;
|
||||
}
|
||||
}
|
275
lib/flutter_union_ad.dart
Normal file
275
lib/flutter_union_ad.dart
Normal file
@@ -0,0 +1,275 @@
|
||||
export 'flutter_union_ad_stream.dart';
|
||||
export 'flutter_union_ad_code.dart.dart';
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:union_ad_ssgf/banner/banner_ad_view.dart';
|
||||
import 'package:union_ad_ssgf/splash/splash_ad_view.dart';
|
||||
import 'flutter_union_ad_code.dart.dart';
|
||||
|
||||
import 'express/express_ad_view.dart';
|
||||
|
||||
part 'flutter_union_ad_callback.dart';
|
||||
|
||||
part 'flutter_unio_ad_bidding_controller.dart';
|
||||
|
||||
class FlutterUnionAd {
|
||||
static const MethodChannel _channel = MethodChannel('flutter_union_ad');
|
||||
|
||||
///
|
||||
/// # SDK注册初始化
|
||||
///
|
||||
/// [androidId] androidId 必填
|
||||
///
|
||||
/// [iosId] iosId 必填
|
||||
///
|
||||
/// [channelId] channelId 渠道id [FlutterTencentadChannel]
|
||||
///
|
||||
/// [personalized] personalized 是否开启个性化广告 [FlutterTencentadPersonalized]
|
||||
///
|
||||
static Future<bool> register({
|
||||
required String androidId,
|
||||
required String iosId,
|
||||
int? personalized,
|
||||
bool? debug,
|
||||
int? channelId,
|
||||
}) async {
|
||||
return await _channel.invokeMethod("register", {
|
||||
"androidId": androidId,
|
||||
"iosId": iosId,
|
||||
"debug": debug ?? false,
|
||||
"channelId": channelId ?? FlutterTencentadChannel.other,
|
||||
"personalized": personalized ?? FlutterTencentadPersonalized.show,
|
||||
});
|
||||
}
|
||||
|
||||
///
|
||||
/// # 获取SDK版本号
|
||||
///
|
||||
static Future<String> getSDKVersion() async {
|
||||
return await _channel.invokeMethod("getSDKVersion");
|
||||
}
|
||||
|
||||
///
|
||||
/// # 激励视频广告预加载
|
||||
///
|
||||
/// [androidId] android广告ID
|
||||
///
|
||||
/// [iosId] ios广告ID
|
||||
///
|
||||
/// [rewardName] 奖励名称
|
||||
///
|
||||
/// [rewardAmount] 奖励金额
|
||||
///
|
||||
/// [userID] 用户id
|
||||
///
|
||||
/// [customData] 扩展参数,服务器回调使用
|
||||
///
|
||||
/// [downloadConfirm] 下载二次确认弹窗 默认false
|
||||
///
|
||||
/// [videoMuted] 是否静音 默认false
|
||||
///
|
||||
/// [isBidding] 是否开启竞价模式 默认false
|
||||
static Future<bool> loadRewardVideoAd({
|
||||
required String androidId,
|
||||
required String iosId,
|
||||
required String rewardName,
|
||||
required int rewardAmount,
|
||||
required String userID,
|
||||
String? customData,
|
||||
bool? downloadConfirm,
|
||||
bool? videoMuted,
|
||||
bool? isBidding,
|
||||
}) async {
|
||||
return await _channel.invokeMethod("loadRewardVideoAd", {
|
||||
"androidId": androidId,
|
||||
"iosId": iosId,
|
||||
"rewardName": rewardName,
|
||||
"rewardAmount": rewardAmount,
|
||||
"userID": userID,
|
||||
"customData": customData ?? "",
|
||||
"videoMuted": videoMuted ?? false,
|
||||
"downloadConfirm": downloadConfirm ?? false,
|
||||
"isBidding": isBidding ?? false,
|
||||
});
|
||||
}
|
||||
|
||||
///
|
||||
/// # 显示激励广告
|
||||
///
|
||||
/// [result] 竞价成功、失败后调用 [FlutterTencentBiddingResult] ,isBidding = true时必传
|
||||
static Future<bool> showRewardVideoAd(
|
||||
{FlutterTencentBiddingResult? result}) async {
|
||||
return await _channel.invokeMethod(
|
||||
"showRewardVideoAd", result?.toJson() ?? {});
|
||||
}
|
||||
|
||||
///
|
||||
/// # 预加载插屏广告
|
||||
///
|
||||
/// [androidId] android广告ID
|
||||
///
|
||||
/// [iosId] ios广告ID
|
||||
///
|
||||
/// [isFullScreen] 是否全屏
|
||||
///
|
||||
/// [downloadConfirm] 下载二次确认弹窗 默认false
|
||||
///
|
||||
/// [isBidding] 是否开启竞价模式 默认false
|
||||
///
|
||||
static Future<bool> loadUnifiedInterstitialAD({
|
||||
required String androidId,
|
||||
required String iosId,
|
||||
required bool isFullScreen,
|
||||
bool? downloadConfirm,
|
||||
bool? isBidding,
|
||||
}) async {
|
||||
return await _channel.invokeMethod("loadInterstitialAD", {
|
||||
"androidId": androidId,
|
||||
"iosId": iosId,
|
||||
"isFullScreen": isFullScreen,
|
||||
"downloadConfirm": downloadConfirm ?? false,
|
||||
"isBidding": isBidding ?? false,
|
||||
});
|
||||
}
|
||||
|
||||
///
|
||||
/// # 显示新模板渲染插屏
|
||||
///
|
||||
///
|
||||
/// [result] 竞价成功、失败后调用 [FlutterTencentBiddingResult] ,isBidding = true时必传
|
||||
///
|
||||
static Future<bool> showUnifiedInterstitialAD(
|
||||
{FlutterTencentBiddingResult? result}) async {
|
||||
return await _channel.invokeMethod(
|
||||
"showInterstitialAD", result?.toJson() ?? {});
|
||||
}
|
||||
|
||||
///
|
||||
/// # banner广告
|
||||
///
|
||||
/// [androidId] android广告ID
|
||||
///
|
||||
/// [iosId] ios广告ID
|
||||
///
|
||||
/// [viewWidth] 广告宽 单位dp
|
||||
///
|
||||
/// [viewHeight] 广告高 单位dp 宽高比应该为6.4:1
|
||||
///
|
||||
/// [FlutterTencentAdBannerCallBack] 广告回调
|
||||
///
|
||||
/// [downloadConfirm] 下载二次确认弹窗 默认false
|
||||
///
|
||||
/// [isBidding] 是否开启竞价模式 默认false
|
||||
///
|
||||
/// [bidding] 竞价成功、失败后调用 [FlutterTencentAdBiddingController] ,isBidding = true时必传
|
||||
///
|
||||
static Widget bannerAdView({
|
||||
required String androidId,
|
||||
required String iosId,
|
||||
required double viewWidth,
|
||||
required double viewHeight,
|
||||
bool? downloadConfirm,
|
||||
bool? isBidding,
|
||||
FlutterUnioAdBiddingController? bidding,
|
||||
FlutterUnionAdBannerCallBack? callBack,
|
||||
}) {
|
||||
return BannerAdView(
|
||||
androidId: androidId,
|
||||
iosId: iosId,
|
||||
viewWidth: viewWidth,
|
||||
viewHeight: viewHeight,
|
||||
callBack: callBack,
|
||||
downloadConfirm: downloadConfirm ?? false,
|
||||
isBidding: isBidding ?? false,
|
||||
bidding: bidding,
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// # 开屏广告
|
||||
///
|
||||
/// [androidId] android广告ID
|
||||
///
|
||||
/// [iosId] ios广告ID
|
||||
///
|
||||
/// [fetchDelay] 设置开屏广告从请求到展示所花的最大时长(并不是指广告曝光时长),
|
||||
/// 取值范围为[1500, 5000]ms。如果需要使用默认值,可以调用上一个构造方法,
|
||||
/// 或者给 fetchDelay 设为0。
|
||||
///
|
||||
/// [FlutterTencentAdSplashCallBack] 广告回调
|
||||
///
|
||||
/// [downloadConfirm] 下载二次确认弹窗 默认false
|
||||
///
|
||||
/// [isBidding] 是否开启竞价模式 默认false
|
||||
///
|
||||
/// [bidding] 竞价成功、失败后调用 [FlutterTencentAdBiddingController] ,isBidding = true时必传
|
||||
///
|
||||
static Widget splashAdView({
|
||||
required String androidId,
|
||||
required String iosId,
|
||||
required int fetchDelay,
|
||||
bool? downloadConfirm,
|
||||
bool? isBidding,
|
||||
FlutterUnioAdBiddingController? bidding,
|
||||
FlutterUnionAdSplashCallBack? callBack,
|
||||
}) {
|
||||
return SplashAdView(
|
||||
androidId: androidId,
|
||||
iosId: iosId,
|
||||
fetchDelay: fetchDelay,
|
||||
callBack: callBack,
|
||||
downloadConfirm: downloadConfirm ?? false,
|
||||
isBidding: isBidding ?? false,
|
||||
bidding: bidding,
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// # 动态信息流/横幅/视频贴片广告
|
||||
///
|
||||
/// [androidId] android广告ID
|
||||
///
|
||||
/// [iosId] ios广告ID
|
||||
///
|
||||
/// [viewWidth] 广告宽 单位dp
|
||||
///
|
||||
/// [viewHeight] 广告高 单位dp
|
||||
///
|
||||
/// [FlutterTencentAdExpressCallBack] 回调事件
|
||||
///
|
||||
/// [downloadConfirm] 下载二次确认弹窗 默认false
|
||||
///
|
||||
/// [isBidding] 是否开启竞价模式 默认false
|
||||
///
|
||||
/// [bidding] 竞价成功、失败后调用 [FlutterTencentAdBiddingController] ,isBidding = true时必传
|
||||
///
|
||||
static Widget expressAdView({
|
||||
required String androidId,
|
||||
required String iosId,
|
||||
required int viewWidth,
|
||||
required int viewHeight,
|
||||
bool? downloadConfirm,
|
||||
bool? isBidding,
|
||||
FlutterUnioAdBiddingController? bidding,
|
||||
FlutterUnionAdExpressCallBack? callBack,
|
||||
}) {
|
||||
return ExpressAdView(
|
||||
androidId: androidId,
|
||||
iosId: iosId,
|
||||
viewWidth: viewWidth,
|
||||
viewHeight: viewHeight,
|
||||
callBack: callBack,
|
||||
downloadConfirm: downloadConfirm ?? false,
|
||||
isBidding: isBidding ?? false,
|
||||
bidding: bidding,
|
||||
);
|
||||
}
|
||||
|
||||
///进入APP下载列表页
|
||||
static Future<bool> enterAPPDownloadListPage() async {
|
||||
return await _channel.invokeMethod("enterAPPDownloadListPage", {});
|
||||
}
|
||||
}
|
158
lib/flutter_union_ad_callback.dart
Normal file
158
lib/flutter_union_ad_callback.dart
Normal file
@@ -0,0 +1,158 @@
|
||||
part of 'flutter_union_ad.dart';
|
||||
|
||||
/// @Description: dart类作用描述
|
||||
|
||||
///显示
|
||||
typedef TOnShow = void Function();
|
||||
|
||||
///曝光
|
||||
typedef TOnExpose = void Function();
|
||||
|
||||
///失败
|
||||
typedef TOnFail = void Function(int code, dynamic message);
|
||||
|
||||
///点击
|
||||
typedef TOnClick = void Function();
|
||||
|
||||
///视频播放
|
||||
typedef TOnVideoPlay = void Function();
|
||||
|
||||
///视频暂停
|
||||
typedef TOnVideoPause = void Function();
|
||||
|
||||
///视频播放结束
|
||||
typedef TOnVideoStop = void Function();
|
||||
|
||||
///跳过
|
||||
typedef TOnSkip = void Function();
|
||||
|
||||
///倒计时结束
|
||||
typedef TOnFinish = void Function();
|
||||
|
||||
///加载超时
|
||||
typedef TOnTimeOut = void Function();
|
||||
|
||||
///关闭
|
||||
typedef TOnClose = void Function();
|
||||
|
||||
///广告预加载完成
|
||||
typedef TOnReady = void Function();
|
||||
|
||||
///广告预加载未完成
|
||||
typedef TOnUnReady = void Function();
|
||||
|
||||
///广告奖励验证
|
||||
typedef TOnVerify = void Function(
|
||||
String transId, String rewardName, int rewardAmount);
|
||||
|
||||
///倒计时
|
||||
typedef TOnADTick = void Function(int time);
|
||||
|
||||
///竞价回调
|
||||
typedef TOnECPM = void Function(String ecpmLevel, int ecpm);
|
||||
|
||||
///banner广告回调
|
||||
class FlutterUnionAdBannerCallBack {
|
||||
TOnShow? onShow;
|
||||
TOnFail? onFail;
|
||||
TOnClick? onClick;
|
||||
TOnExpose? onExpose;
|
||||
TOnClose? onClose;
|
||||
TOnECPM? onECPM;
|
||||
|
||||
FlutterUnionAdBannerCallBack(
|
||||
{this.onShow,
|
||||
this.onFail,
|
||||
this.onClick,
|
||||
this.onExpose,
|
||||
this.onClose,
|
||||
this.onECPM});
|
||||
}
|
||||
|
||||
///动态信息流/横幅/视频贴片广告回调
|
||||
class FlutterUnionAdExpressCallBack {
|
||||
TOnShow? onShow;
|
||||
TOnFail? onFail;
|
||||
TOnClick? onClick;
|
||||
TOnExpose? onExpose;
|
||||
TOnClose? onClose;
|
||||
TOnECPM? onECPM;
|
||||
|
||||
FlutterUnionAdExpressCallBack(
|
||||
{this.onShow,
|
||||
this.onFail,
|
||||
this.onClick,
|
||||
this.onExpose,
|
||||
this.onClose,
|
||||
this.onECPM});
|
||||
}
|
||||
|
||||
///开屏广告回调
|
||||
class FlutterUnionAdSplashCallBack {
|
||||
TOnClose? onClose;
|
||||
TOnShow? onShow;
|
||||
TOnFail? onFail;
|
||||
TOnClick? onClick;
|
||||
TOnExpose? onExpose;
|
||||
TOnADTick? onADTick;
|
||||
TOnECPM? onECPM;
|
||||
|
||||
FlutterUnionAdSplashCallBack(
|
||||
{this.onShow,
|
||||
this.onFail,
|
||||
this.onClick,
|
||||
this.onClose,
|
||||
this.onExpose,
|
||||
this.onADTick,
|
||||
this.onECPM});
|
||||
}
|
||||
|
||||
///插屏广告回调
|
||||
class FlutterUnionAdInteractionCallBack {
|
||||
TOnShow? onShow;
|
||||
TOnClick? onClick;
|
||||
TOnClose? onClose;
|
||||
TOnFail? onFail;
|
||||
TOnReady? onReady;
|
||||
TOnUnReady? onUnReady;
|
||||
TOnExpose? onExpose;
|
||||
TOnVerify? onVerify;
|
||||
TOnECPM? onECPM;
|
||||
|
||||
FlutterUnionAdInteractionCallBack(
|
||||
{this.onShow,
|
||||
this.onClick,
|
||||
this.onClose,
|
||||
this.onFail,
|
||||
this.onExpose,
|
||||
this.onReady,
|
||||
this.onUnReady,
|
||||
this.onVerify,
|
||||
this.onECPM});
|
||||
}
|
||||
|
||||
///激励广告回调
|
||||
class FlutterUnionAdRewardCallBack {
|
||||
TOnShow? onShow;
|
||||
TOnClose? onClose;
|
||||
TOnExpose? onExpose;
|
||||
TOnFail? onFail;
|
||||
TOnClick? onClick;
|
||||
TOnVerify? onVerify;
|
||||
TOnReady? onReady;
|
||||
TOnFinish? onFinish;
|
||||
TOnUnReady? onUnReady;
|
||||
TOnECPM? onECPM;
|
||||
|
||||
FlutterUnionAdRewardCallBack(
|
||||
{this.onShow,
|
||||
this.onClick,
|
||||
this.onExpose,
|
||||
this.onClose,
|
||||
this.onFail,
|
||||
this.onVerify,
|
||||
this.onReady,
|
||||
this.onFinish,
|
||||
this.onUnReady,
|
||||
this.onECPM});
|
||||
}
|
142
lib/flutter_union_ad_code.dart.dart
Normal file
142
lib/flutter_union_ad_code.dart.dart
Normal file
@@ -0,0 +1,142 @@
|
||||
///数据类型
|
||||
class FlutterUnionAdCode {
|
||||
static const String adType = "adType";
|
||||
|
||||
///激励广告
|
||||
static const String rewardAd = "rewardAd";
|
||||
|
||||
///插屏广告
|
||||
static const String interactAd = "interactAd";
|
||||
}
|
||||
|
||||
class FlutterTencentadMethod {
|
||||
///stream中 广告方法
|
||||
static const String onAdMethod = "onAdMethod";
|
||||
|
||||
///广告加载状态 view使用
|
||||
///显示view
|
||||
static const String onShow = "onShow";
|
||||
|
||||
///广告曝光
|
||||
static const String onExpose = "onExpose";
|
||||
|
||||
///加载失败
|
||||
static const String onFail = "onFail";
|
||||
|
||||
///点击
|
||||
static const String onClick = "onClick";
|
||||
|
||||
///视频播放
|
||||
static const String onVideoPlay = "onVideoPlay";
|
||||
|
||||
///视频暂停
|
||||
static const String onVideoPause = "onVideoPause";
|
||||
|
||||
///视频结束
|
||||
static const String onVideoStop = "onVideoStop";
|
||||
|
||||
///倒计时结束
|
||||
static const String onFinish = "onFinish";
|
||||
|
||||
///加载超时
|
||||
static const String onTimeOut = "onTimeOut";
|
||||
|
||||
///广告关闭
|
||||
static const String onClose = "onClose";
|
||||
|
||||
///广告奖励校验
|
||||
static const String onVerify = "onVerify";
|
||||
|
||||
///广告预加载完成
|
||||
static const String onReady = "onReady";
|
||||
|
||||
///广告未预加载
|
||||
static const String onUnReady = "onUnReady";
|
||||
|
||||
///倒计时
|
||||
static const String onADTick = "onADTick";
|
||||
|
||||
///竞价
|
||||
static const String onECPM = "onECPM";
|
||||
}
|
||||
|
||||
///渠道id
|
||||
class FlutterTencentadChannel {
|
||||
///百度
|
||||
static const int baidu = 1;
|
||||
|
||||
///头条
|
||||
static const int toutiao = 2;
|
||||
|
||||
///优量汇
|
||||
static const int tencent = 3;
|
||||
|
||||
///搜狗
|
||||
static const int sougou = 4;
|
||||
|
||||
///其他网盟
|
||||
static const int otherAd = 5;
|
||||
|
||||
///oppe
|
||||
static const int oppo = 6;
|
||||
|
||||
///vivo
|
||||
static const int vivo = 7;
|
||||
|
||||
///huawei
|
||||
static const int huawei = 8;
|
||||
|
||||
///应用宝
|
||||
static const int yinyongbao = 9;
|
||||
|
||||
///小米
|
||||
static const int xiaomi = 10;
|
||||
|
||||
///金立
|
||||
static const int jinli = 11;
|
||||
|
||||
///百度手机助手
|
||||
static const int baiduMobile = 12;
|
||||
|
||||
///魅族
|
||||
static const int meizu = 13;
|
||||
|
||||
///App Store
|
||||
static const int appStore = 14;
|
||||
|
||||
///其他
|
||||
static const int other = 999;
|
||||
}
|
||||
|
||||
///个性化广告
|
||||
class FlutterTencentadPersonalized {
|
||||
///屏蔽个性化推荐广告
|
||||
static const int close = 1;
|
||||
|
||||
///不屏蔽个性化推荐广告
|
||||
static const int show = 0;
|
||||
}
|
||||
|
||||
///竞价失败原因
|
||||
class FlutterTencentAdBiddingLossReason {
|
||||
/// 竞争力不足,如优量汇不是本次竞价的最高出价方,可上报此竞败原因
|
||||
static const int LOW_PRICE = 1;
|
||||
|
||||
/// 返回超时,如优量汇在本次竞价中未返回广告,可上报此竞败原因
|
||||
static const int TIME_OUT = 2;
|
||||
|
||||
///其他
|
||||
static const int OTHER = 10001;
|
||||
}
|
||||
|
||||
///本次竞胜方渠道ID
|
||||
class FlutterTencentAdADNID {
|
||||
///1 - 输给优量汇其它广告,当优量汇目标价报价为本次竞价的最高报价时,可上报此值,仅对混合比价类型的开发者适用
|
||||
static const String tencentADN = "1";
|
||||
|
||||
/// 2 - 输给第三方ADN,当其它ADN报价为本次竞价的最高报价时,可上报此值,您无需回传具体竞胜方渠道;
|
||||
static const String othoerADN = "2";
|
||||
|
||||
/// 3 - 输给自售广告主,当自售广告源报价为本次竞价的最高报价时,可上报此值,仅对有自售广告源的开发者使用
|
||||
static const String appADN = "3";
|
||||
}
|
143
lib/flutter_union_ad_stream.dart
Normal file
143
lib/flutter_union_ad_stream.dart
Normal file
@@ -0,0 +1,143 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'flutter_union_ad.dart';
|
||||
|
||||
/// @Description: dart类作用描述
|
||||
const EventChannel tencentAdEventEvent =
|
||||
EventChannel("com.example.union_ad_ssgf/adevent");
|
||||
|
||||
class FlutterUnionAdStream {
|
||||
///
|
||||
/// # 注册stream监听原生返回的信息
|
||||
/// [rewardAdCallBack] 激励广告回调
|
||||
/// [interactionAdCallBack] 插屏广告回调
|
||||
///
|
||||
static StreamSubscription initAdStream(
|
||||
{FlutterUnionAdRewardCallBack? flutterTencentadRewardCallBack,
|
||||
FlutterUnionAdInteractionCallBack? flutterTencentadInteractionCallBack}) {
|
||||
StreamSubscription _adStream =
|
||||
tencentAdEventEvent.receiveBroadcastStream().listen((data) {
|
||||
switch (data[FlutterUnionAdCode.adType]) {
|
||||
///激励广告
|
||||
case FlutterUnionAdCode.rewardAd:
|
||||
switch (data[FlutterTencentadMethod.onAdMethod]) {
|
||||
case FlutterTencentadMethod.onShow:
|
||||
if (flutterTencentadRewardCallBack?.onShow != null) {
|
||||
flutterTencentadRewardCallBack?.onShow!();
|
||||
}
|
||||
break;
|
||||
case FlutterTencentadMethod.onClose:
|
||||
if (flutterTencentadRewardCallBack?.onClose != null) {
|
||||
flutterTencentadRewardCallBack?.onClose!();
|
||||
}
|
||||
break;
|
||||
case FlutterTencentadMethod.onFail:
|
||||
if (flutterTencentadRewardCallBack?.onFail != null) {
|
||||
flutterTencentadRewardCallBack?.onFail!(
|
||||
data["code"], data["message"]);
|
||||
}
|
||||
break;
|
||||
case FlutterTencentadMethod.onClick:
|
||||
if (flutterTencentadRewardCallBack?.onClick != null) {
|
||||
flutterTencentadRewardCallBack?.onClick!();
|
||||
}
|
||||
break;
|
||||
case FlutterTencentadMethod.onVerify:
|
||||
if (flutterTencentadRewardCallBack?.onVerify != null) {
|
||||
flutterTencentadRewardCallBack?.onVerify!(
|
||||
data["transId"], data["rewardName"], data["rewardAmount"]);
|
||||
}
|
||||
break;
|
||||
case FlutterTencentadMethod.onFinish:
|
||||
if (flutterTencentadRewardCallBack?.onFinish != null) {
|
||||
flutterTencentadRewardCallBack?.onFinish!();
|
||||
}
|
||||
break;
|
||||
case FlutterTencentadMethod.onReady:
|
||||
if (flutterTencentadRewardCallBack?.onReady != null) {
|
||||
flutterTencentadRewardCallBack?.onReady!();
|
||||
}
|
||||
break;
|
||||
case FlutterTencentadMethod.onUnReady:
|
||||
if (flutterTencentadRewardCallBack?.onUnReady != null) {
|
||||
flutterTencentadRewardCallBack?.onUnReady!();
|
||||
}
|
||||
break;
|
||||
case FlutterTencentadMethod.onExpose:
|
||||
if (flutterTencentadRewardCallBack?.onExpose != null) {
|
||||
flutterTencentadRewardCallBack?.onExpose!();
|
||||
}
|
||||
break;
|
||||
case FlutterTencentadMethod.onECPM:
|
||||
if (flutterTencentadRewardCallBack?.onECPM != null) {
|
||||
flutterTencentadRewardCallBack?.onECPM!(
|
||||
data["ecpmLevel"], data["ecpm"]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
///插屏广告
|
||||
case FlutterUnionAdCode.interactAd:
|
||||
switch (data[FlutterTencentadMethod.onAdMethod]) {
|
||||
case FlutterTencentadMethod.onShow:
|
||||
if (flutterTencentadInteractionCallBack?.onShow != null) {
|
||||
flutterTencentadInteractionCallBack?.onShow!();
|
||||
}
|
||||
break;
|
||||
case FlutterTencentadMethod.onClose:
|
||||
if (flutterTencentadInteractionCallBack?.onClose != null) {
|
||||
flutterTencentadInteractionCallBack?.onClose!();
|
||||
}
|
||||
break;
|
||||
case FlutterTencentadMethod.onFail:
|
||||
if (flutterTencentadInteractionCallBack?.onFail != null) {
|
||||
flutterTencentadInteractionCallBack?.onFail!(
|
||||
data["code"], data["message"]);
|
||||
}
|
||||
break;
|
||||
case FlutterTencentadMethod.onClick:
|
||||
if (flutterTencentadInteractionCallBack?.onClick != null) {
|
||||
flutterTencentadInteractionCallBack?.onClick!();
|
||||
}
|
||||
break;
|
||||
case FlutterTencentadMethod.onExpose:
|
||||
if (flutterTencentadInteractionCallBack?.onExpose != null) {
|
||||
flutterTencentadInteractionCallBack?.onExpose!();
|
||||
}
|
||||
break;
|
||||
case FlutterTencentadMethod.onReady:
|
||||
if (flutterTencentadInteractionCallBack?.onReady != null) {
|
||||
flutterTencentadInteractionCallBack?.onReady!();
|
||||
}
|
||||
break;
|
||||
case FlutterTencentadMethod.onUnReady:
|
||||
if (flutterTencentadInteractionCallBack?.onUnReady != null) {
|
||||
flutterTencentadInteractionCallBack?.onUnReady!();
|
||||
}
|
||||
break;
|
||||
case FlutterTencentadMethod.onVerify:
|
||||
if (flutterTencentadInteractionCallBack?.onVerify != null) {
|
||||
flutterTencentadInteractionCallBack?.onVerify!(
|
||||
data["transId"], "", 0);
|
||||
}
|
||||
break;
|
||||
case FlutterTencentadMethod.onECPM:
|
||||
if (flutterTencentadInteractionCallBack?.onECPM != null) {
|
||||
flutterTencentadInteractionCallBack?.onECPM!(
|
||||
data["ecpmLevel"], data["ecpm"]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
return _adStream;
|
||||
}
|
||||
|
||||
static void deleteAdStream(StreamSubscription stream) {
|
||||
stream.cancel();
|
||||
}
|
||||
}
|
137
lib/splash/splash_ad_view.dart
Normal file
137
lib/splash/splash_ad_view.dart
Normal file
@@ -0,0 +1,137 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:union_ad_ssgf/flutter_union_ad.dart';
|
||||
|
||||
class SplashAdView extends StatefulWidget {
|
||||
final String androidId;
|
||||
final String iosId;
|
||||
final int fetchDelay;
|
||||
final FlutterUnionAdSplashCallBack? callBack;
|
||||
final bool downloadConfirm;
|
||||
final bool isBidding;
|
||||
final FlutterUnioAdBiddingController? bidding;
|
||||
|
||||
const SplashAdView({
|
||||
Key? key,
|
||||
required this.androidId,
|
||||
required this.iosId,
|
||||
required this.fetchDelay,
|
||||
this.callBack,
|
||||
required this.downloadConfirm,
|
||||
required this.isBidding,
|
||||
this.bidding,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<SplashAdView> createState() => _SplashAdViewState();
|
||||
}
|
||||
|
||||
class _SplashAdViewState extends State<SplashAdView> {
|
||||
final String _viewType = "com.example.union_ad_ssgf/SplashAdView";
|
||||
|
||||
MethodChannel? _channel;
|
||||
|
||||
//广告是否显示
|
||||
bool _isShowAd = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_isShowAd = true;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!_isShowAd) {
|
||||
return Container();
|
||||
}
|
||||
if (defaultTargetPlatform == TargetPlatform.android) {
|
||||
return SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: AndroidView(
|
||||
viewType: _viewType,
|
||||
creationParams: {
|
||||
"androidId": widget.androidId,
|
||||
"fetchDelay": widget.fetchDelay,
|
||||
"downloadConfirm": widget.downloadConfirm,
|
||||
"isBidding": widget.isBidding,
|
||||
},
|
||||
onPlatformViewCreated: _registerChannel,
|
||||
creationParamsCodec: const StandardMessageCodec(),
|
||||
),
|
||||
);
|
||||
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
|
||||
return SizedBox(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: MediaQuery.of(context).size.height,
|
||||
child: UiKitView(
|
||||
viewType: _viewType,
|
||||
creationParams: {
|
||||
"iosId": widget.iosId,
|
||||
"fetchDelay": widget.fetchDelay,
|
||||
"isBidding": widget.isBidding,
|
||||
},
|
||||
onPlatformViewCreated: _registerChannel,
|
||||
creationParamsCodec: const StandardMessageCodec(),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
|
||||
//注册cannel
|
||||
void _registerChannel(int id) {
|
||||
_channel = MethodChannel("${_viewType}_$id");
|
||||
_channel?.setMethodCallHandler(_platformCallHandler);
|
||||
widget.bidding?.init(_channel);
|
||||
}
|
||||
|
||||
//监听原生view传值
|
||||
Future<dynamic> _platformCallHandler(MethodCall call) async {
|
||||
switch (call.method) {
|
||||
//显示广告
|
||||
case FlutterTencentadMethod.onShow:
|
||||
widget.callBack?.onShow!();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isShowAd = true;
|
||||
});
|
||||
}
|
||||
break;
|
||||
//关闭
|
||||
case FlutterTencentadMethod.onClose:
|
||||
widget.callBack?.onClose!();
|
||||
break;
|
||||
//广告加载失败
|
||||
case FlutterTencentadMethod.onFail:
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isShowAd = false;
|
||||
});
|
||||
}
|
||||
Map map = call.arguments;
|
||||
widget.callBack?.onFail!(map["code"], map["message"]);
|
||||
break;
|
||||
//点击
|
||||
case FlutterTencentadMethod.onClick:
|
||||
widget.callBack?.onClick!();
|
||||
break;
|
||||
//曝光
|
||||
case FlutterTencentadMethod.onExpose:
|
||||
widget.callBack?.onExpose!();
|
||||
break;
|
||||
//倒计时
|
||||
case FlutterTencentadMethod.onADTick:
|
||||
widget.callBack?.onADTick!(call.arguments);
|
||||
break;
|
||||
//竞价
|
||||
case FlutterTencentadMethod.onECPM:
|
||||
Map map = call.arguments;
|
||||
widget.callBack?.onECPM!(map["ecpmLevel"], map["ecpm"]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,99 +0,0 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:union_ad_ssgf/event/ad_event_handler.dart';
|
||||
|
||||
import 'union_ad_ssgf_platform_interface.dart';
|
||||
|
||||
/// 三商共富 Flutter 广告插件
|
||||
class UnionAdSsgf {
|
||||
/// 方法通道
|
||||
static const MethodChannel _methodChannel =
|
||||
MethodChannel('union_ad_ssgf_method');
|
||||
|
||||
/// 事件通道
|
||||
static const EventChannel _eventChannel = EventChannel('union_ad_ssgf_event');
|
||||
|
||||
/// 请求应用跟踪透明度授权
|
||||
static Future<bool> get requestIDFA async {
|
||||
if (Platform.isIOS) {
|
||||
final bool result = await _methodChannel.invokeMethod('requestIDFA');
|
||||
return result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// 初始化广告
|
||||
/// [appId] 应用媒体ID
|
||||
static Future<bool> initAd(String appId) async {
|
||||
final bool result = await _methodChannel.invokeMethod(
|
||||
'initAd',
|
||||
{'appId': appId},
|
||||
);
|
||||
print("🎉🎉🎉 UnionAd ==> 初始化完成");
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 设置个性化广告
|
||||
/// 0:代表开启个性化广告推荐,1:代表关闭个性化广告推荐
|
||||
static Future<bool> setPersonalizedState(int state) async {
|
||||
final bool result = await _methodChannel.invokeMethod(
|
||||
'setPersonalizedState',
|
||||
{'state': state},
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 展示开屏广告
|
||||
/// [posId] 广告位 id
|
||||
/// [logo] 如果传值则展示底部logo,不传不展示,则全屏展示
|
||||
/// [fetchDelay] 拉取广告的超时时间,默认值 3 秒,取值范围[1.5~5]秒
|
||||
static Future<bool> showSplashAd(String posId,
|
||||
{String? logo, double fetchDelay = 3}) async {
|
||||
final bool result = await _methodChannel.invokeMethod(
|
||||
'showSplashAd',
|
||||
{
|
||||
'posId': posId,
|
||||
'logo': logo,
|
||||
'fetchDelay': fetchDelay,
|
||||
},
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 展示激励视频广告
|
||||
/// [posId] 广告位 id
|
||||
/// [playMuted] 是否静音播放
|
||||
/// [customData] 设置服务端验证的自定义信息
|
||||
/// [userId] 设置服务端验证的用户信息
|
||||
static Future<bool> showRewardVideoAd(
|
||||
String posId, {
|
||||
bool playMuted = false,
|
||||
String? customData,
|
||||
String? userId,
|
||||
}) async {
|
||||
final bool result = await _methodChannel.invokeMethod(
|
||||
'showRewardVideoAd',
|
||||
{
|
||||
'posId': posId,
|
||||
'playMuted': playMuted,
|
||||
'customData': customData,
|
||||
'userId': userId,
|
||||
},
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
///事件回调
|
||||
///@params onData 事件回调
|
||||
static Future<void> onEventListener(
|
||||
OnAdEventListener onAdEventListener) async {
|
||||
_eventChannel.receiveBroadcastStream().listen((data) {
|
||||
hanleAdEvent(data, onAdEventListener);
|
||||
});
|
||||
}
|
||||
|
||||
Future<String?> getPlatformVersion() {
|
||||
return UnionAdSsgfPlatform.instance.getPlatformVersion();
|
||||
}
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'union_ad_ssgf_platform_interface.dart';
|
||||
|
||||
/// An implementation of [UnionAdSsgfPlatform] that uses method channels.
|
||||
class MethodChannelUnionAdSsgf extends UnionAdSsgfPlatform {
|
||||
/// The method channel used to interact with the native platform.
|
||||
@visibleForTesting
|
||||
final methodChannel = const MethodChannel('union_ad_ssgf_method');
|
||||
|
||||
@override
|
||||
Future<String?> getPlatformVersion() async {
|
||||
final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
|
||||
return version;
|
||||
}
|
||||
}
|
@@ -1,29 +0,0 @@
|
||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||
|
||||
import 'union_ad_ssgf_method_channel.dart';
|
||||
|
||||
abstract class UnionAdSsgfPlatform extends PlatformInterface {
|
||||
/// Constructs a UnionAdSsgfPlatform.
|
||||
UnionAdSsgfPlatform() : super(token: _token);
|
||||
|
||||
static final Object _token = Object();
|
||||
|
||||
static UnionAdSsgfPlatform _instance = MethodChannelUnionAdSsgf();
|
||||
|
||||
/// The default instance of [UnionAdSsgfPlatform] to use.
|
||||
///
|
||||
/// Defaults to [MethodChannelUnionAdSsgf].
|
||||
static UnionAdSsgfPlatform get instance => _instance;
|
||||
|
||||
/// Platform-specific implementations should set this with their own
|
||||
/// platform-specific class that extends [UnionAdSsgfPlatform] when
|
||||
/// they register themselves.
|
||||
static set instance(UnionAdSsgfPlatform instance) {
|
||||
PlatformInterface.verifyToken(instance, _token);
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
Future<String?> getPlatformVersion() {
|
||||
throw UnimplementedError('platformVersion() has not been implemented.');
|
||||
}
|
||||
}
|
@@ -1,26 +0,0 @@
|
||||
// In order to *not* need this ignore, consider extracting the "web" version
|
||||
// of your plugin as a separate package, instead of inlining it in the same
|
||||
// package as the core of your plugin.
|
||||
// ignore: avoid_web_libraries_in_flutter
|
||||
|
||||
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
|
||||
import 'package:web/web.dart' as web;
|
||||
|
||||
import 'union_ad_ssgf_platform_interface.dart';
|
||||
|
||||
/// A web implementation of the UnionAdSsgfPlatform of the UnionAdSsgf plugin.
|
||||
class UnionAdSsgfWeb extends UnionAdSsgfPlatform {
|
||||
/// Constructs a UnionAdSsgfWeb
|
||||
UnionAdSsgfWeb();
|
||||
|
||||
static void registerWith(Registrar registrar) {
|
||||
UnionAdSsgfPlatform.instance = UnionAdSsgfWeb();
|
||||
}
|
||||
|
||||
/// Returns a [String] containing the version of the platform.
|
||||
@override
|
||||
Future<String?> getPlatformVersion() async {
|
||||
final version = web.window.navigator.userAgent;
|
||||
return version;
|
||||
}
|
||||
}
|
@@ -1,27 +0,0 @@
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:union_ad_ssgf/union_ad_ssgf_method_channel.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
MethodChannelUnionAdSsgf platform = MethodChannelUnionAdSsgf();
|
||||
const MethodChannel channel = MethodChannel('union_ad_ssgf_method');
|
||||
|
||||
setUp(() {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
channel,
|
||||
(MethodCall methodCall) async {
|
||||
return '42';
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null);
|
||||
});
|
||||
|
||||
test('getPlatformVersion', () async {
|
||||
expect(await platform.getPlatformVersion(), '42');
|
||||
});
|
||||
}
|
@@ -1,29 +1,5 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:union_ad_ssgf/union_ad_ssgf.dart';
|
||||
import 'package:union_ad_ssgf/union_ad_ssgf_platform_interface.dart';
|
||||
import 'package:union_ad_ssgf/union_ad_ssgf_method_channel.dart';
|
||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||
|
||||
class MockUnionAdSsgfPlatform
|
||||
with MockPlatformInterfaceMixin
|
||||
implements UnionAdSsgfPlatform {
|
||||
class MockUnionAdSsgfPlatform with MockPlatformInterfaceMixin {}
|
||||
|
||||
@override
|
||||
Future<String?> getPlatformVersion() => Future.value('42');
|
||||
}
|
||||
|
||||
void main() {
|
||||
final UnionAdSsgfPlatform initialPlatform = UnionAdSsgfPlatform.instance;
|
||||
|
||||
test('$MethodChannelUnionAdSsgf is the default instance', () {
|
||||
expect(initialPlatform, isInstanceOf<MethodChannelUnionAdSsgf>());
|
||||
});
|
||||
|
||||
test('getPlatformVersion', () async {
|
||||
UnionAdSsgf unionAdSsgfPlugin = UnionAdSsgf();
|
||||
MockUnionAdSsgfPlatform fakePlatform = MockUnionAdSsgfPlatform();
|
||||
UnionAdSsgfPlatform.instance = fakePlatform;
|
||||
|
||||
expect(await unionAdSsgfPlugin.getPlatformVersion(), '42');
|
||||
});
|
||||
}
|
||||
void main() {}
|
||||
|
Reference in New Issue
Block a user