Browse Source

[new]第一次提交

zk 1 year ago
commit
49560e3d20
62 changed files with 2933 additions and 0 deletions
  1. 29 0
      .gitignore
  2. 33 0
      .metadata
  3. 4 0
      CHANGELOG.md
  4. 1 0
      LICENSE
  5. 12 0
      README.md
  6. 4 0
      analysis_options.yaml
  7. 9 0
      android/.gitignore
  8. 92 0
      android/build.gradle
  9. 23 0
      android/gradle.properties
  10. BIN
      android/gradle/wrapper/gradle-wrapper.jar
  11. 7 0
      android/gradle/wrapper/gradle-wrapper.properties
  12. 249 0
      android/gradlew
  13. 92 0
      android/gradlew.bat
  14. 1 0
      android/settings.gradle
  15. 2 0
      android/src/main/AndroidManifest.xml
  16. 105 0
      android/src/main/java/com/atmob/flutter_ad/FlutterAdPlugin.java
  17. 26 0
      android/src/main/java/com/atmob/flutter_ad/FlutterAdViewPlugin.java
  18. 149 0
      android/src/main/java/com/atmob/flutter_ad/bannerad/BannerAdView.java
  19. 38 0
      android/src/main/java/com/atmob/flutter_ad/bannerad/BannerAdViewFactory.java
  20. 89 0
      android/src/main/java/com/atmob/flutter_ad/config/FlutterAtmobAdConfig.java
  21. 7 0
      android/src/main/java/com/atmob/flutter_ad/constants/CommonParams.java
  22. 17 0
      android/src/main/java/com/atmob/flutter_ad/constants/FlutterAdMethod.java
  23. 16 0
      android/src/main/java/com/atmob/flutter_ad/constants/FlutterListenerMethod.java
  24. 13 0
      android/src/main/java/com/atmob/flutter_ad/constants/FlutterViewConfig.java
  25. 123 0
      android/src/main/java/com/atmob/flutter_ad/interstitial/InterstitialAdCall.java
  26. 152 0
      android/src/main/java/com/atmob/flutter_ad/nativead/NativeAdView.java
  27. 38 0
      android/src/main/java/com/atmob/flutter_ad/nativead/NativeAdViewFactory.java
  28. 127 0
      android/src/main/java/com/atmob/flutter_ad/splash/SplashAdView.java
  29. 36 0
      android/src/main/java/com/atmob/flutter_ad/splash/SplashAdViewFactory.java
  30. 72 0
      android/src/main/java/com/atmob/flutter_ad/utils/FlutterParamsUtils.java
  31. 12 0
      android/src/main/java/com/atmob/flutter_ad/utils/SizeUtils.java
  32. 18 0
      android/src/main/java/com/atmob/flutter_ad/utils/ViewUtils.java
  33. 80 0
      android/src/main/java/com/atmob/flutter_ad/video/RewardVideoAdCall.java
  34. 40 0
      android/src/main/java/com/atmob/flutter_ad/widget/AdLoadContainer.java
  35. 29 0
      android/src/test/java/com/atmob/flutter_ad/FlutterAdPluginTest.java
  36. 38 0
      ios/.gitignore
  37. 0 0
      ios/Assets/.gitkeep
  38. 19 0
      ios/Classes/FlutterAdPlugin.swift
  39. 14 0
      ios/Resources/PrivacyInfo.xcprivacy
  40. 29 0
      ios/flutter_ad.podspec
  41. 24 0
      lib/flutter_ad.dart
  42. 80 0
      lib/src/config/ad_config_info.dart
  43. 90 0
      lib/src/config/atmob_ad_config.dart
  44. 31 0
      lib/src/constants/ad_constant.dart
  45. 26 0
      lib/src/constants/flutter_method.dart
  46. 17 0
      lib/src/constants/view_type.dart
  47. 91 0
      lib/src/core/flutter_ad_method_channel.dart
  48. 55 0
      lib/src/core/flutter_ad_platform.dart
  49. 45 0
      lib/src/core/flutter_atmob_ad.dart
  50. 3 0
      lib/src/listener/ad_action_listener.dart
  51. 13 0
      lib/src/listener/atmob_ad_listener.dart
  52. 27 0
      lib/src/listener/banner_ad_listener.dart
  53. 27 0
      lib/src/listener/interstitial_ad_listener.dart
  54. 27 0
      lib/src/listener/interstitial_full_ad_listener.dart
  55. 27 0
      lib/src/listener/native_ad_listener.dart
  56. 31 0
      lib/src/listener/reward_video_ad_listener.dart
  57. 31 0
      lib/src/listener/splash_ad_listener.dart
  58. 53 0
      lib/src/utils/flutter_ad_listener_method_channel_helper.dart
  59. 111 0
      lib/src/widget/flutter_banner_ad_view.dart
  60. 114 0
      lib/src/widget/flutter_native_ad_view.dart
  61. 93 0
      lib/src/widget/flutter_splash_ad_view.dart
  62. 72 0
      pubspec.yaml

+ 29 - 0
.gitignore

@@ -0,0 +1,29 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
+/pubspec.lock
+**/doc/api/
+.dart_tool/
+build/

+ 33 - 0
.metadata

@@ -0,0 +1,33 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+  revision: "80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819"
+  channel: "stable"
+
+project_type: plugin
+
+# Tracks metadata for the flutter migrate command
+migration:
+  platforms:
+    - platform: root
+      create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
+      base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
+    - platform: android
+      create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
+      base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
+    - platform: ios
+      create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
+      base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
+
+  # User provided section
+
+  # List of Local paths (relative to this file) that should be
+  # ignored by the migrate tool.
+  #
+  # Files that are not part of the templates will be ignored by default.
+  unmanaged_files:
+    - 'lib/main.dart'
+    - 'ios/Runner.xcodeproj/project.pbxproj'

+ 4 - 0
CHANGELOG.md

@@ -0,0 +1,4 @@
+## 0.0.1
+
+* 新增Android端广告插件
+* sdk:plus.ad:ad:2.9.6-SNAPSHOT

+ 1 - 0
LICENSE

@@ -0,0 +1 @@
+TODO: Add your license here.

+ 12 - 0
README.md

@@ -0,0 +1,12 @@
+# flutter_ad
+
+国内广告插件
+
+## Getting Started
+
+FlutterAtmobAd 调用初始化、激励视频、插屏、插全屏
+FlutterSplashAdView 调用开屏广告
+FlutterBannerAdView 调用Banner广告
+FlutterNativeAdView 调用原生广告
+
+

+ 4 - 0
analysis_options.yaml

@@ -0,0 +1,4 @@
+include: package:flutter_lints/flutter.yaml
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options

+ 9 - 0
android/.gitignore

@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.cxx

+ 92 - 0
android/build.gradle

@@ -0,0 +1,92 @@
+group = "com.atmob.flutter_ad"
+version = "1.0"
+
+buildscript {
+    repositories {
+        google()
+        mavenCentral()
+    }
+
+    dependencies {
+        classpath("com.android.tools.build:gradle:7.3.0")
+    }
+}
+
+
+rootProject.allprojects {
+    repositories {
+        google()
+        mavenCentral()
+        maven {
+            credentials {
+                username "$atmob_maven_username"
+                password "$atmob_maven_password"
+            }
+            allowInsecureProtocol = true
+            url "$atmob_maven_url/repository/android-group/"
+        }
+    }
+}
+
+// 加载 local.properties 文件
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+    localPropertiesFile.withInputStream { stream ->
+        localProperties.load(stream)
+    }
+}
+
+// 读取变量
+def flutterSdk = localProperties.getProperty('flutter.sdk')
+
+apply plugin: "com.android.library"
+
+android {
+    if (project.android.hasProperty("namespace")) {
+        namespace = "com.atmob.flutter_ad"
+    }
+
+    compileSdk = 34
+
+    compileOptions {
+        sourceCompatibility = JavaVersion.VERSION_1_8
+        targetCompatibility = JavaVersion.VERSION_1_8
+    }
+
+    defaultConfig {
+        minSdk = 21
+    }
+
+    dependencies {
+        //flutter
+        compileOnly files("$flutterSdk/bin/cache/artifacts/engine/android-arm/flutter.jar")
+
+        //AndroidX
+        compileOnly "androidx.annotation:annotation:1.1.0"
+
+        implementation "androidx.appcompat:appcompat:1.6.1"
+
+        implementation "androidx.recyclerview:recyclerview:1.3.0"
+        //广告模块
+        implementation "plus.ad:ad:2.9.7-SNAPSHOT"
+
+    }
+
+    testOptions {
+        unitTests.all {
+            testLogging {
+                events "passed", "skipped", "failed", "standardOut", "standardError"
+                outputs.upToDateWhen { false }
+                showStandardStreams = true
+            }
+        }
+    }
+
+    configurations.configureEach {
+        resolutionStrategy {
+            // don't cache changing modules at all
+            cacheChangingModulesFor 10, 'seconds'
+        }
+    }
+}

+ 23 - 0
android/gradle.properties

@@ -0,0 +1,23 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
+android.enableJetifier=true
+android.injected.testOnly=false

BIN
android/gradle/wrapper/gradle-wrapper.jar


+ 7 - 0
android/gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists

+ 249 - 0
android/gradlew

@@ -0,0 +1,249 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+#   Gradle start up script for POSIX generated by Gradle.
+#
+#   Important for running:
+#
+#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+#       noncompliant, but you have some other compliant shell such as ksh or
+#       bash, then to run this script, type that shell name before the whole
+#       command line, like:
+#
+#           ksh Gradle
+#
+#       Busybox and similar reduced shells will NOT work, because this script
+#       requires all of these POSIX shell features:
+#         * functions;
+#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+#         * compound commands having a testable exit status, especially «case»;
+#         * various built-in commands including «command», «set», and «ulimit».
+#
+#   Important for patching:
+#
+#   (2) This script targets any POSIX shell, so it avoids extensions provided
+#       by Bash, Ksh, etc; in particular arrays are avoided.
+#
+#       The "traditional" practice of packing multiple parameters into a
+#       space-separated string is a well documented source of bugs and security
+#       problems, so this is (mostly) avoided, by progressively accumulating
+#       options in "$@", and eventually passing that to Java.
+#
+#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+#       see the in-line comments for details.
+#
+#       There are tweaks for specific operating systems such as AIX, CygWin,
+#       Darwin, MinGW, and NonStop.
+#
+#   (3) This script is generated from the Groovy template
+#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       within the Gradle project.
+#
+#       You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
+    [ -h "$app_path" ]
+do
+    ls=$( ls -ld "$app_path" )
+    link=${ls#*' -> '}
+    case $link in             #(
+      /*)   app_path=$link ;; #(
+      *)    app_path=$APP_HOME$link ;;
+    esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+    echo "$*"
+} >&2
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in                #(
+  CYGWIN* )         cygwin=true  ;; #(
+  Darwin* )         darwin=true  ;; #(
+  MSYS* | MINGW* )  msys=true    ;; #(
+  NONSTOP* )        nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD=$JAVA_HOME/jre/sh/java
+    else
+        JAVACMD=$JAVA_HOME/bin/java
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD=java
+    if ! command -v java >/dev/null 2>&1
+    then
+        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+    case $MAX_FD in #(
+      max*)
+        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
+        MAX_FD=$( ulimit -H -n ) ||
+            warn "Could not query maximum file descriptor limit"
+    esac
+    case $MAX_FD in  #(
+      '' | soft) :;; #(
+      *)
+        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
+        ulimit -n "$MAX_FD" ||
+            warn "Could not set maximum file descriptor limit to $MAX_FD"
+    esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+#   * args from the command line
+#   * the main class name
+#   * -classpath
+#   * -D...appname settings
+#   * --module-path (only if needed)
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+    JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    for arg do
+        if
+            case $arg in                                #(
+              -*)   false ;;                            # don't mess with options #(
+              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
+                    [ -e "$t" ] ;;                      #(
+              *)    false ;;
+            esac
+        then
+            arg=$( cygpath --path --ignore --mixed "$arg" )
+        fi
+        # Roll the args list around exactly as many times as the number of
+        # args, so each arg winds up back in the position where it started, but
+        # possibly modified.
+        #
+        # NB: a `for` loop captures its iteration list before it begins, so
+        # changing the positional parameters here affects neither the number of
+        # iterations, nor the values presented in `arg`.
+        shift                   # remove old arg
+        set -- "$@" "$arg"      # push replacement arg
+    done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+#     and any embedded shellness will be escaped.
+#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+#     treated as '${Hostname}' itself on the command line.
+
+set -- \
+        "-Dorg.gradle.appname=$APP_BASE_NAME" \
+        -classpath "$CLASSPATH" \
+        org.gradle.wrapper.GradleWrapperMain \
+        "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+    die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+#   set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+        xargs -n1 |
+        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+        tr '\n' ' '
+    )" '"$@"'
+
+exec "$JAVACMD" "$@"

+ 92 - 0
android/gradlew.bat

@@ -0,0 +1,92 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem      https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

+ 1 - 0
android/settings.gradle

@@ -0,0 +1 @@
+rootProject.name = 'flutter_ad'

+ 2 - 0
android/src/main/AndroidManifest.xml

@@ -0,0 +1,2 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.atmob.flutter_ad"></manifest>

+ 105 - 0
android/src/main/java/com/atmob/flutter_ad/FlutterAdPlugin.java

@@ -0,0 +1,105 @@
+package com.atmob.flutter_ad;
+
+import android.app.Activity;
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.atmob.flutter_ad.config.FlutterAtmobAdConfig;
+import com.atmob.flutter_ad.constants.FlutterAdMethod;
+import com.atmob.flutter_ad.interstitial.InterstitialAdCall;
+import com.atmob.flutter_ad.video.RewardVideoAdCall;
+import com.atmob.logging.AtmobLog;
+
+import io.flutter.BuildConfig;
+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;
+
+/**
+ * FlutterAdPlugin
+ */
+public class FlutterAdPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware {
+
+    final String TAG = "FlutterAdPlugin";
+    /// The MethodChannel that will the communication between Flutter and native Android
+    ///
+    /// This local reference serves to register the plugin with the Flutter Engine and unregister it
+    /// when the Flutter Engine is detached from the Activity
+    private MethodChannel channel;
+    private Activity mActivity;
+    private Context applicationContext;
+    private FlutterPluginBinding flutterPluginBinding;
+
+    @Override
+    public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
+        AtmobLog.logEnable(BuildConfig.DEBUG);
+        AtmobLog.d(TAG, "onAttachedToEngine...");
+        applicationContext = flutterPluginBinding.getApplicationContext();
+        channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "flutter_ad");
+        channel.setMethodCallHandler(this);
+        this.flutterPluginBinding = flutterPluginBinding;
+    }
+
+
+    @Override
+    public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
+        AtmobLog.d(TAG, "onMethodCall..." + call.method);
+        try {
+            switch (call.method) {
+                case FlutterAdMethod.initAd:
+                    FlutterAtmobAdConfig.initAd(applicationContext, call, result);
+                    break;
+                case FlutterAdMethod.loadInterstitial:
+                    InterstitialAdCall.loadInterstitial(applicationContext, channel, call, result);
+                    break;
+                case FlutterAdMethod.loadInterstitialFull:
+                    InterstitialAdCall.loadInterstitialFull(applicationContext, channel, call, result);
+                    break;
+                case FlutterAdMethod.loadRewardVideo:
+                    RewardVideoAdCall.loadRewardVideo(applicationContext, channel, call, result);
+                    break;
+                default:
+                    result.notImplemented();
+                    break;
+
+            }
+        } catch (Exception e) {
+            result.error("Exception", e.getMessage(), e.getStackTrace());
+        }
+    }
+
+    @Override
+    public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
+        AtmobLog.d(TAG, "onDetachedFromEngine...");
+        channel.setMethodCallHandler(null);
+    }
+
+    @Override
+    public void onAttachedToActivity(@NonNull ActivityPluginBinding activityPluginBinding) {
+        AtmobLog.d(TAG, "onAttachedToActivity...");
+        mActivity = activityPluginBinding.getActivity();
+        FlutterAdViewPlugin.registerWith(flutterPluginBinding, mActivity);
+    }
+
+    @Override
+    public void onDetachedFromActivityForConfigChanges() {
+
+    }
+
+    @Override
+    public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding activityPluginBinding) {
+
+    }
+
+    @Override
+    public void onDetachedFromActivity() {
+        mActivity = null;
+        AtmobLog.d(TAG, "onDetachedFromActivity...");
+    }
+}

+ 26 - 0
android/src/main/java/com/atmob/flutter_ad/FlutterAdViewPlugin.java

@@ -0,0 +1,26 @@
+package com.atmob.flutter_ad;
+
+
+import android.app.Activity;
+
+import com.atmob.flutter_ad.bannerad.BannerAdViewFactory;
+import com.atmob.flutter_ad.constants.FlutterViewConfig;
+import com.atmob.flutter_ad.nativead.NativeAdViewFactory;
+import com.atmob.flutter_ad.splash.SplashAdViewFactory;
+
+import io.flutter.embedding.engine.plugins.FlutterPlugin;
+
+public class FlutterAdViewPlugin {
+
+
+    public static void registerWith(FlutterPlugin.FlutterPluginBinding flutterPluginBinding, Activity mActivity) {
+
+        flutterPluginBinding.getPlatformViewRegistry().registerViewFactory(FlutterViewConfig.splashAdView, new SplashAdViewFactory(mActivity, flutterPluginBinding.getBinaryMessenger()));
+
+        flutterPluginBinding.getPlatformViewRegistry().registerViewFactory(FlutterViewConfig.nativeAdView, new NativeAdViewFactory(mActivity, flutterPluginBinding.getBinaryMessenger()));
+
+        flutterPluginBinding.getPlatformViewRegistry().registerViewFactory(FlutterViewConfig.bannerAdView, new BannerAdViewFactory(mActivity, flutterPluginBinding.getBinaryMessenger()));
+
+
+    }
+}

+ 149 - 0
android/src/main/java/com/atmob/flutter_ad/bannerad/BannerAdView.java

@@ -0,0 +1,149 @@
+package com.atmob.flutter_ad.bannerad;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+import androidx.annotation.Nullable;
+
+import com.atmob.ad.listener.BannerListener;
+import com.atmob.flutter_ad.constants.FlutterListenerMethod;
+import com.atmob.flutter_ad.constants.FlutterViewConfig;
+import com.atmob.flutter_ad.utils.FlutterParamsUtils;
+import com.atmob.flutter_ad.utils.SizeUtils;
+import com.atmob.flutter_ad.widget.AdLoadContainer;
+import com.atmob.logging.AtmobLog;
+import com.atmob.sdk.AtmobAdNative;
+import com.atmob.sdk.AtmobAdSdk;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugin.common.MethodChannel;
+import io.flutter.plugin.platform.PlatformView;
+import io.flutter.plugin.platform.PlatformViewWrapper;
+
+public class BannerAdView implements PlatformView {
+
+    private final String TAG = "BannerAdView";
+
+    private final Context context;
+    private final Activity activity;
+
+    private final BinaryMessenger messenger;
+
+    private final int viewId;
+
+    Map<String, Object> args;
+
+    private AdLoadContainer mContainer = null;
+
+    private MethodChannel channel;
+
+    int adFuncId;
+
+    AtmobAdNative adNative;
+
+    public BannerAdView(Context context, Activity activity, BinaryMessenger messenger, int viewId, Map<String, Object> args) {
+        this.context = context;
+        this.activity = activity;
+        this.messenger = messenger;
+        this.viewId = viewId;
+        this.args = args;
+        initParams();
+        initView();
+        loadBannerAd();
+    }
+
+    void initView() {
+        mContainer = new AdLoadContainer(context);
+        mContainer.setLayoutParams(new AdLoadContainer.LayoutParams(AdLoadContainer.LayoutParams.MATCH_PARENT, AdLoadContainer.LayoutParams.WRAP_CONTENT));
+
+        mContainer.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+            @Override
+            public void onGlobalLayout() {
+                if (mContainer.getMeasuredHeight() == 0) {
+                    return;
+                }
+                Map<String, Object> map = new HashMap<>();
+                map.put("width", SizeUtils.pxToDp(context, mContainer.getMeasuredWidth()));
+                map.put("height", SizeUtils.pxToDp(context, mContainer.getMeasuredHeight()));
+                channel.invokeMethod(FlutterListenerMethod.size, map);
+                mContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+            }
+        });
+    }
+
+
+    void initParams() {
+        if (args == null) {
+            return;
+        }
+        adFuncId = FlutterParamsUtils.getInt(args, "adFuncId", 0);
+        AtmobLog.d(TAG, "adFuncId:" + adFuncId);
+        channel = new MethodChannel(messenger, FlutterViewConfig.bannerAdView + "_" + viewId);
+    }
+
+
+    void loadBannerAd() {
+        adNative = AtmobAdSdk.getInstance().createAdNative(context);
+        adNative.loadBannerExpress(adFuncId, mContainer, new BannerListener() {
+            @Override
+            public void onShow() {
+                AtmobLog.d(TAG, "onShow");
+                if (channel != null) {
+                    channel.invokeMethod(FlutterListenerMethod.onShow, null);
+                }
+            }
+
+            @Override
+            public void onClose() {
+                AtmobLog.d(TAG, "onClose");
+                if (channel != null) {
+                    channel.invokeMethod(FlutterListenerMethod.onClose, null);
+                }
+            }
+
+            @Override
+            public void onFail(String msg) {
+                AtmobLog.d(TAG, "onFail:" + msg);
+                if (channel != null) {
+                    channel.invokeMethod(FlutterListenerMethod.onFail, msg);
+                }
+            }
+
+            @Override
+            public void onClick() {
+                AtmobLog.d(TAG, "onClick");
+                if (channel != null) {
+                    channel.invokeMethod(FlutterListenerMethod.onClick, null);
+                }
+            }
+        });
+    }
+
+    @Nullable
+    @Override
+    public View getView() {
+        if (mContainer.getChildCount() == 0) {
+            return mContainer;
+        } else if (!(mContainer.getParent() instanceof PlatformViewWrapper)) {
+            return (View) mContainer.getParent();
+        } else {
+            return mContainer;
+        }
+    }
+
+
+    @Override
+    public void dispose() {
+        AtmobLog.d(TAG, "dispose");
+        if (mContainer != null) {
+            mContainer.removeAllViews();
+        }
+    }
+
+
+}

+ 38 - 0
android/src/main/java/com/atmob/flutter_ad/bannerad/BannerAdViewFactory.java

@@ -0,0 +1,38 @@
+package com.atmob.flutter_ad.bannerad;
+
+import android.app.Activity;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.atmob.flutter_ad.nativead.NativeAdView;
+
+import java.util.Map;
+
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugin.common.StandardMessageCodec;
+import io.flutter.plugin.platform.PlatformView;
+import io.flutter.plugin.platform.PlatformViewFactory;
+
+public class BannerAdViewFactory extends PlatformViewFactory {
+
+    private final Activity activity;
+    private final BinaryMessenger messenger;
+
+    public BannerAdViewFactory(Activity activity, BinaryMessenger messenger) {
+        super(StandardMessageCodec.INSTANCE);
+        this.activity = activity;
+        this.messenger = messenger;
+    }
+
+    @NonNull
+    @Override
+    public PlatformView create(Context context, int viewId, @Nullable Object args) {
+        Map<String, Object> params = null;
+        if (args instanceof Map) {
+            params = (Map<String, Object>) args;
+        }
+        return new BannerAdView(context, activity, messenger, viewId, params);
+    }
+}

+ 89 - 0
android/src/main/java/com/atmob/flutter_ad/config/FlutterAtmobAdConfig.java

@@ -0,0 +1,89 @@
+package com.atmob.flutter_ad.config;
+
+import android.content.Context;
+import android.text.TextUtils;
+
+import com.atmob.flutter_ad.utils.FlutterParamsUtils;
+import com.atmob.logging.AtmobLog;
+import com.atmob.sdk.AtmobAdConfig;
+import com.atmob.sdk.AtmobAdSdk;
+import com.atmob.sdk.GromoreSplashInfo;
+
+import java.util.Map;
+
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+
+public class FlutterAtmobAdConfig {
+
+
+    final static String TAG = "FlutterAtmobAdConfig";
+
+    /**
+     * 参考dart层传参
+     * 'atmobAppKey': atmobAppKey,
+     * 'atmobChanel': atmobChannelInfo?.atmobChanel,
+     * 'atmobAppId': atmobChannelInfo?.atmobAppId,
+     * 'atmobTgPlatform': atmobChannelInfo?.atmobTgPlatform,
+     * //穿山甲
+     * 'csjAppId': csjConfig?.appId,
+     * //ks
+     * 'ksAppId': ksConfig?.appId,
+     * //topon
+     * 'toponAppId': toponConfig?.appId,
+     * 'toponAppKey': toponConfig?.appKey,
+     * //gromore
+     * 'gromorePlatformId': gromoreSplashInfo?.platformId,
+     * 'gromorePositionId': gromoreSplashInfo?.positionId,
+     * 'gromoreAppId': gromoreSplashInfo?.appId,
+     * 'gromoreAppKey': gromoreSplashInfo?.getAppKey,
+     * 'debug': debug,
+     *
+     * @param applicationContext
+     * @param call
+     * @param result
+     */
+    public static void initAd(Context applicationContext, MethodCall call, MethodChannel.Result result) {
+        try {
+            Map<String, Object> params = call.arguments();
+            String atmobAppKey = (String) params.get("atmobAppKey");
+            if (TextUtils.isEmpty(atmobAppKey)) {
+                result.error("atmobAppKey is empty", "atmobAppKey is empty", null);
+                return;
+            }
+            String atmobChanel = FlutterParamsUtils.getString(params, "atmobChanel");
+            int atmobAppId = FlutterParamsUtils.getInt(params, "atmobAppId", 0);
+            int atmobTgPlatform = FlutterParamsUtils.getInt(params, "atmobAppId", 0);
+            String csjAppId = FlutterParamsUtils.getString(params, "csjAppId");
+            String toponAppKey = FlutterParamsUtils.getString(params, "toponAppKey");
+            String toponAppId = FlutterParamsUtils.getString(params, "toponAppId");
+
+            boolean debug = FlutterParamsUtils.getBoolean(params, "debug", false);
+
+            int gromorePlatformId = FlutterParamsUtils.getInt(params, "gromorePlatformId", -1);
+            String gromorePositionId = FlutterParamsUtils.getString(params, "gromorePositionId");
+            String gromoreAppId = FlutterParamsUtils.getString(params, "gromoreAppId");
+            String gromoreAppKey = FlutterParamsUtils.getString(params, "gromoreAppKey");
+
+
+            AtmobAdConfig.Build build = new AtmobAdConfig.Build(atmobAppKey);
+            build.atmobInfo(atmobChanel, atmobAppId, atmobTgPlatform);
+            build.csjAppId(csjAppId);
+            build.toponAppId(toponAppKey, toponAppId);
+
+            if (gromorePositionId != null && gromoreAppId != null) {
+                GromoreSplashInfo gromoreSplashInfo = new GromoreSplashInfo(gromorePlatformId, gromorePositionId, gromoreAppId, gromoreAppKey);
+                build.gromoreSplashInfo(gromoreSplashInfo);
+            }
+
+            build.debug(debug);
+            AtmobAdSdk.getInstance().initAd(applicationContext, build.build());
+            AtmobLog.d(TAG, "initAd:success");
+            result.success(null);
+        } catch (Exception e) {
+            AtmobLog.d(TAG, "initAd:" + e.getMessage());
+            result.error("Exception", e.getMessage(), e.getStackTrace());
+        }
+    }
+
+}

+ 7 - 0
android/src/main/java/com/atmob/flutter_ad/constants/CommonParams.java

@@ -0,0 +1,7 @@
+package com.atmob.flutter_ad.constants;
+
+public class CommonParams {
+
+
+    public static final String AdFuncId = "adFuncId";
+}

+ 17 - 0
android/src/main/java/com/atmob/flutter_ad/constants/FlutterAdMethod.java

@@ -0,0 +1,17 @@
+package com.atmob.flutter_ad.constants;
+
+public class FlutterAdMethod {
+    private FlutterAdMethod() {
+    }
+
+    public static final String flutterAdMethod = "flutterAdMethod";
+    //
+
+    public static final String initAd = "initAd";
+
+    public static final String loadInterstitial = "loadInterstitial";
+
+    public static final String loadInterstitialFull = "loadInterstitialFull";
+
+    public static final String loadRewardVideo = "loadRewardVideo";
+}

+ 16 - 0
android/src/main/java/com/atmob/flutter_ad/constants/FlutterListenerMethod.java

@@ -0,0 +1,16 @@
+package com.atmob.flutter_ad.constants;
+
+public class FlutterListenerMethod {
+
+    private FlutterListenerMethod() {
+    }
+
+    public static final String onShow = "onShow";
+    public static final String onClose = "onClose";
+    public static final String onFail = "onFail";
+    public static final String onClick = "onClick";
+    public static final String onRewarded = "onRewarded";
+    public static final String action = "action";
+    public static final String size = "size";
+
+}

+ 13 - 0
android/src/main/java/com/atmob/flutter_ad/constants/FlutterViewConfig.java

@@ -0,0 +1,13 @@
+package com.atmob.flutter_ad.constants;
+
+public class FlutterViewConfig {
+
+    private FlutterViewConfig() {
+    }
+
+
+    public static final String splashAdView = "com.atmob.flutter_ad/splashAdView";
+    public static final String nativeAdView = "com.atmob.flutter_ad/nativeAdView";
+    public static final String bannerAdView = "com.atmob.flutter_ad/bannerAdView";
+
+}

+ 123 - 0
android/src/main/java/com/atmob/flutter_ad/interstitial/InterstitialAdCall.java

@@ -0,0 +1,123 @@
+package com.atmob.flutter_ad.interstitial;
+
+import android.content.Context;
+import android.text.TextUtils;
+
+import com.atmob.ad.listener.InterstitialFullListener;
+import com.atmob.ad.listener.InterstitialListener;
+import com.atmob.flutter_ad.constants.CommonParams;
+import com.atmob.flutter_ad.constants.FlutterAdMethod;
+import com.atmob.flutter_ad.constants.FlutterListenerMethod;
+import com.atmob.flutter_ad.utils.FlutterParamsUtils;
+import com.atmob.logging.AtmobLog;
+import com.atmob.sdk.AtmobAdNative;
+import com.atmob.sdk.AtmobAdSdk;
+
+import java.util.Map;
+
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+
+public class InterstitialAdCall {
+
+    private static final String TAG = "InterstitialAdCall";
+
+    public static void loadInterstitial(Context context, MethodChannel channel, MethodCall call, MethodChannel.Result result) {
+        Map<String, Object> params = call.arguments();
+        String adFuncId = null;
+        if (params != null && params.containsKey(CommonParams.AdFuncId)) {
+            adFuncId = (String) params.get(CommonParams.AdFuncId);
+        }
+        if (TextUtils.isEmpty(adFuncId)) {
+            result.error("AdFuncId is empty", "AdFuncId is empty", null);
+            return;
+        }
+        int intAdFuncId;
+        try {
+            intAdFuncId = Integer.parseInt(adFuncId);
+        } catch (NumberFormatException e) {
+            result.error("AdFuncId is not a number", "AdFuncId is not a number", null);
+            return;
+        }
+        AtmobLog.d(TAG, "loadInterstitial: adFuncId = " + adFuncId);
+        AtmobAdNative adNative = AtmobAdSdk.getInstance().createAdNative(context);
+        String finalAdFuncId = adFuncId;
+        adNative.loadInterstitial(intAdFuncId, new InterstitialListener() {
+            @Override
+            public void onShow() {
+                AtmobLog.d(TAG, "loadInterstitial..onShow");
+                channel.invokeMethod(FlutterAdMethod.flutterAdMethod, FlutterParamsUtils.createCommonParams(finalAdFuncId, FlutterListenerMethod.onShow));
+            }
+
+            @Override
+            public void onClose() {
+                AtmobLog.d(TAG, "loadInterstitial..onClose");
+                channel.invokeMethod(FlutterAdMethod.flutterAdMethod, FlutterParamsUtils.createCommonParams(finalAdFuncId, FlutterListenerMethod.onClose));
+
+            }
+
+            @Override
+            public void onFail(String s) {
+                AtmobLog.d(TAG, "loadInterstitial..onFail: " + s);
+                channel.invokeMethod(FlutterAdMethod.flutterAdMethod, FlutterParamsUtils.createCommonParams(finalAdFuncId, FlutterListenerMethod.onFail, s));
+
+            }
+
+            @Override
+            public void onClick() {
+                AtmobLog.d(TAG, "loadInterstitial..onClick");
+                channel.invokeMethod(FlutterAdMethod.flutterAdMethod, FlutterParamsUtils.createCommonParams(finalAdFuncId, FlutterListenerMethod.onClick));
+
+            }
+        });
+    }
+
+    public static void loadInterstitialFull(Context context, MethodChannel channel, MethodCall call, MethodChannel.Result result) {
+        Map<String, Object> params = call.arguments();
+        String adFuncId = null;
+        if (params != null && params.containsKey(CommonParams.AdFuncId)) {
+            adFuncId = (String) params.get(CommonParams.AdFuncId);
+        }
+        if (TextUtils.isEmpty(adFuncId)) {
+            result.error("AdFuncId is empty", "AdFuncId is empty", null);
+            return;
+        }
+        int intAdFuncId;
+        try {
+            intAdFuncId = Integer.parseInt(adFuncId);
+        } catch (NumberFormatException e) {
+            result.error("AdFuncId is not a number", "AdFuncId is not a number", null);
+            return;
+        }
+        AtmobLog.d(TAG, "loadInterstitialFull: adFuncId = " + adFuncId);
+        AtmobAdNative adNative = AtmobAdSdk.getInstance().createAdNative(context);
+        String finalAdFuncId = adFuncId;
+        adNative.loadInterstitialFull(intAdFuncId, new InterstitialFullListener() {
+            @Override
+            public void onShow() {
+                AtmobLog.d(TAG, "loadInterstitialFull..onShow");
+                channel.invokeMethod(FlutterAdMethod.flutterAdMethod, FlutterParamsUtils.createCommonParams(finalAdFuncId, FlutterListenerMethod.onShow));
+            }
+
+            @Override
+            public void onClose() {
+                AtmobLog.d(TAG, "loadInterstitialFull..onClose");
+                channel.invokeMethod(FlutterAdMethod.flutterAdMethod, FlutterParamsUtils.createCommonParams(finalAdFuncId, FlutterListenerMethod.onClose));
+
+            }
+
+            @Override
+            public void onFail(String s) {
+                AtmobLog.d(TAG, "loadInterstitialFull..onFail: " + s);
+                channel.invokeMethod(FlutterAdMethod.flutterAdMethod, FlutterParamsUtils.createCommonParams(finalAdFuncId, FlutterListenerMethod.onFail, s));
+
+            }
+
+            @Override
+            public void onClick() {
+                AtmobLog.d(TAG, "loadInterstitialFull..onClick");
+                channel.invokeMethod(FlutterAdMethod.flutterAdMethod, FlutterParamsUtils.createCommonParams(finalAdFuncId, FlutterListenerMethod.onClick));
+            }
+        });
+    }
+}

+ 152 - 0
android/src/main/java/com/atmob/flutter_ad/nativead/NativeAdView.java

@@ -0,0 +1,152 @@
+package com.atmob.flutter_ad.nativead;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+import androidx.annotation.Nullable;
+
+import com.atmob.ad.listener.NativeExpressListener;
+import com.atmob.flutter_ad.constants.FlutterListenerMethod;
+import com.atmob.flutter_ad.constants.FlutterViewConfig;
+import com.atmob.flutter_ad.utils.FlutterParamsUtils;
+import com.atmob.flutter_ad.utils.SizeUtils;
+import com.atmob.flutter_ad.widget.AdLoadContainer;
+import com.atmob.logging.AtmobLog;
+import com.atmob.sdk.AtmobAdNative;
+import com.atmob.sdk.AtmobAdSdk;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugin.common.MethodChannel;
+import io.flutter.plugin.platform.PlatformView;
+import io.flutter.plugin.platform.PlatformViewWrapper;
+
+public class NativeAdView implements PlatformView {
+
+    private final String TAG = "NativeAdView";
+
+    private final Context context;
+    private final Activity activity;
+
+    private final BinaryMessenger messenger;
+
+    private final int viewId;
+
+    Map<String, Object> args;
+
+    private AdLoadContainer mContainer = null;
+
+    private MethodChannel channel;
+
+    int adFuncId;
+
+    AtmobAdNative adNative;
+
+    public NativeAdView(Context context, Activity activity, BinaryMessenger messenger, int viewId, Map<String, Object> args) {
+        this.context = context;
+        this.activity = activity;
+        this.messenger = messenger;
+        this.viewId = viewId;
+        this.args = args;
+        initParams();
+        initView();
+        loadNativeAd();
+    }
+
+    void initView() {
+        mContainer = new AdLoadContainer(context);
+        mContainer.setLayoutParams(new AdLoadContainer.LayoutParams(AdLoadContainer.LayoutParams.MATCH_PARENT, AdLoadContainer.LayoutParams.WRAP_CONTENT));
+
+        mContainer.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+            @Override
+            public void onGlobalLayout() {
+                if (mContainer.getMeasuredHeight() == 0) {
+                    return;
+                }
+                Map<String, Object> map = new HashMap<>();
+                map.put("width", SizeUtils.pxToDp(context, mContainer.getMeasuredWidth()));
+                map.put("height", SizeUtils.pxToDp(context, mContainer.getMeasuredHeight()));
+                channel.invokeMethod(FlutterListenerMethod.size, map);
+                mContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+            }
+        });
+    }
+
+
+    void initParams() {
+        if (args == null) {
+            return;
+        }
+        adFuncId = FlutterParamsUtils.getInt(args, "adFuncId", 0);
+        AtmobLog.d(TAG, "adFuncId:" + adFuncId);
+        channel = new MethodChannel(messenger, FlutterViewConfig.nativeAdView + "_" + viewId);
+    }
+
+
+    void loadNativeAd() {
+        adNative = AtmobAdSdk.getInstance().createAdNative(context);
+        adNative.loadNativeExpress(adFuncId, mContainer, new NativeExpressListener() {
+            @Override
+            public void onShow() {
+                AtmobLog.d(TAG, "onShow");
+                if (channel != null) {
+                    channel.invokeMethod(FlutterListenerMethod.onShow, null);
+                }
+            }
+
+            @Override
+            public void onClose() {
+                AtmobLog.d(TAG, "onClose");
+                if (channel != null) {
+                    channel.invokeMethod(FlutterListenerMethod.onClose, null);
+                }
+            }
+
+            @Override
+            public void onFail(String msg) {
+                AtmobLog.d(TAG, "onFail:" + msg);
+                if (channel != null) {
+                    channel.invokeMethod(FlutterListenerMethod.onFail, msg);
+                }
+            }
+
+            @Override
+            public void onClick() {
+                AtmobLog.d(TAG, "onClick");
+                if (channel != null) {
+                    channel.invokeMethod(FlutterListenerMethod.onClick, null);
+                }
+            }
+        });
+    }
+
+    @Nullable
+    @Override
+    public View getView() {
+        if (mContainer.getChildCount() == 0) {
+            return mContainer;
+        } else if (!(mContainer.getParent() instanceof PlatformViewWrapper)) {
+            return (View) mContainer.getParent();
+        } else {
+            return mContainer;
+        }
+    }
+
+
+    @Override
+    public void dispose() {
+        AtmobLog.d(TAG, "dispose");
+        if (mContainer != null) {
+            mContainer.removeAllViews();
+        }
+        if (adNative != null) {
+            adNative.removeNativeExpress();
+        }
+    }
+
+
+}

+ 38 - 0
android/src/main/java/com/atmob/flutter_ad/nativead/NativeAdViewFactory.java

@@ -0,0 +1,38 @@
+package com.atmob.flutter_ad.nativead;
+
+import android.app.Activity;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.atmob.flutter_ad.splash.SplashAdView;
+
+import java.util.Map;
+
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugin.common.StandardMessageCodec;
+import io.flutter.plugin.platform.PlatformView;
+import io.flutter.plugin.platform.PlatformViewFactory;
+
+public class NativeAdViewFactory extends PlatformViewFactory {
+
+    private final Activity activity;
+    private final BinaryMessenger messenger;
+
+    public NativeAdViewFactory(Activity activity, BinaryMessenger messenger) {
+        super(StandardMessageCodec.INSTANCE);
+        this.activity = activity;
+        this.messenger = messenger;
+    }
+
+    @NonNull
+    @Override
+    public PlatformView create(Context context, int viewId, @Nullable Object args) {
+        Map<String, Object> params = null;
+        if (args instanceof Map) {
+            params = (Map<String, Object>) args;
+        }
+        return new NativeAdView(context, activity, messenger, viewId, params);
+    }
+}

+ 127 - 0
android/src/main/java/com/atmob/flutter_ad/splash/SplashAdView.java

@@ -0,0 +1,127 @@
+package com.atmob.flutter_ad.splash;
+
+import android.app.Activity;
+import android.content.Context;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import androidx.annotation.Nullable;
+
+import com.atmob.ad.listener.SplashListener;
+import com.atmob.flutter_ad.constants.FlutterListenerMethod;
+import com.atmob.flutter_ad.constants.FlutterViewConfig;
+import com.atmob.flutter_ad.utils.FlutterParamsUtils;
+import com.atmob.logging.AtmobLog;
+import com.atmob.sdk.AtmobAdNative;
+import com.atmob.sdk.AtmobAdSdk;
+
+import java.util.Map;
+
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugin.common.MethodChannel;
+import io.flutter.plugin.platform.PlatformView;
+
+public class SplashAdView implements PlatformView {
+
+
+    private final String TAG = "SplashAdView";
+
+    private final Context context;
+    private final Activity activity;
+
+    private final BinaryMessenger messenger;
+
+    private final int viewId;
+
+    Map<String, Object> args;
+
+    private FrameLayout mContainer = null;
+
+    private MethodChannel channel;
+
+    int adFuncId;
+
+    public SplashAdView(Context context, Activity activity, BinaryMessenger messenger, int viewId, Map<String, Object> args) {
+        this.context = context;
+        this.activity = activity;
+        this.messenger = messenger;
+        this.viewId = viewId;
+        this.args = args;
+        initParams();
+        initView();
+        loadSplashAd();
+    }
+
+
+    void initView() {
+        mContainer = new FrameLayout(context);
+        mContainer.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
+        channel = new MethodChannel(messenger, FlutterViewConfig.splashAdView + "_" + viewId);
+    }
+
+
+    void initParams() {
+        if (args == null) {
+            return;
+        }
+        adFuncId = FlutterParamsUtils.getInt(args, "adFuncId", 0);
+        AtmobLog.d(TAG, "adFuncId:" + adFuncId);
+    }
+
+
+    private void loadSplashAd() {
+        AtmobAdNative adNative = AtmobAdSdk.getInstance().createAdNative(context);
+        adNative.loadSplash(adFuncId, mContainer, () -> {
+                    AtmobLog.d(TAG, "action");
+                    if (channel != null) {
+                        channel.invokeMethod(FlutterListenerMethod.action, null);
+                    }
+                },
+                new SplashListener() {
+                    @Override
+                    public void onShow() {
+                        AtmobLog.d(TAG, "onShow");
+                        if (channel != null) {
+                            channel.invokeMethod(FlutterListenerMethod.onShow, null);
+                        }
+                    }
+
+                    @Override
+                    public void onClose() {
+                        AtmobLog.d(TAG, "onClose");
+                        if (channel != null) {
+                            channel.invokeMethod(FlutterListenerMethod.onClose, null);
+                        }
+                    }
+
+                    @Override
+                    public void onFail(String msg) {
+                        AtmobLog.d(TAG, "onFail:" + msg);
+                        if (channel != null) {
+                            channel.invokeMethod(FlutterListenerMethod.onFail, msg);
+                        }
+                    }
+
+                    @Override
+                    public void onClick() {
+                        AtmobLog.d(TAG, "onClick");
+                        if (channel != null) {
+                            channel.invokeMethod(FlutterListenerMethod.onClick, null);
+                        }
+                    }
+                });
+    }
+
+    @Nullable
+    @Override
+    public View getView() {
+        return mContainer;
+    }
+
+    @Override
+    public void dispose() {
+        if (mContainer != null) {
+            mContainer.removeAllViews();
+        }
+    }
+}

+ 36 - 0
android/src/main/java/com/atmob/flutter_ad/splash/SplashAdViewFactory.java

@@ -0,0 +1,36 @@
+package com.atmob.flutter_ad.splash;
+
+import android.app.Activity;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.Map;
+
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugin.common.StandardMessageCodec;
+import io.flutter.plugin.platform.PlatformView;
+import io.flutter.plugin.platform.PlatformViewFactory;
+
+public class SplashAdViewFactory extends PlatformViewFactory {
+
+    private final Activity activity;
+    private final BinaryMessenger messenger;
+
+    public SplashAdViewFactory(Activity activity, BinaryMessenger messenger) {
+        super(StandardMessageCodec.INSTANCE);
+        this.activity = activity;
+        this.messenger = messenger;
+    }
+
+    @NonNull
+    @Override
+    public PlatformView create(Context context, int viewId, @Nullable Object args) {
+        Map<String, Object> params = null;
+        if (args instanceof Map) {
+            params = (Map<String, Object>) args;
+        }
+        return new SplashAdView(context, activity, messenger, viewId, params);
+    }
+}

+ 72 - 0
android/src/main/java/com/atmob/flutter_ad/utils/FlutterParamsUtils.java

@@ -0,0 +1,72 @@
+package com.atmob.flutter_ad.utils;
+
+
+import android.text.TextUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class FlutterParamsUtils {
+
+
+    public static Map<String, Object> createCommonParams(String adFuncId, String method, String failMsg) {
+        Map<String, Object> params = new HashMap<>();
+        params.put("adFuncId", adFuncId);
+        params.put("method", method);
+        params.put("failMsg", failMsg);
+        return params;
+    }
+
+    public static Map<String, Object> createCommonParams(String adFuncId, String method) {
+        Map<String, Object> params = new HashMap<>();
+        params.put("adFuncId", adFuncId);
+        params.put("method", method);
+        return params;
+    }
+
+    public static String getString(Map<String, Object> params, String param) {
+        if (params == null) {
+            return null;
+        }
+        if (!params.containsKey(param)) {
+            return null;
+        }
+        Object target = params.get(param);
+        if (target == null) {
+            return null;
+        }
+        return (String) target;
+    }
+
+    public static int getInt(Map<String, Object> params, String param, int defaultValue) {
+        if (params == null) {
+            return defaultValue;
+        }
+        if (!params.containsKey(param)) {
+            return defaultValue;
+        }
+        Object target = params.get(param);
+        if (target == null) {
+            return defaultValue;
+        }
+        if (TextUtils.isDigitsOnly(params.toString())) {
+            return defaultValue;
+        }
+        return Integer.parseInt(target.toString());
+    }
+
+    public static boolean getBoolean(Map<String, Object> params, String param, boolean defaultValue) {
+        if (params == null) {
+            return defaultValue;
+        }
+        if (!params.containsKey(param)) {
+            return defaultValue;
+        }
+        Object target = params.get(param);
+        if (target == null) {
+            return defaultValue;
+        }
+        return (boolean) target;
+    }
+
+}

+ 12 - 0
android/src/main/java/com/atmob/flutter_ad/utils/SizeUtils.java

@@ -0,0 +1,12 @@
+package com.atmob.flutter_ad.utils;
+
+import android.content.Context;
+
+public class SizeUtils {
+
+
+    public static float pxToDp(Context context, int px) {
+        float density = context.getResources().getDisplayMetrics().density;
+        return px / density;
+    }
+}

+ 18 - 0
android/src/main/java/com/atmob/flutter_ad/utils/ViewUtils.java

@@ -0,0 +1,18 @@
+package com.atmob.flutter_ad.utils;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.atmob.logging.AtmobLog;
+
+public class ViewUtils {
+
+    public static void travelAllView(View view) {
+        AtmobLog.d("ViewUtils", "view: " + view.getClass() + ": w: " + view.getWidth() + ", h: " + view.getHeight());
+        ViewGroup parent;
+        while ((view.getParent() instanceof ViewGroup) && (parent = (ViewGroup) view.getParent()) != null) {
+            view = parent;
+            AtmobLog.d("ViewUtils", "travelAllView: " + view.getClass() + ": w: " + view.getWidth() + ", h: " + view.getHeight());
+        }
+    }
+}

+ 80 - 0
android/src/main/java/com/atmob/flutter_ad/video/RewardVideoAdCall.java

@@ -0,0 +1,80 @@
+package com.atmob.flutter_ad.video;
+
+import android.content.Context;
+import android.text.TextUtils;
+
+import com.atmob.ad.listener.InterstitialFullListener;
+import com.atmob.ad.listener.RewardVideoListener;
+import com.atmob.flutter_ad.constants.CommonParams;
+import com.atmob.flutter_ad.constants.FlutterAdMethod;
+import com.atmob.flutter_ad.constants.FlutterListenerMethod;
+import com.atmob.flutter_ad.utils.FlutterParamsUtils;
+import com.atmob.logging.AtmobLog;
+import com.atmob.sdk.AtmobAdNative;
+import com.atmob.sdk.AtmobAdSdk;
+
+import java.util.Map;
+
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+
+public class RewardVideoAdCall {
+
+    private static final String TAG = "RewardVideoAdCall";
+
+    public static void loadRewardVideo(Context context, MethodChannel channel, MethodCall call, MethodChannel.Result result) {
+        Map<String, Object> params = call.arguments();
+        String adFuncId = null;
+        if (params != null && params.containsKey(CommonParams.AdFuncId)) {
+            adFuncId = (String) params.get(CommonParams.AdFuncId);
+        }
+        if (TextUtils.isEmpty(adFuncId)) {
+            result.error("AdFuncId is empty", "AdFuncId is empty", null);
+            return;
+        }
+        int intAdFuncId;
+        try {
+            intAdFuncId = Integer.parseInt(adFuncId);
+        } catch (NumberFormatException e) {
+            result.error("AdFuncId is not a number", "AdFuncId is not a number", null);
+            return;
+        }
+        AtmobLog.d(TAG, "loadRewardVideo: adFuncId = " + adFuncId);
+        AtmobAdNative adNative = AtmobAdSdk.getInstance().createAdNative(context);
+        String finalAdFuncId = adFuncId;
+        adNative.loadRewardVideo(intAdFuncId, new RewardVideoListener() {
+            @Override
+            public void onRewarded() {
+                AtmobLog.d(TAG, "loadRewardVideo..onRewarded");
+                channel.invokeMethod(FlutterAdMethod.flutterAdMethod, FlutterParamsUtils.createCommonParams(finalAdFuncId, FlutterListenerMethod.onRewarded));
+            }
+
+            @Override
+            public void onShow() {
+                AtmobLog.d(TAG, "loadRewardVideo..onShow");
+                channel.invokeMethod(FlutterAdMethod.flutterAdMethod, FlutterParamsUtils.createCommonParams(finalAdFuncId, FlutterListenerMethod.onShow));
+            }
+
+            @Override
+            public void onClose() {
+                AtmobLog.d(TAG, "loadRewardVideo..onClose");
+                channel.invokeMethod(FlutterAdMethod.flutterAdMethod, FlutterParamsUtils.createCommonParams(finalAdFuncId, FlutterListenerMethod.onClose));
+
+            }
+
+            @Override
+            public void onFail(String s) {
+                AtmobLog.d(TAG, "loadRewardVideo..onFail: " + s);
+                channel.invokeMethod(FlutterAdMethod.flutterAdMethod, FlutterParamsUtils.createCommonParams(finalAdFuncId, FlutterListenerMethod.onFail, s));
+
+            }
+
+            @Override
+            public void onClick() {
+                AtmobLog.d(TAG, "loadRewardVideo..onClick");
+                channel.invokeMethod(FlutterAdMethod.flutterAdMethod, FlutterParamsUtils.createCommonParams(finalAdFuncId, FlutterListenerMethod.onClick));
+
+            }
+        });
+    }
+}

+ 40 - 0
android/src/main/java/com/atmob/flutter_ad/widget/AdLoadContainer.java

@@ -0,0 +1,40 @@
+package com.atmob.flutter_ad.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.ViewGroup;
+
+public class AdLoadContainer extends ViewGroup {
+
+    public AdLoadContainer(Context context) {
+        super(context);
+    }
+
+    public AdLoadContainer(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public AdLoadContainer(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (getChildCount() == 0) {
+            setMeasuredDimension(0, 0);
+        } else {
+            measureChildren(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+            setMeasuredDimension(getChildAt(0).getMeasuredWidth(), getChildAt(0).getMeasuredHeight());
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        for (int i = 0; i < getChildCount(); i++) {
+            getChildAt(i).layout(0, 0, getChildAt(i).getMeasuredWidth(), getChildAt(i).getMeasuredHeight());
+        }
+    }
+
+
+}

+ 29 - 0
android/src/test/java/com/atmob/flutter_ad/FlutterAdPluginTest.java

@@ -0,0 +1,29 @@
+package com.atmob.flutter_ad;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import io.flutter.plugin.common.MethodCall;
+import io.flutter.plugin.common.MethodChannel;
+import org.junit.Test;
+
+/**
+ * This demonstrates a simple unit test of the Java portion of this plugin's implementation.
+ *
+ * Once you have built the plugin's example app, you can run these tests from the command
+ * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or
+ * you can run them directly from IDEs that support JUnit such as Android Studio.
+ */
+
+public class FlutterAdPluginTest {
+  @Test
+  public void onMethodCall_getPlatformVersion_returnsExpectedValue() {
+    FlutterAdPlugin plugin = new FlutterAdPlugin();
+
+    final MethodCall call = new MethodCall("getPlatformVersion", null);
+    MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
+    plugin.onMethodCall(call, mockResult);
+
+    verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE);
+  }
+}

+ 38 - 0
ios/.gitignore

@@ -0,0 +1,38 @@
+.idea/
+.vagrant/
+.sconsign.dblite
+.svn/
+
+.DS_Store
+*.swp
+profile
+
+DerivedData/
+build/
+GeneratedPluginRegistrant.h
+GeneratedPluginRegistrant.m
+
+.generated/
+
+*.pbxuser
+*.mode1v3
+*.mode2v3
+*.perspectivev3
+
+!default.pbxuser
+!default.mode1v3
+!default.mode2v3
+!default.perspectivev3
+
+xcuserdata
+
+*.moved-aside
+
+*.pyc
+*sync/
+Icon?
+.tags*
+
+/Flutter/Generated.xcconfig
+/Flutter/ephemeral/
+/Flutter/flutter_export_environment.sh

+ 0 - 0
ios/Assets/.gitkeep


+ 19 - 0
ios/Classes/FlutterAdPlugin.swift

@@ -0,0 +1,19 @@
+import Flutter
+import UIKit
+
+public class FlutterAdPlugin: NSObject, FlutterPlugin {
+  public static func register(with registrar: FlutterPluginRegistrar) {
+    let channel = FlutterMethodChannel(name: "flutter_ad", binaryMessenger: registrar.messenger())
+    let instance = FlutterAdPlugin()
+    registrar.addMethodCallDelegate(instance, channel: channel)
+  }
+
+  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
+    switch call.method {
+    case "getPlatformVersion":
+      result("iOS " + UIDevice.current.systemVersion)
+    default:
+      result(FlutterMethodNotImplemented)
+    }
+  }
+}

+ 14 - 0
ios/Resources/PrivacyInfo.xcprivacy

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>NSPrivacyTrackingDomains</key>
+	<array/>
+	<key>NSPrivacyAccessedAPITypes</key>
+	<array/>
+	<key>NSPrivacyCollectedDataTypes</key>
+	<array/>
+	<key>NSPrivacyTracking</key>
+	<false/>
+</dict>
+</plist>

+ 29 - 0
ios/flutter_ad.podspec

@@ -0,0 +1,29 @@
+#
+# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
+# Run `pod lib lint flutter_ad.podspec` to validate before publishing.
+#
+Pod::Spec.new do |s|
+  s.name             = 'flutter_ad'
+  s.version          = '0.0.1'
+  s.summary          = '国内广告插件'
+  s.description      = <<-DESC
+国内广告插件
+                       DESC
+  s.homepage         = 'http://example.com'
+  s.license          = { :file => '../LICENSE' }
+  s.author           = { 'Your Company' => 'email@example.com' }
+  s.source           = { :path => '.' }
+  s.source_files = 'Classes/**/*'
+  s.dependency 'Flutter'
+  s.platform = :ios, '12.0'
+
+  # Flutter.framework does not contain a i386 slice.
+  s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
+  s.swift_version = '5.0'
+
+  # If your plugin requires a privacy manifest, for example if it uses any
+  # required reason APIs, update the PrivacyInfo.xcprivacy file to describe your
+  # plugin's privacy impact, and then uncomment this line. For more information,
+  # see https://developer.apple.com/documentation/bundleresources/privacy_manifest_files
+  # s.resource_bundles = {'flutter_ad_privacy' => ['Resources/PrivacyInfo.xcprivacy']}
+end

+ 24 - 0
lib/flutter_ad.dart

@@ -0,0 +1,24 @@
+library flutter_ad;
+
+//平台接口
+export 'package:flutter_ad/src/core/flutter_atmob_ad.dart';
+
+//配置
+export 'package:flutter_ad/src/config/atmob_ad_config.dart';
+export 'package:flutter_ad/src/config/ad_config_info.dart';
+export 'package:flutter_ad/src/constants/ad_constant.dart';
+
+//监听
+export 'package:flutter_ad/src/listener/ad_action_listener.dart';
+export 'package:flutter_ad/src/listener/atmob_ad_listener.dart';
+export 'package:flutter_ad/src/listener/banner_ad_listener.dart';
+export 'package:flutter_ad/src/listener/interstitial_ad_listener.dart';
+export 'package:flutter_ad/src/listener/interstitial_full_ad_listener.dart';
+export 'package:flutter_ad/src/listener/native_ad_listener.dart';
+export 'package:flutter_ad/src/listener/reward_video_ad_listener.dart';
+export 'package:flutter_ad/src/listener/splash_ad_listener.dart';
+
+//widget
+export 'package:flutter_ad/src/widget/flutter_splash_ad_view.dart';
+export 'package:flutter_ad/src/widget/flutter_banner_ad_view.dart';
+export 'package:flutter_ad/src/widget/flutter_native_ad_view.dart';

+ 80 - 0
lib/src/config/ad_config_info.dart

@@ -0,0 +1,80 @@
+//渠道配置 仅Android使用
+import '../constants/ad_constant.dart';
+
+class AtmobChannelInfo {
+  final String? atmobChanel;
+  final int? atmobAppId;
+  final int? atmobTgPlatform;
+
+  AtmobChannelInfo({
+    this.atmobChanel,
+    this.atmobAppId,
+    this.atmobTgPlatform,
+  });
+
+  String? getAtmobChanel() => atmobChanel;
+
+  int? getAtmobAppId() => atmobAppId;
+
+  int? getAtmobTgPlatform() => atmobTgPlatform;
+}
+
+//穿山甲
+class CsjConfig {
+  final String appId;
+
+  CsjConfig({
+    required this.appId,
+  });
+
+  String getAppId() => appId;
+}
+
+//快手
+class KsConfig {
+  final String appId;
+
+  KsConfig({
+    required this.appId,
+  });
+
+  String getAppId() => appId;
+}
+
+//TopOn
+class ToponConfig {
+  final String appKey;
+  final String appId;
+
+  ToponConfig({
+    required this.appKey,
+    required this.appId,
+  });
+
+  String getAppKey() => appKey;
+
+  String getAppId() => appId;
+}
+
+//穿山甲 gromore
+class GromoreSplashInfo {
+  final AdPlatform platformId;
+  final String positionId;
+  final String appId;
+  final String? appKey;
+
+  GromoreSplashInfo({
+    required this.platformId,
+    required this.positionId,
+    required this.appId,
+    required this.appKey,
+  });
+
+  int get platformIdValue => platformId.value;
+
+  String get getPositionId => positionId;
+
+  String get getAppId => appId;
+
+  String? get getAppKey => appKey;
+}

+ 90 - 0
lib/src/config/atmob_ad_config.dart

@@ -0,0 +1,90 @@
+import 'dart:io';
+
+import 'ad_config_info.dart';
+
+class AtmobAdConfig {
+  AndroidAdConfig? androidAdConfig;
+  IosAdConfig? iosAdConfig;
+
+  AtmobAdConfig({
+    this.androidAdConfig,
+    this.iosAdConfig,
+  });
+
+  AndroidAdConfig? getAndroidAdConfig() => androidAdConfig;
+
+  IosAdConfig? getIosAdConfig() => iosAdConfig;
+
+  Map<String, dynamic>? toMap() {
+    if (Platform.isAndroid) {
+      return androidAdConfig?.toMap();
+    } else if (Platform.isIOS) {
+      return iosAdConfig?.toMap();
+    }
+    throw UnimplementedError('platform not supported');
+  }
+}
+
+class AndroidAdConfig {
+  final String atmobAppKey;
+
+  final AtmobChannelInfo? atmobChannelInfo;
+
+  final CsjConfig? csjConfig;
+
+  final KsConfig? ksConfig;
+
+  final ToponConfig? toponConfig;
+
+  final GromoreSplashInfo? gromoreSplashInfo;
+
+  final bool debug;
+
+  AndroidAdConfig({
+    required this.atmobAppKey,
+    this.atmobChannelInfo,
+    this.csjConfig,
+    this.ksConfig,
+    this.toponConfig,
+    this.gromoreSplashInfo,
+    this.debug = false,
+  });
+
+  Map<String, dynamic> toMap() {
+    return {
+      'atmobAppKey': atmobAppKey,
+      'atmobChanel': atmobChannelInfo?.atmobChanel,
+      'atmobAppId': atmobChannelInfo?.atmobAppId,
+      'atmobTgPlatform': atmobChannelInfo?.atmobTgPlatform,
+      //穿山甲
+      'csjAppId': csjConfig?.appId,
+      //ks
+      'ksAppId': ksConfig?.appId,
+      //topon
+      'toponAppId': toponConfig?.appId,
+      'toponAppKey': toponConfig?.appKey,
+      //gromore
+      'gromorePlatformId': gromoreSplashInfo?.platformId.value,
+      'gromorePositionId': gromoreSplashInfo?.positionId,
+      'gromoreAppId': gromoreSplashInfo?.appId,
+      'gromoreAppKey': gromoreSplashInfo?.getAppKey,
+      'debug': debug,
+    };
+  }
+}
+
+//ios配置类,根据需要自行配置
+class IosAdConfig {
+  final bool debug;
+
+  IosAdConfig({
+    this.debug = false,
+  });
+
+  Map<String, dynamic> toMap() {
+    //ios可以根据情况自定义配置
+    return {
+      'debug': debug,
+    };
+  }
+}

+ 31 - 0
lib/src/constants/ad_constant.dart

@@ -0,0 +1,31 @@
+//广告类型
+enum AdType {
+  splash(1), // 启屏
+  nativeExpress(2), // 信息流
+  interstitial(3), // 插屏
+  rewardVideo(4), // 激励视频
+  nativeUnified(5), // 自渲染信息流
+  bannerExpress(6), // banner信息流
+  interstitialFull(7); // 插全屏
+
+  const AdType(this.value);
+
+  final int value;
+}
+
+// 广告平台
+enum AdPlatform {
+  undefined(-1),
+  csj(0), // 穿山甲
+  gdt(1), // 广点通
+  topOn(9), // topOn 弃用
+  ks(10), // 快手
+  groMore(11), // groMore
+  gdtCustom(365), // 自定义优量汇
+  baidu(5), // 百度
+  mintegral(13); // mintegral
+
+  const AdPlatform(this.value);
+
+  final int value;
+}

+ 26 - 0
lib/src/constants/flutter_method.dart

@@ -0,0 +1,26 @@
+class FlutterListenerMethod {
+  static const String onShow = "onShow";
+
+  static const String onClose = "onClose";
+
+  static const String onFail = "onFail";
+
+  static const String onClick = "onClick";
+
+  static const String onRewarded = "onRewarded";
+
+  static const String action = "action";
+
+  static const String size = "size";
+}
+
+class FlutterAdMethod {
+  static const String flutterAdMethod = "flutterAdMethod";
+
+  //广告类型
+  static const String loadInterstitial = "loadInterstitial";
+
+  static const String loadInterstitialFull = "loadInterstitialFull";
+
+  static const String loadRewardVideo = "loadRewardVideo";
+}

+ 17 - 0
lib/src/constants/view_type.dart

@@ -0,0 +1,17 @@
+class ChannelViewType {
+  ChannelViewType._();
+
+  static const String splashViewType = "com.atmob.flutter_ad/splashAdView";
+  static const String nativeViewType = "com.atmob.flutter_ad/nativeAdView";
+  static const String bannerViewType = "com.atmob.flutter_ad/bannerAdView";
+}
+
+class ChannelMethod {
+  ChannelMethod._();
+
+  static const String loadInterstitial =
+      "com.atmob.flutter_ad/loadInterstitial";
+  static const String loadInterstitialFull =
+      "com.atmob.flutter_ad/loadInterstitialFull";
+  static const String loadRewardVideo = "com.atmob.flutter_ad/loadRewardVideo";
+}

+ 91 - 0
lib/src/core/flutter_ad_method_channel.dart

@@ -0,0 +1,91 @@
+import 'package:flutter/foundation.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_ad/src/constants/flutter_method.dart';
+import '../config/atmob_ad_config.dart';
+import '../listener/interstitial_ad_listener.dart';
+import '../listener/interstitial_full_ad_listener.dart';
+import '../listener/reward_video_ad_listener.dart';
+import '../utils/flutter_ad_listener_method_channel_helper.dart';
+import 'flutter_ad_platform.dart';
+
+/// An implementation of [FlutterAdPlatform] that uses method channels.
+class MethodChannelFlutterAd extends FlutterAdPlatform {
+  /// The method channel used to interact with the native platform.
+  @visibleForTesting
+  final methodChannel = const MethodChannel('flutter_ad');
+
+  MethodChannelFlutterAd() {
+    FlutterAdListenerMethodChannelHelper.setMethodChannel(methodChannel);
+  }
+
+  @override
+  Future<void> initAd(AtmobAdConfig adConfig) {
+    return methodChannel.invokeMethod("initAd", adConfig.toMap());
+  }
+
+  @override
+  void loadInterstitial(
+      {required String androidAdFuncId,
+      required String iosAdFuncId,
+      InterstitialAdListener? listener}) {
+    String adFuncId;
+    switch (defaultTargetPlatform) {
+      case TargetPlatform.android:
+        adFuncId = androidAdFuncId;
+        break;
+      case TargetPlatform.iOS:
+        adFuncId = iosAdFuncId;
+        break;
+      default:
+        throw UnsupportedError("Unsupported platform");
+    }
+    FlutterAdListenerMethodChannelHelper.setAdListener(adFuncId, listener);
+    methodChannel.invokeMethod(FlutterAdMethod.loadInterstitial, {
+      "adFuncId": adFuncId,
+    });
+  }
+
+  @override
+  void loadInterstitialFull(
+      {required String androidAdFuncId,
+      required String iosAdFuncId,
+      InterstitialFullAdListener? listener}) {
+    String adFuncId;
+    switch (defaultTargetPlatform) {
+      case TargetPlatform.android:
+        adFuncId = androidAdFuncId;
+        break;
+      case TargetPlatform.iOS:
+        adFuncId = iosAdFuncId;
+        break;
+      default:
+        throw UnsupportedError("Unsupported platform");
+    }
+    FlutterAdListenerMethodChannelHelper.setAdListener(adFuncId, listener);
+    methodChannel.invokeMethod(FlutterAdMethod.loadInterstitialFull, {
+      "adFuncId": adFuncId,
+    });
+  }
+
+  @override
+  void loadRewardVideo(
+      {required String androidAdFuncId,
+      required String iosAdFuncId,
+      RewardVideoAdListener? listener}) {
+    String adFuncId;
+    switch (defaultTargetPlatform) {
+      case TargetPlatform.android:
+        adFuncId = androidAdFuncId;
+        break;
+      case TargetPlatform.iOS:
+        adFuncId = iosAdFuncId;
+        break;
+      default:
+        throw UnsupportedError("Unsupported platform");
+    }
+    FlutterAdListenerMethodChannelHelper.setAdListener(adFuncId, listener);
+    methodChannel.invokeMethod(FlutterAdMethod.loadRewardVideo, {
+      "adFuncId": adFuncId,
+    });
+  }
+}

+ 55 - 0
lib/src/core/flutter_ad_platform.dart

@@ -0,0 +1,55 @@
+import 'package:flutter_ad/src/config/atmob_ad_config.dart';
+import 'package:flutter_ad/src/listener/interstitial_ad_listener.dart';
+import 'package:flutter_ad/src/listener/interstitial_full_ad_listener.dart';
+import 'package:flutter_ad/src/listener/reward_video_ad_listener.dart';
+import 'package:plugin_platform_interface/plugin_platform_interface.dart';
+
+import 'flutter_ad_method_channel.dart';
+
+abstract class FlutterAdPlatform extends PlatformInterface {
+  /// Constructs a FlutterAdPlatform.
+  FlutterAdPlatform() : super(token: _token);
+
+  static final Object _token = Object();
+
+  static FlutterAdPlatform _instance = MethodChannelFlutterAd();
+
+  /// The default instance of [FlutterAdPlatform] to use.
+  ///
+  /// Defaults to [MethodChannelFlutterAd].
+  static FlutterAdPlatform get instance => _instance;
+
+  /// Platform-specific implementations should set this with their own
+  /// platform-specific class that extends [FlutterAdPlatform] when
+  /// they register themselves.
+  static set instance(FlutterAdPlatform instance) {
+    PlatformInterface.verifyToken(instance, _token);
+    _instance = instance;
+  }
+
+  Future<void> initAd(AtmobAdConfig adConfig) {
+    throw UnimplementedError('initAd() has not been implemented.');
+  }
+
+  void loadInterstitial(
+      {required String androidAdFuncId,
+      required String iosAdFuncId,
+      InterstitialAdListener? listener}) {
+    throw UnimplementedError('loadInterstitial() has not been implemented.');
+  }
+
+  void loadInterstitialFull(
+      {required String androidAdFuncId,
+      required String iosAdFuncId,
+      InterstitialFullAdListener? listener}) {
+    throw UnimplementedError(
+        'loadInterstitialFull() has not been implemented.');
+  }
+
+  void loadRewardVideo(
+      {required String androidAdFuncId,
+      required String iosAdFuncId,
+      RewardVideoAdListener? listener}) {
+    throw UnimplementedError('loadRewardVideo() has not been implemented.');
+  }
+}

+ 45 - 0
lib/src/core/flutter_atmob_ad.dart

@@ -0,0 +1,45 @@
+import 'package:flutter_ad/src/listener/interstitial_full_ad_listener.dart';
+import 'package:flutter_ad/src/listener/reward_video_ad_listener.dart';
+
+import '../config/atmob_ad_config.dart';
+import '../listener/interstitial_ad_listener.dart';
+import 'flutter_ad_platform.dart';
+
+class FlutterAtmobAd {
+  FlutterAtmobAd._();
+
+  /// 初始化广告
+  static Future<void> initAd(AtmobAdConfig adConfig) async {
+    return FlutterAdPlatform.instance.initAd(adConfig);
+  }
+
+  static void loadInterstitial(
+      {required String androidAdFuncId,
+      required String iosAdFuncId,
+      InterstitialAdListener? listener}) {
+    FlutterAdPlatform.instance.loadInterstitial(
+        androidAdFuncId: androidAdFuncId,
+        iosAdFuncId: iosAdFuncId,
+        listener: listener);
+  }
+
+  static void loadInterstitialFull(
+      {required String androidAdFuncId,
+      required String iosAdFuncId,
+      InterstitialFullAdListener? listener}) {
+    FlutterAdPlatform.instance.loadInterstitialFull(
+        androidAdFuncId: androidAdFuncId,
+        iosAdFuncId: iosAdFuncId,
+        listener: listener);
+  }
+
+  static void loadRewardVideo(
+      {required String androidAdFuncId,
+      required String iosAdFuncId,
+      RewardVideoAdListener? listener}) {
+    FlutterAdPlatform.instance.loadRewardVideo(
+        androidAdFuncId: androidAdFuncId,
+        iosAdFuncId: iosAdFuncId,
+        listener: listener);
+  }
+}

+ 3 - 0
lib/src/listener/ad_action_listener.dart

@@ -0,0 +1,3 @@
+abstract class AdActionListener {
+  void action();
+}

+ 13 - 0
lib/src/listener/atmob_ad_listener.dart

@@ -0,0 +1,13 @@
+abstract class AtmobAdListener {
+// 广告展示
+  void onShow();
+
+// 广告关闭
+  void onClose();
+
+// 广告失败
+  void onFail(String msg);
+
+// 广告点击
+  void onClick();
+}

+ 27 - 0
lib/src/listener/banner_ad_listener.dart

@@ -0,0 +1,27 @@
+import 'atmob_ad_listener.dart';
+
+abstract class BannerAdListener implements AtmobAdListener {
+  final void Function() onAdClick;
+  final void Function() onAdClose;
+  final void Function(String msg) onAdFail;
+  final void Function() onAdShow;
+
+  BannerAdListener({
+    required this.onAdClick,
+    required this.onAdClose,
+    required this.onAdFail,
+    required this.onAdShow,
+  });
+
+  @override
+  void onClick() => onAdClick();
+
+  @override
+  void onClose() => onAdClose();
+
+  @override
+  void onFail(String msg) => onAdFail(msg);
+
+  @override
+  void onShow() => onAdShow();
+}

+ 27 - 0
lib/src/listener/interstitial_ad_listener.dart

@@ -0,0 +1,27 @@
+import 'package:flutter_ad/src/listener/atmob_ad_listener.dart';
+
+class InterstitialAdListener implements AtmobAdListener {
+  final void Function() onAdClick;
+  final void Function() onAdClose;
+  final void Function(String msg) onAdFail;
+  final void Function() onAdShow;
+
+  InterstitialAdListener({
+    required this.onAdClick,
+    required this.onAdClose,
+    required this.onAdFail,
+    required this.onAdShow,
+  });
+
+  @override
+  void onClick() => onAdClick();
+
+  @override
+  void onClose() => onAdClose();
+
+  @override
+  void onFail(String msg) => onAdFail(msg);
+
+  @override
+  void onShow() => onAdShow();
+}

+ 27 - 0
lib/src/listener/interstitial_full_ad_listener.dart

@@ -0,0 +1,27 @@
+import 'package:flutter_ad/src/listener/atmob_ad_listener.dart';
+
+abstract class InterstitialFullAdListener implements AtmobAdListener {
+  final void Function() onAdClick;
+  final void Function() onAdClose;
+  final void Function(String msg) onAdFail;
+  final void Function() onAdShow;
+
+  InterstitialFullAdListener({
+    required this.onAdClick,
+    required this.onAdClose,
+    required this.onAdFail,
+    required this.onAdShow,
+  });
+
+  @override
+  void onClick() => onAdClick();
+
+  @override
+  void onClose() => onAdClose();
+
+  @override
+  void onFail(String msg) => onAdFail(msg);
+
+  @override
+  void onShow() => onAdShow();
+}

+ 27 - 0
lib/src/listener/native_ad_listener.dart

@@ -0,0 +1,27 @@
+import 'package:flutter_ad/src/listener/atmob_ad_listener.dart';
+
+abstract class NativeAdListener implements AtmobAdListener {
+  final void Function() onAdClick;
+  final void Function() onAdClose;
+  final void Function(String msg) onAdFail;
+  final void Function() onAdShow;
+
+  NativeAdListener({
+    required this.onAdClick,
+    required this.onAdClose,
+    required this.onAdFail,
+    required this.onAdShow,
+  });
+
+  @override
+  void onClick() => onAdClick();
+
+  @override
+  void onClose() => onAdClose();
+
+  @override
+  void onFail(String msg) => onAdFail(msg);
+
+  @override
+  void onShow() => onAdShow();
+}

+ 31 - 0
lib/src/listener/reward_video_ad_listener.dart

@@ -0,0 +1,31 @@
+import 'package:flutter_ad/src/listener/atmob_ad_listener.dart';
+
+abstract class RewardVideoAdListener implements AtmobAdListener {
+  final void Function() onAdClick;
+  final void Function() onAdClose;
+  final void Function(String msg) onAdFail;
+  final void Function() onAdShow;
+  final void Function() onAdRewarded;
+
+  RewardVideoAdListener({
+    required this.onAdClick,
+    required this.onAdClose,
+    required this.onAdFail,
+    required this.onAdShow,
+    required this.onAdRewarded,
+  });
+
+  @override
+  void onClick() => onAdClick();
+
+  @override
+  void onClose() => onAdClose();
+
+  @override
+  void onFail(String msg) => onAdFail(msg);
+
+  @override
+  void onShow() => onAdShow();
+
+  void onRewarded() => onAdRewarded();
+}

+ 31 - 0
lib/src/listener/splash_ad_listener.dart

@@ -0,0 +1,31 @@
+import 'package:flutter_ad/src/listener/atmob_ad_listener.dart';
+
+import '../../flutter_ad.dart';
+
+class SplashAdListener implements AtmobAdListener {
+  final void Function() onAdClick;
+  final void Function() onAdClose;
+  final void Function(String msg) onAdFail;
+  final void Function() onAdShow;
+
+  SplashAdListener({
+    required this.onAdClick,
+    required this.onAdClose,
+    required this.onAdFail,
+    required this.onAdShow,
+  });
+
+  @override
+  void onClick() => onAdClick();
+
+  @override
+  void onClose() => onAdClose();
+
+  @override
+  void onFail(String msg) => onAdFail(msg);
+
+  @override
+  void onShow() => onAdShow();
+}
+
+typedef SplashAdAction = void Function();

+ 53 - 0
lib/src/utils/flutter_ad_listener_method_channel_helper.dart

@@ -0,0 +1,53 @@
+import 'package:flutter/services.dart';
+import 'package:flutter_ad/src/constants/flutter_method.dart';
+import 'package:flutter_ad/src/listener/atmob_ad_listener.dart';
+import 'package:flutter_ad/src/listener/reward_video_ad_listener.dart';
+
+class FlutterAdListenerMethodChannelHelper {
+  FlutterAdListenerMethodChannelHelper._();
+
+  static MethodChannel? _methodChannel;
+
+  static final Map<String, AtmobAdListener> _adListenerMap = {};
+
+  static setAdListener(String adFuncId, AtmobAdListener? listener) {
+    if (listener == null) {
+      return;
+    }
+    _adListenerMap.addAll({adFuncId: listener});
+  }
+
+  static void setMethodChannel(MethodChannel channel) {
+    _methodChannel = channel;
+    _adListenerMap.clear();
+    _methodChannel?.setMethodCallHandler(_platformCallHandler);
+  }
+
+  static Future<dynamic> _platformCallHandler(MethodCall call) async {
+    if (call.method == FlutterAdMethod.flutterAdMethod) {
+      final String adFuncId = call.arguments["adFuncId"];
+      final AtmobAdListener? listener = _adListenerMap[adFuncId];
+      _adListenerMap.remove(adFuncId);
+      if (listener == null) {
+        return;
+      }
+      switch (call.arguments["method"]) {
+        case FlutterListenerMethod.onShow:
+          listener.onShow();
+          break;
+        case FlutterListenerMethod.onFail:
+          listener.onFail(call.arguments["failMsg"]);
+          break;
+        case FlutterListenerMethod.onClick:
+          listener.onClick();
+          break;
+        case FlutterListenerMethod.onClose:
+          listener.onClose();
+          break;
+        case FlutterListenerMethod.onRewarded:
+          if (listener is RewardVideoAdListener) listener.onRewarded();
+          break;
+      }
+    }
+  }
+}

+ 111 - 0
lib/src/widget/flutter_banner_ad_view.dart

@@ -0,0 +1,111 @@
+import 'dart:io';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_ad/src/listener/banner_ad_listener.dart';
+import '../constants/flutter_method.dart';
+import '../constants/view_type.dart';
+
+class FlutterBannerAdView extends StatefulWidget {
+  final int androidAdId;
+  final String iosAdId;
+  final BannerAdListener? listener;
+
+  const FlutterBannerAdView({
+    super.key,
+    required this.androidAdId,
+    required this.iosAdId,
+    this.listener,
+  });
+
+  @override
+  State<FlutterBannerAdView> createState() => _FlutterSplashAdViewState();
+}
+
+class _FlutterSplashAdViewState extends State<FlutterBannerAdView>
+    with AutomaticKeepAliveClientMixin<FlutterBannerAdView> {
+  MethodChannel? _channel;
+  final String viewType = ChannelViewType.bannerViewType;
+
+  bool _isShowAd = true;
+  double _height = 0.5;
+
+  @override
+  void initState() {
+    super.initState();
+    _isShowAd = true;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    if (!_isShowAd) {
+      return Container();
+    }
+    if (Platform.isAndroid) {
+      return SizedBox(
+        width: double.infinity,
+        height: _height,
+        child: AndroidView(
+            viewType: viewType,
+            creationParams: {
+              "adFuncId": widget.androidAdId,
+            },
+            creationParamsCodec: const StandardMessageCodec(),
+            onPlatformViewCreated: _registerChannel),
+      );
+    } else if (Platform.isIOS) {
+      return UiKitView(
+        viewType: viewType,
+        creationParams: {
+          "adFuncId": widget.iosAdId,
+        },
+      );
+    } else {
+      return Container();
+    }
+  }
+
+  void _registerChannel(int viewId) {
+    _channel = MethodChannel("${viewType}_$viewId");
+    _channel?.setMethodCallHandler(_platformCallHandler);
+  }
+
+  //监听原生view传值
+  Future<dynamic> _platformCallHandler(MethodCall call) async {
+    switch (call.method) {
+      case FlutterListenerMethod.size:
+        if (call.arguments != null) {
+          setState(() {
+            _height = call.arguments['height'];
+          });
+        }
+        break;
+      //显示广告
+      case FlutterListenerMethod.onShow:
+        if (widget.listener != null) {
+          widget.listener!.onShow();
+        }
+        break;
+      //广告加载失败
+      case FlutterListenerMethod.onFail:
+        if (widget.listener != null) {
+          widget.listener!.onFail(call.arguments);
+        }
+        break;
+      //广告点击
+      case FlutterListenerMethod.onClick:
+        if (widget.listener != null) {
+          widget.listener!.onClick();
+        }
+        break;
+      //广告关闭
+      case FlutterListenerMethod.onClose:
+        if (widget.listener != null) {
+          widget.listener!.onClose();
+        }
+        break;
+    }
+  }
+
+  @override
+  bool get wantKeepAlive => true;
+}

+ 114 - 0
lib/src/widget/flutter_native_ad_view.dart

@@ -0,0 +1,114 @@
+import 'dart:io';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import '../constants/flutter_method.dart';
+import '../constants/view_type.dart';
+import '../listener/native_ad_listener.dart';
+
+class FlutterNativeAdView extends StatefulWidget {
+  final int androidAdId;
+  final String iosAdId;
+  final NativeAdListener? listener;
+
+  const FlutterNativeAdView({
+    super.key,
+    required this.androidAdId,
+    required this.iosAdId,
+    this.listener,
+  });
+
+  @override
+  State<FlutterNativeAdView> createState() => _FlutterSplashAdViewState();
+}
+
+class _FlutterSplashAdViewState extends State<FlutterNativeAdView>
+    with AutomaticKeepAliveClientMixin<FlutterNativeAdView> {
+  MethodChannel? _channel;
+  final String viewType = ChannelViewType.nativeViewType;
+
+  bool _isShowAd = true;
+  double _height = 0.5;
+
+  @override
+  void initState() {
+    super.initState();
+    _isShowAd = true;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    if (!_isShowAd) {
+      return Container();
+    }
+    if (Platform.isAndroid) {
+      return SizedBox(
+        width: double.infinity,
+        height: _height,
+        child: AndroidView(
+            viewType: viewType,
+            creationParams: {
+              "adFuncId": widget.androidAdId,
+            },
+            creationParamsCodec: const StandardMessageCodec(),
+            onPlatformViewCreated: _registerChannel),
+      );
+    } else if (Platform.isIOS) {
+      return UiKitView(
+        viewType: viewType,
+        creationParams: {
+          "adFuncId": widget.iosAdId,
+        },
+      );
+    } else {
+      return Container();
+    }
+  }
+
+  void _registerChannel(int id) {
+    _channel = MethodChannel("${viewType}_$id");
+    _channel?.setMethodCallHandler(_platformCallHandler);
+  }
+
+  //监听原生view传值
+  Future<dynamic> _platformCallHandler(MethodCall call) async {
+    switch (call.method) {
+      case FlutterListenerMethod.size:
+        if (call.arguments != null) {
+          setState(() {
+            _height = call.arguments['height'];
+          });
+        }
+        break;
+      //显示广告
+      case FlutterListenerMethod.onShow:
+        if (widget.listener != null) {
+          widget.listener!.onShow();
+        }
+        break;
+      //广告加载失败
+      case FlutterListenerMethod.onFail:
+        if (widget.listener != null) {
+          widget.listener!.onFail(call.arguments);
+        }
+        break;
+      //广告点击
+      case FlutterListenerMethod.onClick:
+        if (widget.listener != null) {
+          widget.listener!.onClick();
+        }
+        break;
+      //广告关闭
+      case FlutterListenerMethod.onClose:
+        if (widget.listener != null) {
+          widget.listener!.onClose();
+        }
+        break;
+    }
+  }
+
+  @override
+  bool get wantKeepAlive {
+    return true;
+  }
+}

+ 93 - 0
lib/src/widget/flutter_splash_ad_view.dart

@@ -0,0 +1,93 @@
+import 'dart:io';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/services.dart';
+import '../constants/flutter_method.dart';
+import '../constants/view_type.dart';
+import '../listener/splash_ad_listener.dart';
+
+class FlutterSplashAdView extends StatefulWidget {
+  final String androidAdId;
+  final String iosAdId;
+  final SplashAdAction? action; //广告关闭或者加载失败时的回调,一般使用这个即可
+  final SplashAdListener? listener;
+
+  const FlutterSplashAdView({
+    super.key,
+    required this.androidAdId,
+    required this.iosAdId,
+    this.action,
+    this.listener,
+  });
+
+  @override
+  State<FlutterSplashAdView> createState() => _FlutterSplashAdViewState();
+}
+
+class _FlutterSplashAdViewState extends State<FlutterSplashAdView> {
+  MethodChannel? _channel;
+  final String viewType = ChannelViewType.splashViewType;
+
+  bool _isShowAd = true;
+
+  @override
+  void initState() {
+    super.initState();
+    _isShowAd = true;
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    if (!_isShowAd) {
+      return Container();
+    }
+    if (Platform.isAndroid) {
+      return AndroidView(
+          viewType: viewType,
+          creationParams: {
+            "adFuncId": widget.androidAdId,
+          },
+          creationParamsCodec: const StandardMessageCodec(),
+          onPlatformViewCreated: _registerChannel);
+    } else if (Platform.isIOS) {
+      return UiKitView(
+        viewType: viewType,
+        creationParams: {
+          "adFuncId": widget.iosAdId,
+        },
+      );
+    } else {
+      return Container();
+    }
+  }
+
+  void _registerChannel(int id) {
+    _channel = MethodChannel("${viewType}_$id");
+    _channel?.setMethodCallHandler(_platformCallHandler);
+  }
+
+  //监听原生view传值
+  Future<dynamic> _platformCallHandler(MethodCall call) async {
+    switch (call.method) {
+      //显示广告
+      case FlutterListenerMethod.onShow:
+        widget.listener?.onShow();
+        break;
+      //广告加载失败
+      case FlutterListenerMethod.onFail:
+        widget.listener?.onFail(call.arguments);
+        break;
+      //广告点击
+      case FlutterListenerMethod.onClick:
+        widget.listener?.onClick();
+        break;
+      //广告关闭
+      case FlutterListenerMethod.onClose:
+        widget.listener?.onClose();
+        break;
+      //下一步 android 原生那边当 onFail|onClose 触发时会触发
+      case FlutterListenerMethod.action:
+        widget.action?.call();
+        break;
+    }
+  }
+}

+ 72 - 0
pubspec.yaml

@@ -0,0 +1,72 @@
+name: flutter_ad
+description: "国内广告插件"
+version: 0.0.1
+homepage:
+
+environment:
+  sdk: ^3.5.0
+  flutter: '>=3.3.0'
+
+dependencies:
+  flutter:
+    sdk: flutter
+  plugin_platform_interface: ^2.0.2
+
+dev_dependencies:
+  flutter_test:
+    sdk: flutter
+  flutter_lints: ^4.0.0
+
+# For information on the generic Dart part of this file, see the
+# following page: https://dart.dev/tools/pub/pubspec
+
+# The following section is specific to Flutter packages.
+flutter:
+  # This section identifies this Flutter project as a plugin project.
+  # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)
+  # which should be registered in the plugin registry. This is required for
+  # using method channels.
+  # The Android 'package' specifies package in which the registered class is.
+  # This is required for using method channels on Android.
+  # The 'ffiPlugin' specifies that native code should be built and bundled.
+  # This is required for using `dart:ffi`.
+  # All these are used by the tooling to maintain consistency when
+  # adding or updating assets for this project.
+  plugin:
+    platforms:
+      android:
+        package: com.atmob.flutter_ad
+        pluginClass: FlutterAdPlugin
+      ios:
+        pluginClass: FlutterAdPlugin
+
+  # To add assets to your plugin package, add an assets section, like this:
+  # assets:
+  #   - images/a_dot_burr.jpeg
+  #   - images/a_dot_ham.jpeg
+  #
+  # For details regarding assets in packages, see
+  # https://flutter.dev/to/asset-from-package
+  #
+  # An image asset can refer to one or more resolution-specific "variants", see
+  # https://flutter.dev/to/resolution-aware-images
+
+  # To add custom fonts to your plugin package, add a fonts section here,
+  # in this "flutter" section. Each entry in this list should have a
+  # "family" key with the font family name, and a "fonts" key with a
+  # list giving the asset and other descriptors for the font. For
+  # example:
+  # fonts:
+  #   - family: Schyler
+  #     fonts:
+  #       - asset: fonts/Schyler-Regular.ttf
+  #       - asset: fonts/Schyler-Italic.ttf
+  #         style: italic
+  #   - family: Trajan Pro
+  #     fonts:
+  #       - asset: fonts/TrajanPro.ttf
+  #       - asset: fonts/TrajanPro_Bold.ttf
+  #         weight: 700
+  #
+  # For details regarding fonts in packages, see
+  # https://flutter.dev/to/font-from-package