├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ └── config.yml ├── .gitignore ├── .gitmodules ├── .idea ├── .gitignore ├── compiler.xml ├── kotlinc.xml ├── migrations.xml ├── misc.xml └── runConfigurations.xml ├── LICENSE ├── README.md ├── admob ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── org │ └── godotengine │ └── plugin │ └── android │ └── admob │ ├── AdmobPlugin.java │ ├── Banner.java │ ├── Interstitial.java │ ├── RewardedInterstitial.java │ └── RewardedVideo.java ├── build.gradle.kts ├── demo ├── .gitignore ├── Main.gd ├── Main.gd.uid ├── Main.tscn ├── admob.png ├── android │ └── .build_version ├── export_presets.cfg ├── project.godot └── scene │ ├── Scene2.gd │ ├── Scene2.gd.uid │ └── Scene2.tscn ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: cengiz-pz 2 | buy_me_a_coffee: cengizpz 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | # disable blank issue creation 2 | blank_issues_enabled: false 3 | 4 | contact_links: 5 | - name: Bugs 6 | url: https://github.com/godot-sdk-integrations/godot-admob/issues 7 | about: Please report bugs on the new repository. 8 | 9 | - name: Feature Requests 10 | url: https://github.com/godot-sdk-integrations/godot-admob/issues 11 | about: Please submit feature requests on the new repository. 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.aar 4 | *.ap_ 5 | *.aab 6 | 7 | # Files for the ART/Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | # Uncomment the following line in case you need and you don't have the release build type files in your app 18 | # release/ 19 | 20 | # Gradle files 21 | .gradle/ 22 | gradle/ 23 | build/ 24 | 25 | # Local configuration file (sdk path, etc) 26 | local.properties 27 | 28 | # Proguard folder generated by Eclipse 29 | proguard/ 30 | 31 | # Log Files 32 | *.log 33 | 34 | # Android Studio Navigation editor temp files 35 | .navigation/ 36 | 37 | # Android Studio captures folder 38 | captures/ 39 | 40 | # IntelliJ 41 | *.iml 42 | .idea/workspace.xml 43 | .idea/tasks.xml 44 | .idea/gradle.xml 45 | .idea/assetWizardSettings.xml 46 | .idea/dictionaries 47 | .idea/libraries 48 | # Android Studio 3 in .gitignore file. 49 | .idea/caches 50 | .idea/modules.xml 51 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 52 | .idea/navEditor.xml 53 | 54 | # Keystore files 55 | # Uncomment the following lines if you do not want to check your keystore files in. 56 | #*.jks 57 | #*.keystore 58 | 59 | # External native build folder generated in Android Studio 2.2 and later 60 | .externalNativeBuild 61 | .cxx/ 62 | 63 | # Google Services (e.g. APIs or Firebase) 64 | # google-services.json 65 | 66 | # Freeline 67 | freeline.py 68 | freeline/ 69 | freeline_project_description.json 70 | 71 | # fastlane 72 | fastlane/report.xml 73 | fastlane/Preview.html 74 | fastlane/screenshots 75 | fastlane/test_output 76 | fastlane/readme.md 77 | 78 | # Version control 79 | vcs.xml 80 | 81 | # lint 82 | lint/intermediates/ 83 | lint/generated/ 84 | lint/outputs/ 85 | lint/tmp/ 86 | # lint/reports/ 87 | 88 | # Godot 89 | .godot/ 90 | *.import 91 | demo/addons 92 | 93 | # Mac OS 94 | .DS_Store 95 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "addon"] 2 | path = admob/addon_template 3 | url = https://github.com/cengiz-pz/godot-admob-addon.git 4 | branch = main 5 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Cengiz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | $${\color{red}27/04/2025: \space This \space repository \space has \space moved \space under \space the \space Godot \space SDK \space Integrations \space Github \space organization.}$$ 2 | $${\color{red}Future \space releases \space will \space be \space published \space at \space the \space new \space repository:}$$ 3 | 4 | ### https://github.com/godot-sdk-integrations/godot-admob 5 | 6 |

7 | 8 |

9 | 10 |

11 | 12 | # ![](admob/addon_template/icon.png?raw=true) Android Admob Plugin 13 | 14 | Enables AdMob functionality on Godot apps that are exported to the Android platform and allows 15 | displaying of Admob ads. 16 | 17 | _For iOS version, visit https://github.com/cengiz-pz/godot-ios-admob-plugin ._ 18 | 19 | This branch contains the latest version of the plugin, which contains breaking changes to the plugin 20 | interface. The original version of the plugin can be found on the 21 | [Release 1.0 branch](https://github.com/cengiz-pz/godot-android-admob-plugin/tree/release-1.0). 22 | 23 | ## ![](admob/addon_template/icon.png?raw=true) Prerequisites 24 | Follow instructions on the following page to create a custom Android gradle build 25 | - [Create custom Android gradle build](https://docs.godotengine.org/en/stable/tutorials/export/android_gradle_build.html) 26 | 27 | Create an AdMob account at the following link: 28 | - [Google AdMob](https://admob.google.com/) 29 | - create an app in AdMob console 30 | - [create ad(s)](https://support.google.com/admob/answer/6173650?hl=en) for your app via the AdMob console 31 | - if needed, [create consent form(s)](https://support.google.com/admob/answer/10113207?hl=en) for your app via the AdMob console 32 | 33 | ## ![](admob/addon_template/icon.png?raw=true) Installation 34 | _Before installing this plugin, make sure to uninstall any previous versions of the same plugin._ 35 | 36 | _If installing both Android and iOS versions of the plugin in the same project, then make sure that both versions use the same addon interface version._ 37 | 38 | There are 2 ways to install the `Admob` plugin into your project: 39 | - Through the Godot Editor's AssetLib 40 | - Manually by downloading archives from Github 41 | 42 | ### ![](admob/addon_template/icon.png?raw=true) Installing via AssetLib 43 | Steps: 44 | - search for and select the `Admob` plugin in Godot Editor 45 | - click `Download` button 46 | - on the installation dialog... 47 | - keep `Change Install Folder` setting pointing to your project's root directory 48 | - keep `Ignore asset root` checkbox checked 49 | - click `Install` button 50 | - enable the plugin via the `Plugins` tab of `Project->Project Settings...` menu, in the Godot Editor 51 | 52 | #### ![](addon/icon.png?raw=true) Installing both Android and iOS versions of the plugin in the same project 53 | When installing via AssetLib, the installer may display a warning that states "_[x number of]_ files conflict with your project and won't be installed." You can ignore this warning since both versions use the same addon code. 54 | 55 | ### ![](admob/addon_template/icon.png?raw=true) Installing manually 56 | Steps: 57 | - download release archive from Github 58 | - unzip the release archive 59 | - copy to your Godot project's root directory 60 | - enable the plugin via the `Plugins` tab of `Project->Project Settings...` menu, in the Godot Editor 61 | 62 | 63 | ## ![](admob/addon_template/icon.png?raw=true) Usage 64 | - Add `Admob` node to your main scene and populate the ID fields of the node 65 | - Debug IDs will only be used when your Godot app is run in debug mode 66 | - Real IDs will only be used when the `is_real` field of the node is set to `true` 67 | 68 | ### ![](admob/addon_template/icon.png?raw=true) Signals 69 | - register listeners for one or more of the following signals of the `Admob` node: 70 | - `initialization_completed(status_data: InitializationStatus)` 71 | - `banner_ad_loaded(ad_id: String)` 72 | - `banner_ad_failed_to_load(ad_id: String, error_data: LoadAdError)` 73 | - `banner_ad_refreshed(ad_id: String)` 74 | - `banner_ad_clicked(ad_id: String)` 75 | - `banner_ad_impression(ad_id: String)` 76 | - `banner_ad_opened(ad_id: String)` 77 | - `banner_ad_closed(ad_id: String)` 78 | - `interstitial_ad_loaded(ad_id: String)` 79 | - `interstitial_ad_failed_to_load(ad_id: String, error_data: LoadAdError)` 80 | - `interstitial_ad_refreshed(ad_id: String)` 81 | - `interstitial_ad_impression(ad_id: String)` 82 | - `interstitial_ad_clicked(ad_id: String)` 83 | - `interstitial_ad_showed_full_screen_content(ad_id: String)` 84 | - `interstitial_ad_failed_to_show_full_screen_content(ad_id: String, error_data: AdError)` 85 | - `interstitial_ad_dismissed_full_screen_content(ad_id: String)` 86 | - `rewarded_ad_loaded(ad_id: String)` 87 | - `rewarded_ad_failed_to_load(ad_id: String, error_data: LoadAdError)` 88 | - `rewarded_ad_impression(ad_id: String)` 89 | - `rewarded_ad_clicked(ad_id: String)` 90 | - `rewarded_ad_showed_full_screen_content(ad_id: String)` 91 | - `rewarded_ad_failed_to_show_full_screen_content(ad_id: String, error_data: AdError)` 92 | - `rewarded_ad_dismissed_full_screen_content(ad_id: String)` 93 | - `rewarded_ad_user_earned_reward(ad_id: String, reward_data: RewardItem)` 94 | - `rewarded_interstitial_ad_loaded(ad_id: String)` 95 | - `rewarded_interstitial_ad_failed_to_load(ad_id: String, error_data: LoadAdError)` 96 | - `rewarded_interstitial_ad_impression(ad_id: String)` 97 | - `rewarded_interstitial_ad_clicked(ad_id: String)` 98 | - `rewarded_interstitial_ad_showed_full_screen_content(ad_id: String)` 99 | - `rewarded_interstitial_ad_failed_to_show_full_screen_content(ad_id: String, error_data: AdError)` 100 | - `rewarded_interstitial_ad_dismissed_full_screen_content(ad_id: String)` 101 | - `rewarded_interstitial_ad_user_earned_reward(ad_id: String, reward_data: RewardItem)` 102 | - `consent_form_loaded` 103 | - `consent_form_dismissed(error_data: FormError)` 104 | - `consent_form_failed_to_load(error_data: FormError)` 105 | - `consent_info_updated` 106 | - `consent_info_update_failed(error_data: FormError)` 107 | 108 | ### ![](admob/addon_template/icon.png?raw=true) Loading and displaying ads 109 | - initialize the plugin 110 | - call the `initialize()` method of the `Admob` node 111 | - wait for the `initialization_completed` signal 112 | - use one or more of the following `load_*()` methods to load ads from the `Admob` node: 113 | - `load_banner_ad(ad_request: LoadAdRequest)` 114 | - `load_interstitia_adl(ad_request: LoadAdRequest)` 115 | - `load_rewarded_ad(ad_request: LoadAdRequest)` 116 | - `load_rewarded_interstitial_ad(ad_request: LoadAdRequest)` 117 | - the `Admob` node will emit the following signals once ads have been loaded or failed to load: 118 | - `banner_ad_loaded(ad_id: String)` 119 | - `banner_ad_failed_to_load(ad_id: String, error_data: LoadAdError)` 120 | - `interstitial_ad_loaded(ad_id: String)` 121 | - `interstitial_ad_failed_to_load(ad_id: String, error_data: LoadAdError)` 122 | - `rewarded_ad_loaded(ad_id: String)` 123 | - `rewarded_ad_failed_to_load(ad_id: String, error_data: LoadAdError)` 124 | - `rewarded_interstitial_ad_loaded(ad_id: String)` 125 | - `rewarded_interstitial_ad_failed_to_load(ad_id: String, error_data: LoadAdError)` 126 | - once ads have been loaded, call corresponding `show_*()` method from the `Admob` node with the `ad_id` received: 127 | - `show_banner_ad(ad_id: String)` 128 | - `show_interstitial_ad(ad_id: String)` 129 | - `show_rewarded_ad(ad_id: String)` 130 | - `show_rewarded_interstitial_ad(ad_id: String)` 131 | 132 | ### ![](admob/addon_template/icon.png?raw=true) Banner Size 133 | - The following methods return the size of a Banner ad: 134 | - `get_banner_dimension()` 135 | - `get_banner_dimension_in_pixels()` 136 | - These methods are not supported for `FLUID` sized ads. For banner ads of size `FLUID`, the `get_banner_dimension()` method will return `(-3, -4)` and the `get_banner_dimension_in_pixels()` method will return `(-1, -1)`. 137 | 138 | ### ![](admob/addon_template/icon.png?raw=true) User Consent 139 | - Methods: 140 | - `get_consent_status()` - Returns a consent status value defined in `ConsentInformation.gd` 141 | - `update_consent_info(params: ConsentRequestParameters)` - To be called if `get_consent_status()` returns status UNKNOWN (0). 142 | - `reset_consent_info()` - To be used only when testing and debugging your application. 143 | - `is_consent_form_available()` 144 | - `load_consent_form()` - To be called if `get_consent_status()` returns status REQUIRED (2) and `is_consent_form_available()` returns `false`. 145 | - `show_consent_form()` - To be called after `consent_form_loaded` signal has been emitted or `is_consent_form_available()` returns `true`. 146 | 147 | 148 | ## ![](admob/addon_template/icon.png?raw=true) Android Export 149 | Android export requires several configuration settings. 150 | 151 | ### ![](admob/addon_template/icon.png?raw=true) File-based Export Configuration 152 | In order to enable file-based export configuration, an `export.cfg` file should be placed in the `addons/AdmobPlugin` directory with the following content: 153 | 154 | ``` 155 | [General] 156 | is_real = false 157 | 158 | [Debug] 159 | app_id = "ca-app-pub-3940256099942544~3347511713" 160 | 161 | [Release] 162 | app_id = "ca-app-pub-3940256099942544~3347511713" 163 | ``` 164 | 165 | The `is_real` and `app_id` configuration items are mandatory and if not found in the `export.cfg` file, then the plugin will fall back to node-based configuration. 166 | 167 | ### ![](admob/addon_template/icon.png?raw=true) Node-based Export Configuration 168 | If `export.cfg` file is not found or file-based configuration fails, then the plugin will attempt to load node-based configuration. 169 | 170 | During Android export, the plugin searches for an `Admob` node in the scene that is open in the Godot Editor. If not found, then the plugin searches for an `Admob` node in the project's main scene. Therefore; 171 | - Make sure that the scene that contains the `Admob` node is selected in the Godot Editor when building and exporting for Android, or 172 | - Make sure that your Godot project's main scene contains an `Admob` node 173 | 174 | 175 | ## ![](admob/addon_template/icon.png?raw=true) Troubleshooting 176 | 177 | ### Missing APP ID 178 | If your game crashes due to missing APP ID, then make sure that you enter your Admob APP ID in the Admob node and pay attention to the [Android Export section](#android-export). 179 | 180 | ### ADB logcat 181 | `adb logcat` is one of the best tools for troubleshooting unexpected behavior 182 | - use `$> adb logcat | grep 'godot'` on Linux 183 | - `adb logcat *:W` to see warnings and errors 184 | - `adb logcat *:E` to see only errors 185 | - `adb logcat | grep 'godot|somethingElse'` to filter using more than one string at the same time 186 | - use `#> adb.exe logcat | select-string "godot"` on powershell (Windows) 187 | 188 | Also check out: 189 | https://docs.godotengine.org/en/stable/tutorials/platform/android/android_plugin.html#troubleshooting 190 | 191 |

192 | 193 | --- 194 | # ![](admob/addon_template/icon.png?raw=true) Credits 195 | Based on [Shin-NiL](https://github.com/Shin-NiL)'s [Godot Admob Plugin](https://github.com/Shin-NiL/Godot-Android-Admob-Plugin) 196 | 197 | Developed by [Cengiz](https://github.com/cengiz-pz) 198 | 199 | Original repository: [Godot Android Admob Plugin](https://github.com/cengiz-pz/godot-android-admob-plugin) 200 | 201 |

202 | 203 | --- 204 | # ![](admob/addon_template/icon.png?raw=true) Tutorials 205 | The following is a video tutorial by [16BitDev](https://www.youtube.com/@16bitdev) that covers the whole process of setting up Admob for your Godot app on Android. 206 | 207 | [![Watch the video](https://img.youtube.com/vi/V9_Gpy0R3RE/0.jpg)](https://www.youtube.com/watch?v=V9_Gpy0R3RE) 208 | 209 |

210 | 211 | ___ 212 | 213 | # ![](admob/addon_template/icon.png?raw=true) Contribution 214 | 215 | This section provides information on how to build the plugin for contributors. 216 | 217 |
218 | 219 | ___ 220 | 221 | ## ![](admob/addon_template/icon.png?raw=true) Prerequisites 222 | 223 | - [Install AndroidStudio](https://developer.android.com/studio) 224 | 225 |
226 | 227 | ___ 228 | 229 | ## ![](admob/addon_template/icon.png?raw=true) Refreshing addon submodule 230 | 231 | - Remove `admob/addon_template` directory 232 | - Run `git submodule update --remote --merge` 233 | 234 |

235 | 236 | --- 237 | # ![](admob/addon_template/icon.png?raw=true) All Plugins 238 | 239 | | Plugin | Android | iOS | 240 | | :---: | :--- | :--- | 241 | | Notification Scheduler | https://github.com/cengiz-pz/godot-android-notification-scheduler-plugin | https://github.com/cengiz-pz/godot-ios-notification-scheduler-plugin | 242 | | Admob | https://github.com/cengiz-pz/godot-android-admob-plugin | https://github.com/cengiz-pz/godot-ios-admob-plugin | 243 | | Deeplink | https://github.com/cengiz-pz/godot-android-deeplink-plugin | https://github.com/cengiz-pz/godot-ios-deeplink-plugin | 244 | | Share | https://github.com/cengiz-pz/godot-android-share-plugin | https://github.com/cengiz-pz/godot-ios-share-plugin | 245 | | In-App Review | https://github.com/cengiz-pz/godot-android-inapp-review-plugin | https://github.com/cengiz-pz/godot-ios-inapp-review-plugin | 246 | -------------------------------------------------------------------------------- /admob/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // 2 | // © 2024-present https://github.com/cengiz-pz 3 | // 4 | 5 | import com.android.build.gradle.internal.tasks.factory.dependsOn 6 | import org.apache.tools.ant.filters.ReplaceTokens 7 | 8 | plugins { 9 | id("com.android.library") 10 | id("org.jetbrains.kotlin.android") version "2.0.10" 11 | } 12 | 13 | val pluginNodeName = "Admob" 14 | val pluginName = pluginNodeName + "Plugin" 15 | val pluginPackageName = "org.godotengine.plugin.android.admob" 16 | val godotVersion = "4.4.1" 17 | val pluginVersion = "4.0" 18 | val targetOs = "android" 19 | val demoAddOnsDirectory = "../demo/addons" 20 | val templateDirectory = "addon_template" 21 | val pluginDependencies = arrayOf( 22 | "androidx.appcompat:appcompat:1.7.0", 23 | "com.google.android.gms:play-services-ads:24.0.0" 24 | ) 25 | 26 | android { 27 | namespace = pluginPackageName 28 | compileSdk = 34 29 | 30 | buildFeatures { 31 | buildConfig = true 32 | } 33 | 34 | defaultConfig { 35 | minSdk = 23 36 | 37 | manifestPlaceholders["godotPluginName"] = pluginName 38 | manifestPlaceholders["godotPluginPackageName"] = pluginPackageName 39 | buildConfigField("String", "GODOT_PLUGIN_NAME", "\"${pluginName}\"") 40 | setProperty("archivesBaseName", "$pluginName") 41 | } 42 | 43 | compileOptions { 44 | sourceCompatibility = JavaVersion.VERSION_17 45 | targetCompatibility = JavaVersion.VERSION_17 46 | } 47 | 48 | kotlinOptions { 49 | jvmTarget = "17" 50 | } 51 | } 52 | 53 | dependencies { 54 | implementation("org.godotengine:godot:$godotVersion.stable") 55 | pluginDependencies.forEach { implementation(it) } 56 | } 57 | 58 | val copyDebugAARToDemoAddons by tasks.registering(Copy::class) { 59 | description = "Copies the generated debug AAR binary to the plugin's addons directory" 60 | from("build/outputs/aar") 61 | include("$pluginName-debug.aar") 62 | into("$demoAddOnsDirectory/$pluginName/bin/debug") 63 | } 64 | 65 | val copyReleaseAARToDemoAddons by tasks.registering(Copy::class) { 66 | description = "Copies the generated release AAR binary to the plugin's addons directory" 67 | from("build/outputs/aar") 68 | include("$pluginName-release.aar") 69 | into("$demoAddOnsDirectory/$pluginName/bin/release") 70 | } 71 | 72 | val cleanDemoAddons by tasks.registering(Delete::class) { 73 | delete("$demoAddOnsDirectory/$pluginName") 74 | } 75 | 76 | val copyPngsToDemo by tasks.registering(Copy::class) { 77 | description = "Copies the PNG images to the plugin's addons directory" 78 | from(templateDirectory) 79 | into("$demoAddOnsDirectory/$pluginName") 80 | include("**/*.png") 81 | } 82 | 83 | val copyAddonsToDemo by tasks.registering(Copy::class) { 84 | description = "Copies the export scripts templates to the plugin's addons directory" 85 | 86 | dependsOn(cleanDemoAddons) 87 | finalizedBy(copyDebugAARToDemoAddons) 88 | finalizedBy(copyReleaseAARToDemoAddons) 89 | finalizedBy(copyPngsToDemo) 90 | 91 | from(templateDirectory) 92 | into("$demoAddOnsDirectory/$pluginName") 93 | include("**/*.gd") 94 | include("**/*.cfg") 95 | var dependencyString = "" 96 | for (i in pluginDependencies.indices) { 97 | dependencyString += "\"${pluginDependencies[i]}\"" 98 | if (i < pluginDependencies.size-1) dependencyString += ", " 99 | } 100 | filter(ReplaceTokens::class, 101 | "tokens" to mapOf( 102 | "pluginName" to pluginName, 103 | "pluginNodeName" to pluginNodeName, 104 | "pluginVersion" to pluginVersion, 105 | "pluginPackage" to pluginPackageName, 106 | "targetOs" to targetOs, 107 | "pluginDependencies" to dependencyString)) 108 | } 109 | 110 | tasks.register("packageDistribution") { 111 | archiveFileName.set("${pluginName}-${pluginVersion}.zip") 112 | destinationDirectory.set(layout.buildDirectory.dir("dist")) 113 | 114 | from("../demo/addons/${pluginName}") { 115 | into("${pluginName}-root/addons/${pluginName}") 116 | } 117 | } 118 | 119 | tasks.named("clean").apply { 120 | dependsOn(cleanDemoAddons) 121 | } 122 | 123 | afterEvaluate { 124 | tasks.named("assembleDebug").configure { 125 | finalizedBy(copyAddonsToDemo) 126 | } 127 | tasks.named("assembleRelease").configure { 128 | finalizedBy(copyAddonsToDemo) 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /admob/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /admob/src/main/java/org/godotengine/plugin/android/admob/AdmobPlugin.java: -------------------------------------------------------------------------------- 1 | // 2 | // © 2024-present https://github.com/cengiz-pz 3 | // 4 | 5 | package org.godotengine.plugin.android.admob; 6 | 7 | import static com.google.android.gms.ads.RequestConfiguration.TAG_FOR_CHILD_DIRECTED_TREATMENT_TRUE; 8 | 9 | import android.annotation.SuppressLint; 10 | import android.app.Activity; 11 | import android.os.Bundle; 12 | import android.provider.Settings; 13 | import android.util.DisplayMetrics; 14 | import android.util.Log; 15 | import android.view.Display; 16 | import android.view.View; 17 | import android.view.WindowManager; 18 | import android.widget.FrameLayout; 19 | 20 | import androidx.annotation.NonNull; 21 | import androidx.collection.ArraySet; 22 | 23 | import com.google.android.gms.ads.AdError; 24 | import com.google.android.gms.ads.AdListener; 25 | import com.google.android.gms.ads.AdRequest; 26 | import com.google.android.gms.ads.AdSize; 27 | import com.google.android.gms.ads.LoadAdError; 28 | import com.google.android.gms.ads.MobileAds; 29 | import com.google.android.gms.ads.RequestConfiguration; 30 | import com.google.android.gms.ads.ResponseInfo; 31 | import com.google.android.gms.ads.initialization.AdapterStatus; 32 | import com.google.android.gms.ads.initialization.InitializationStatus; 33 | import com.google.android.gms.ads.initialization.OnInitializationCompleteListener; 34 | import com.google.android.gms.ads.rewarded.RewardItem; 35 | import com.google.android.gms.ads.rewarded.ServerSideVerificationOptions; 36 | import com.google.android.ump.ConsentDebugSettings; 37 | import com.google.android.ump.ConsentForm; 38 | import com.google.android.ump.ConsentInformation; 39 | import com.google.android.ump.ConsentRequestParameters; 40 | import com.google.android.ump.FormError; 41 | import com.google.android.ump.UserMessagingPlatform; 42 | 43 | import org.godotengine.godot.Dictionary; 44 | import org.godotengine.godot.Godot; 45 | import org.godotengine.godot.plugin.GodotPlugin; 46 | import org.godotengine.godot.plugin.SignalInfo; 47 | import org.godotengine.godot.plugin.UsedByGodot; 48 | 49 | import java.security.MessageDigest; 50 | import java.security.NoSuchAlgorithmException; 51 | import java.util.ArrayList; 52 | import java.util.Arrays; 53 | import java.util.HashMap; 54 | import java.util.List; 55 | import java.util.Locale; 56 | import java.util.Map; 57 | import java.util.Set; 58 | 59 | public class AdmobPlugin extends GodotPlugin { 60 | static final String CLASS_NAME = AdmobPlugin.class.getSimpleName(); 61 | private static final String LOG_TAG = "godot::" + CLASS_NAME; 62 | 63 | private static final String SIGNAL_INITIALIZATION_COMPLETED = "initialization_completed"; 64 | private static final String SIGNAL_BANNER_AD_LOADED = "banner_ad_loaded"; 65 | private static final String SIGNAL_BANNER_AD_FAILED_TO_LOAD = "banner_ad_failed_to_load"; 66 | private static final String SIGNAL_BANNER_AD_REFRESHED = "banner_ad_refreshed"; 67 | private static final String SIGNAL_BANNER_AD_IMPRESSION = "banner_ad_impression"; 68 | private static final String SIGNAL_BANNER_AD_CLICKED = "banner_ad_clicked"; 69 | private static final String SIGNAL_BANNER_AD_OPENED = "banner_ad_opened"; 70 | private static final String SIGNAL_BANNER_AD_CLOSED = "banner_ad_closed"; 71 | private static final String SIGNAL_INTERSTITIAL_AD_LOADED = "interstitial_ad_loaded"; 72 | private static final String SIGNAL_INTERSTITIAL_AD_FAILED_TO_LOAD = "interstitial_ad_failed_to_load"; 73 | private static final String SIGNAL_INTERSTITIAL_AD_REFRESHED = "interstitial_ad_refreshed"; 74 | private static final String SIGNAL_INTERSTITIAL_AD_IMPRESSION = "interstitial_ad_impression"; 75 | private static final String SIGNAL_INTERSTITIAL_AD_CLICKED = "interstitial_ad_clicked"; 76 | private static final String SIGNAL_INTERSTITIAL_AD_SHOWED_FULL_SCREEN_CONTENT = "interstitial_ad_showed_full_screen_content"; 77 | private static final String SIGNAL_INTERSTITIAL_AD_FAILED_TO_SHOW_FULL_SCREEN_CONTENT = "interstitial_ad_failed_to_show_full_screen_content"; 78 | private static final String SIGNAL_INTERSTITIAL_AD_DISMISSED_FULL_SCREEN_CONTENT = "interstitial_ad_dismissed_full_screen_content"; 79 | private static final String SIGNAL_REWARDED_AD_LOADED = "rewarded_ad_loaded"; 80 | private static final String SIGNAL_REWARDED_AD_FAILED_TO_LOAD = "rewarded_ad_failed_to_load"; 81 | private static final String SIGNAL_REWARDED_AD_IMPRESSION = "rewarded_ad_impression"; 82 | private static final String SIGNAL_REWARDED_AD_CLICKED = "rewarded_ad_clicked"; 83 | private static final String SIGNAL_REWARDED_AD_SHOWED_FULL_SCREEN_CONTENT = "rewarded_ad_showed_full_screen_content"; 84 | private static final String SIGNAL_REWARDED_AD_FAILED_TO_SHOW_FULL_SCREEN_CONTENT = "rewarded_ad_failed_to_show_full_screen_content"; 85 | private static final String SIGNAL_REWARDED_AD_DISMISSED_FULL_SCREEN_CONTENT = "rewarded_ad_dismissed_full_screen_content"; 86 | private static final String SIGNAL_REWARDED_AD_USER_EARNED_REWARD = "rewarded_ad_user_earned_reward"; 87 | private static final String SIGNAL_REWARDED_INTERSTITIAL_AD_LOADED = "rewarded_interstitial_ad_loaded"; 88 | private static final String SIGNAL_REWARDED_INTERSTITIAL_AD_FAILED_TO_LOAD = "rewarded_interstitial_ad_failed_to_load"; 89 | private static final String SIGNAL_REWARDED_INTERSTITIAL_AD_IMPRESSION = "rewarded_interstitial_ad_impression"; 90 | private static final String SIGNAL_REWARDED_INTERSTITIAL_AD_CLICKED = "rewarded_interstitial_ad_clicked"; 91 | private static final String SIGNAL_REWARDED_INTERSTITIAL_AD_SHOWED_FULL_SCREEN_CONTENT = "rewarded_interstitial_ad_showed_full_screen_content"; 92 | private static final String SIGNAL_REWARDED_INTERSTITIAL_AD_FAILED_TO_SHOW_FULL_SCREEN_CONTENT = "rewarded_interstitial_ad_failed_to_show_full_screen_content"; 93 | private static final String SIGNAL_REWARDED_INTERSTITIAL_AD_DISMISSED_FULL_SCREEN_CONTENT = "rewarded_interstitial_ad_dismissed_full_screen_content"; 94 | private static final String SIGNAL_REWARDED_INTERSTITIAL_AD_USER_EARNED_REWARD = "rewarded_interstitial_ad_user_earned_reward"; 95 | private static final String SIGNAL_CONSENT_FORM_LOADED = "consent_form_loaded"; 96 | private static final String SIGNAL_CONSENT_FORM_FAILED_TO_LOAD = "consent_form_failed_to_load"; 97 | private static final String SIGNAL_CONSENT_FORM_DISMISSED = "consent_form_dismissed"; 98 | private static final String SIGNAL_CONSENT_INFO_UPDATED = "consent_info_updated"; 99 | private static final String SIGNAL_CONSENT_INFO_UPDATE_FAILED = "consent_info_update_failed"; 100 | 101 | private Activity activity; 102 | 103 | /** 104 | * Whether app is being tested (isReal=false) or app is in production (isReal=true) 105 | */ 106 | private boolean isReal = false; 107 | 108 | 109 | private boolean isForChildDirectedTreatment = false; 110 | 111 | /** 112 | * Ads are personalized by default, GDPR compliance within the European Economic Area may require disabling of personalization. 113 | */ 114 | private boolean isPersonalized = true; 115 | private String maxAdContentRating = ""; 116 | private Bundle extras = null; 117 | 118 | private FrameLayout layout = null; 119 | 120 | private int bannerAdIdSequence; 121 | private int interstitialAdIdSequence; 122 | private int rewardedAdIdSequence; 123 | private int rewardedInterstitialAdIdSequence; 124 | 125 | private boolean isInitialized; 126 | 127 | private Map bannerAds; 128 | private Map interstitialAds; 129 | private Map rewardedAds; 130 | private Map rewardedInterstitialAds; 131 | 132 | private ConsentForm consentForm; 133 | 134 | 135 | public AdmobPlugin(Godot godot) { 136 | super(godot); 137 | 138 | bannerAds = new HashMap<>(); 139 | interstitialAds = new HashMap<>(); 140 | rewardedAds = new HashMap<>(); 141 | rewardedInterstitialAds = new HashMap<>(); 142 | 143 | isInitialized = false; 144 | } 145 | 146 | @NonNull 147 | @Override 148 | public String getPluginName() { 149 | return CLASS_NAME; 150 | } 151 | 152 | @NonNull 153 | @Override 154 | public Set getPluginSignals() { 155 | Set signals = new ArraySet<>(); 156 | 157 | signals.add(new SignalInfo(SIGNAL_INITIALIZATION_COMPLETED, Dictionary.class)); 158 | 159 | signals.add(new SignalInfo(SIGNAL_BANNER_AD_LOADED, String.class)); 160 | signals.add(new SignalInfo(SIGNAL_BANNER_AD_FAILED_TO_LOAD, String.class, Dictionary.class)); 161 | signals.add(new SignalInfo(SIGNAL_BANNER_AD_REFRESHED, String.class)); 162 | signals.add(new SignalInfo(SIGNAL_BANNER_AD_IMPRESSION, String.class)); 163 | signals.add(new SignalInfo(SIGNAL_BANNER_AD_CLICKED, String.class)); 164 | signals.add(new SignalInfo(SIGNAL_BANNER_AD_OPENED, String.class)); 165 | signals.add(new SignalInfo(SIGNAL_BANNER_AD_CLOSED, String.class)); 166 | 167 | signals.add(new SignalInfo(SIGNAL_INTERSTITIAL_AD_LOADED, String.class)); 168 | signals.add(new SignalInfo(SIGNAL_INTERSTITIAL_AD_FAILED_TO_LOAD, String.class, Dictionary.class)); 169 | signals.add(new SignalInfo(SIGNAL_INTERSTITIAL_AD_REFRESHED, String.class)); 170 | signals.add(new SignalInfo(SIGNAL_INTERSTITIAL_AD_IMPRESSION, String.class)); 171 | signals.add(new SignalInfo(SIGNAL_INTERSTITIAL_AD_CLICKED, String.class)); 172 | signals.add(new SignalInfo(SIGNAL_INTERSTITIAL_AD_SHOWED_FULL_SCREEN_CONTENT, String.class)); 173 | signals.add(new SignalInfo(SIGNAL_INTERSTITIAL_AD_FAILED_TO_SHOW_FULL_SCREEN_CONTENT, String.class, Dictionary.class)); 174 | signals.add(new SignalInfo(SIGNAL_INTERSTITIAL_AD_DISMISSED_FULL_SCREEN_CONTENT, String.class)); 175 | 176 | signals.add(new SignalInfo(SIGNAL_REWARDED_AD_LOADED, String.class)); 177 | signals.add(new SignalInfo(SIGNAL_REWARDED_AD_FAILED_TO_LOAD, String.class, Dictionary.class)); 178 | signals.add(new SignalInfo(SIGNAL_REWARDED_AD_IMPRESSION, String.class)); 179 | signals.add(new SignalInfo(SIGNAL_REWARDED_AD_CLICKED, String.class)); 180 | signals.add(new SignalInfo(SIGNAL_REWARDED_AD_SHOWED_FULL_SCREEN_CONTENT, String.class)); 181 | signals.add(new SignalInfo(SIGNAL_REWARDED_AD_FAILED_TO_SHOW_FULL_SCREEN_CONTENT, String.class, Dictionary.class)); 182 | signals.add(new SignalInfo(SIGNAL_REWARDED_AD_DISMISSED_FULL_SCREEN_CONTENT, String.class)); 183 | signals.add(new SignalInfo(SIGNAL_REWARDED_AD_USER_EARNED_REWARD, String.class, Dictionary.class)); 184 | 185 | signals.add(new SignalInfo(SIGNAL_REWARDED_INTERSTITIAL_AD_LOADED, String.class)); 186 | signals.add(new SignalInfo(SIGNAL_REWARDED_INTERSTITIAL_AD_FAILED_TO_LOAD, String.class, Dictionary.class)); 187 | signals.add(new SignalInfo(SIGNAL_REWARDED_INTERSTITIAL_AD_IMPRESSION, String.class)); 188 | signals.add(new SignalInfo(SIGNAL_REWARDED_INTERSTITIAL_AD_CLICKED, String.class)); 189 | signals.add(new SignalInfo(SIGNAL_REWARDED_INTERSTITIAL_AD_SHOWED_FULL_SCREEN_CONTENT, String.class)); 190 | signals.add(new SignalInfo(SIGNAL_REWARDED_INTERSTITIAL_AD_FAILED_TO_SHOW_FULL_SCREEN_CONTENT, String.class, Dictionary.class)); 191 | signals.add(new SignalInfo(SIGNAL_REWARDED_INTERSTITIAL_AD_DISMISSED_FULL_SCREEN_CONTENT, String.class)); 192 | signals.add(new SignalInfo(SIGNAL_REWARDED_INTERSTITIAL_AD_USER_EARNED_REWARD, String.class, Dictionary.class)); 193 | 194 | signals.add(new SignalInfo(SIGNAL_CONSENT_FORM_LOADED)); 195 | signals.add(new SignalInfo(SIGNAL_CONSENT_FORM_FAILED_TO_LOAD, Dictionary.class)); 196 | signals.add(new SignalInfo(SIGNAL_CONSENT_FORM_DISMISSED, Dictionary.class)); 197 | 198 | signals.add(new SignalInfo(SIGNAL_CONSENT_INFO_UPDATED)); 199 | signals.add(new SignalInfo(SIGNAL_CONSENT_INFO_UPDATE_FAILED, Dictionary.class)); 200 | 201 | return signals; 202 | } 203 | 204 | @UsedByGodot 205 | public void initialize() { 206 | Log.d(LOG_TAG, "initialize()"); 207 | 208 | bannerAdIdSequence = 0; 209 | interstitialAdIdSequence = 0; 210 | rewardedAdIdSequence = 0; 211 | rewardedInterstitialAdIdSequence = 0; 212 | 213 | bannerAds.clear(); 214 | interstitialAds.clear(); 215 | rewardedAds.clear(); 216 | rewardedInterstitialAds.clear(); 217 | 218 | isInitialized = false; 219 | 220 | MobileAds.initialize(activity, new OnInitializationCompleteListener() { 221 | @Override 222 | public void onInitializationComplete(InitializationStatus initializationStatus) { 223 | isInitialized = true; 224 | emitSignal(SIGNAL_INITIALIZATION_COMPLETED, convert(initializationStatus)); 225 | } 226 | }); 227 | } 228 | 229 | @UsedByGodot 230 | public void set_request_configuration(Dictionary configData) { 231 | Log.d(LOG_TAG, "set_request_configuration()"); 232 | MobileAds.setRequestConfiguration(createRequestConfiguration(configData)); 233 | } 234 | 235 | @UsedByGodot 236 | public Dictionary get_initialization_status() { 237 | Log.d(LOG_TAG, "get_initialization_status()"); 238 | return convert(MobileAds.getInitializationStatus()); 239 | } 240 | 241 | @UsedByGodot 242 | public Dictionary get_current_adaptive_banner_size(int width) { 243 | Log.d(LOG_TAG, "get_current_adaptive_banner_size()"); 244 | int currentWidth = (width == AdSize.FULL_WIDTH) ? Banner.getAdWidth(activity) : width; 245 | return convert(AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(activity, currentWidth)); 246 | } 247 | 248 | @UsedByGodot 249 | public Dictionary get_portrait_adaptive_banner_size(int width) { 250 | Log.d(LOG_TAG, "get_portrait_adaptive_banner_size()"); 251 | int currentWidth = (width == AdSize.FULL_WIDTH) ? Banner.getAdWidth(activity) : width; 252 | return convert(AdSize.getPortraitAnchoredAdaptiveBannerAdSize(activity, currentWidth)); 253 | } 254 | 255 | @UsedByGodot 256 | public Dictionary get_landscape_adaptive_banner_size(int width) { 257 | Log.d(LOG_TAG, "get_landscape_adaptive_banner_size()"); 258 | int currentWidth = (width == AdSize.FULL_WIDTH) ? Banner.getAdWidth(activity) : width; 259 | return convert(AdSize.getLandscapeAnchoredAdaptiveBannerAdSize(activity, currentWidth)); 260 | } 261 | 262 | @UsedByGodot 263 | public void load_banner_ad(Dictionary adData) { 264 | if (isInitialized) { 265 | Log.d(LOG_TAG, "load_banner_ad()"); 266 | 267 | if (adData.containsKey("ad_unit_id")) { 268 | String adUnitId = (String) adData.get("ad_unit_id"); 269 | String adId = String.format("%s-%d", adUnitId, ++bannerAdIdSequence); 270 | Banner banner = new Banner(adId, adUnitId, adData, createAdRequest(adData), activity, layout, 271 | new BannerListener() { 272 | @Override 273 | public void onAdLoaded(String adId) { 274 | emitSignal(SIGNAL_BANNER_AD_LOADED, adId); 275 | } 276 | 277 | @Override 278 | public void onAdRefreshed(String adId) { 279 | Log.d(LOG_TAG, String.format("onAdRefreshed(%s) banner", adId)); 280 | emitSignal(SIGNAL_BANNER_AD_REFRESHED, adId); 281 | } 282 | 283 | @Override 284 | public void onAdFailedToLoad(String adId, LoadAdError adError) { 285 | emitSignal(SIGNAL_BANNER_AD_FAILED_TO_LOAD, adId, convert(adError)); 286 | } 287 | 288 | @Override 289 | public void onAdClicked(String adId) { 290 | emitSignal(SIGNAL_BANNER_AD_CLICKED, adId); 291 | } 292 | 293 | @Override 294 | public void onAdClosed(String adId) { 295 | emitSignal(SIGNAL_BANNER_AD_CLOSED, adId); 296 | } 297 | 298 | @Override 299 | public void onAdImpression(String adId) { 300 | emitSignal(SIGNAL_BANNER_AD_IMPRESSION, adId); 301 | } 302 | 303 | @Override 304 | public void onAdOpened(String adId) { 305 | emitSignal(SIGNAL_BANNER_AD_OPENED, adId); 306 | } 307 | }); 308 | bannerAds.put(adId, banner); 309 | activity.runOnUiThread(() -> { 310 | banner.load(); 311 | }); 312 | } else { 313 | Log.e(LOG_TAG, "load_banner_ad(): Error: Ad unit id is required!"); 314 | } 315 | } 316 | else { 317 | Log.e(LOG_TAG, "load_banner_ad(): Error: Plugin is not initialized!"); 318 | } 319 | } 320 | 321 | @UsedByGodot 322 | public void show_banner_ad(String adId) { 323 | activity.runOnUiThread(() -> { 324 | if (bannerAds.containsKey(adId)) { 325 | Log.d(LOG_TAG, String.format("show_banner_ad(): %s", adId)); 326 | Banner bannerAd = bannerAds.get(adId); 327 | bannerAd.show(); 328 | } 329 | else { 330 | Log.e(LOG_TAG, String.format("show_banner_ad(): Error: banner ad %s not found", adId)); 331 | } 332 | }); 333 | } 334 | 335 | @UsedByGodot 336 | public void hide_banner_ad(String adId) { 337 | activity.runOnUiThread(() -> { 338 | if (bannerAds.containsKey(adId)) { 339 | Log.d(LOG_TAG, String.format("hide_banner_ad(): %s", adId)); 340 | Banner bannerAd = bannerAds.get(adId); 341 | bannerAd.hide(); 342 | } 343 | else { 344 | Log.e(LOG_TAG, String.format("hide_banner_ad(): Error: banner ad %s not found", adId)); 345 | } 346 | }); 347 | } 348 | 349 | @UsedByGodot 350 | public void remove_banner_ad(String adId) { 351 | activity.runOnUiThread(() -> { 352 | if (bannerAds.containsKey(adId)) { 353 | Log.d(LOG_TAG, String.format("remove_banner_ad(): %s", adId)); 354 | Banner bannerAd = bannerAds.remove(adId); 355 | bannerAd.remove(); 356 | } 357 | else { 358 | Log.e(LOG_TAG, String.format("remove_banner_ad(): Error: banner ad %s not found", adId)); 359 | } 360 | }); 361 | } 362 | 363 | @UsedByGodot 364 | public int get_banner_width(String adId) { 365 | int result = 0; 366 | 367 | if (bannerAds.containsKey(adId)) { 368 | Log.d(LOG_TAG, String.format("get_banner_width(): %s", adId)); 369 | Banner bannerAd = bannerAds.get(adId); 370 | result = bannerAd.getWidth(); 371 | } 372 | else { 373 | Log.e(LOG_TAG, String.format("get_banner_width(): Error: banner ad %s not found", adId)); 374 | } 375 | 376 | return result; 377 | } 378 | 379 | @UsedByGodot 380 | public int get_banner_height(String adId) { 381 | int result = 0; 382 | 383 | if (bannerAds.containsKey(adId)) { 384 | Log.d(LOG_TAG, String.format("get_banner_height(): %s", adId)); 385 | Banner bannerAd = bannerAds.get(adId); 386 | result = bannerAd.getHeight(); 387 | } 388 | else { 389 | Log.e(LOG_TAG, String.format("get_banner_height(): Error: banner ad %s not found", adId)); 390 | } 391 | 392 | return result; 393 | } 394 | 395 | @UsedByGodot 396 | public int get_banner_width_in_pixels(String adId) { 397 | int result = 0; 398 | 399 | if (bannerAds.containsKey(adId)) { 400 | Banner bannerAd = bannerAds.get(adId); 401 | result = bannerAd.getWidthInPixels(); 402 | Log.d(LOG_TAG, String.format("get_banner_width_in_pixels(): %s - %d", adId, result)); 403 | } 404 | else { 405 | Log.e(LOG_TAG, String.format("get_banner_width_in_pixels(): Error: banner ad %s not found", adId)); 406 | } 407 | 408 | return result; 409 | } 410 | 411 | @UsedByGodot 412 | public int get_banner_height_in_pixels(String adId) { 413 | int result = 0; 414 | 415 | if (bannerAds.containsKey(adId)) { 416 | Banner bannerAd = bannerAds.get(adId); 417 | result = bannerAd.getHeightInPixels(); 418 | Log.d(LOG_TAG, String.format("get_banner_height_in_pixels(): %s - %d", adId, result)); 419 | } 420 | else { 421 | Log.e(LOG_TAG, String.format("get_banner_height_in_pixels(): Error: banner ad %s not found", adId)); 422 | } 423 | 424 | return result; 425 | } 426 | 427 | @UsedByGodot 428 | public void load_interstitial_ad(Dictionary adData) { 429 | if (isInitialized) { 430 | Log.d(LOG_TAG, "load_interstitial_ad()"); 431 | 432 | if (adData.containsKey("ad_unit_id")) { 433 | String adUnitId = (String) adData.get("ad_unit_id"); 434 | String adId = String.format("%s-%d", adUnitId, ++interstitialAdIdSequence); 435 | 436 | activity.runOnUiThread(() -> { 437 | Interstitial ad = new Interstitial(adId, adUnitId, createAdRequest(adData), activity, new InterstitialListener() { 438 | @Override 439 | public void onInterstitialLoaded(String adId) { 440 | emitSignal(SIGNAL_INTERSTITIAL_AD_LOADED, adId); 441 | } 442 | 443 | @Override 444 | public void onInterstitialReloaded(String adId) { 445 | emitSignal(SIGNAL_INTERSTITIAL_AD_REFRESHED, adId); 446 | } 447 | 448 | @Override 449 | public void onInterstitialFailedToLoad(String adId, LoadAdError loadAdError) { 450 | emitSignal(SIGNAL_INTERSTITIAL_AD_FAILED_TO_LOAD, adId, convert(loadAdError)); 451 | } 452 | 453 | @Override 454 | public void onInterstitialFailedToShow(String adId, AdError adError) { 455 | emitSignal(SIGNAL_INTERSTITIAL_AD_FAILED_TO_SHOW_FULL_SCREEN_CONTENT, adId, convert(adError)); 456 | } 457 | 458 | @Override 459 | public void onInterstitialOpened(String adId) { 460 | emitSignal(SIGNAL_INTERSTITIAL_AD_SHOWED_FULL_SCREEN_CONTENT, adId); 461 | } 462 | 463 | @Override 464 | public void onInterstitialClosed(String adId) { 465 | emitSignal(SIGNAL_INTERSTITIAL_AD_DISMISSED_FULL_SCREEN_CONTENT, adId); 466 | } 467 | 468 | @Override 469 | public void onInterstitialClicked(String adId) { 470 | emitSignal(SIGNAL_INTERSTITIAL_AD_CLICKED, adId); 471 | } 472 | 473 | @Override 474 | public void onInterstitialImpression(String adId) { 475 | emitSignal(SIGNAL_INTERSTITIAL_AD_IMPRESSION, adId); 476 | } 477 | }); 478 | interstitialAds.put(adId, ad); 479 | Log.d(LOG_TAG, String.format("load_interstitial_ad(): %s", adId)); 480 | ad.load(); 481 | }); 482 | } else { 483 | Log.e(LOG_TAG, "load_interstitial_ad(): Error: Ad unit id is required!"); 484 | } 485 | } 486 | else { 487 | Log.e(LOG_TAG, "load_interstitial_ad(): Error: Plugin is not initialized!"); 488 | } 489 | } 490 | 491 | @UsedByGodot 492 | public void show_interstitial_ad(String adId) { 493 | activity.runOnUiThread(() -> { 494 | if (interstitialAds.containsKey(adId)) { 495 | Log.d(LOG_TAG, String.format("show_interstitial_ad(): %s", adId)); 496 | Interstitial ad = interstitialAds.get(adId); 497 | assert ad != null; 498 | ad.show(); 499 | } 500 | else { 501 | Log.e(LOG_TAG, String.format("show_interstitial_ad(): Error: ad %s not found", adId)); 502 | } 503 | }); 504 | } 505 | 506 | @UsedByGodot 507 | public void remove_interstitial_ad(String adId) { 508 | if (interstitialAds.containsKey(adId)) { 509 | Log.d(LOG_TAG, String.format("remove_interstitial_ad(): %s", adId)); 510 | interstitialAds.remove(adId); 511 | } 512 | else { 513 | Log.e(LOG_TAG, String.format("remove_interstitial_ad(): Error: ad %s not found", adId)); 514 | } 515 | } 516 | 517 | @UsedByGodot 518 | public void load_rewarded_ad(Dictionary adData) { 519 | if (isInitialized) { 520 | Log.d(LOG_TAG, "load_rewarded_ad()"); 521 | 522 | if (adData.containsKey("ad_unit_id")) { 523 | String adUnitId = (String) adData.get("ad_unit_id"); 524 | String adId = String.format("%s-%d", adUnitId, ++rewardedAdIdSequence); 525 | 526 | activity.runOnUiThread(() -> { 527 | RewardedVideo ad = new RewardedVideo(adId, adUnitId, createAdRequest(adData), activity, new RewardedVideoListener() { 528 | @Override 529 | public void onRewardedVideoLoaded(String adId) { 530 | emitSignal(SIGNAL_REWARDED_AD_LOADED, adId); 531 | } 532 | 533 | @Override 534 | public void onRewardedVideoFailedToLoad(String adId, LoadAdError loadAdError) { 535 | emitSignal(SIGNAL_REWARDED_AD_FAILED_TO_LOAD, adId, convert(loadAdError)); 536 | } 537 | 538 | @Override 539 | public void onRewardedVideoOpened(String adId) { 540 | emitSignal(SIGNAL_REWARDED_AD_SHOWED_FULL_SCREEN_CONTENT, adId); 541 | } 542 | 543 | @Override 544 | public void onRewardedVideoFailedToShow(String adId, AdError adError) { 545 | emitSignal(SIGNAL_REWARDED_AD_FAILED_TO_SHOW_FULL_SCREEN_CONTENT, adId, convert(adError)); 546 | } 547 | 548 | @Override 549 | public void onRewardedVideoClosed(String adId) { 550 | emitSignal(SIGNAL_REWARDED_AD_DISMISSED_FULL_SCREEN_CONTENT, adId); 551 | } 552 | 553 | @Override 554 | public void onRewardedClicked(String adId) { 555 | emitSignal(SIGNAL_REWARDED_AD_CLICKED, adId); 556 | } 557 | 558 | @Override 559 | public void onRewardedAdImpression(String adId) { 560 | emitSignal(SIGNAL_REWARDED_AD_IMPRESSION, adId); 561 | } 562 | 563 | @Override 564 | public void onRewarded(String adId, RewardItem reward) { 565 | emitSignal(SIGNAL_REWARDED_AD_USER_EARNED_REWARD, adId, convert(reward)); 566 | } 567 | }); 568 | ad.setServerSideVerificationOptions(createSSVO(adData)); 569 | rewardedAds.put(adId, ad); 570 | Log.d(LOG_TAG, String.format("load_rewarded_ad(): %s", adId)); 571 | ad.load(); 572 | }); 573 | } else { 574 | Log.e(LOG_TAG, "load_rewarded_ad(): Error: Ad unit id is required!"); 575 | } 576 | } 577 | else { 578 | Log.e(LOG_TAG, "load_rewarded_ad(): Error: Plugin is not initialized!"); 579 | } 580 | } 581 | 582 | @UsedByGodot 583 | public void show_rewarded_ad(String adId) { 584 | activity.runOnUiThread(() -> { 585 | if (rewardedAds.containsKey(adId)) { 586 | Log.d(LOG_TAG, String.format("show_rewarded_ad(): %s", adId)); 587 | RewardedVideo ad = rewardedAds.get(adId); 588 | ad.show(); 589 | } 590 | else { 591 | Log.e(LOG_TAG, String.format("show_rewarded_ad(): Error: ad %s not found", adId)); 592 | } 593 | }); 594 | } 595 | 596 | @UsedByGodot 597 | public void remove_rewarded_ad(String adId) { 598 | if (rewardedAds.containsKey(adId)) { 599 | Log.d(LOG_TAG, String.format("remove_rewarded_ad(): %s", adId)); 600 | rewardedAds.remove(adId); 601 | } 602 | else { 603 | Log.e(LOG_TAG, String.format("remove_rewarded_ad(): Error: ad %s not found", adId)); 604 | } 605 | } 606 | 607 | @UsedByGodot 608 | public void load_rewarded_interstitial_ad(Dictionary adData) { 609 | if (isInitialized) { 610 | Log.d(LOG_TAG, "load_rewarded_interstitial_ad()"); 611 | 612 | if (adData.containsKey("ad_unit_id")) { 613 | String adUnitId = (String) adData.get("ad_unit_id"); 614 | String adId = String.format("%s-%d", adUnitId, ++rewardedInterstitialAdIdSequence); 615 | 616 | activity.runOnUiThread(() -> { 617 | RewardedInterstitial ad = new RewardedInterstitial(adId, adUnitId, createAdRequest(adData), activity, 618 | new RewardedInterstitialListener() { 619 | @Override 620 | public void onRewardedInterstitialLoaded(String adId) { 621 | emitSignal(SIGNAL_REWARDED_INTERSTITIAL_AD_LOADED, adId); 622 | } 623 | 624 | @Override 625 | public void onRewardedInterstitialFailedToLoad(String adId, LoadAdError loadAdError) { 626 | emitSignal(SIGNAL_REWARDED_INTERSTITIAL_AD_FAILED_TO_LOAD, adId, convert(loadAdError)); 627 | } 628 | 629 | @Override 630 | public void onRewardedInterstitialOpened(String adId) { 631 | emitSignal(SIGNAL_REWARDED_INTERSTITIAL_AD_SHOWED_FULL_SCREEN_CONTENT, adId); 632 | } 633 | 634 | @Override 635 | public void onRewardedInterstitialFailedToShow(String adId, AdError adError) { 636 | emitSignal(SIGNAL_REWARDED_INTERSTITIAL_AD_FAILED_TO_SHOW_FULL_SCREEN_CONTENT, adId, convert(adError)); 637 | } 638 | 639 | @Override 640 | public void onRewardedInterstitialClosed(String adId) { 641 | emitSignal(SIGNAL_REWARDED_INTERSTITIAL_AD_DISMISSED_FULL_SCREEN_CONTENT, adId); 642 | } 643 | 644 | @Override 645 | public void onRewardedClicked(String adId) { 646 | emitSignal(SIGNAL_REWARDED_INTERSTITIAL_AD_CLICKED, adId); 647 | } 648 | 649 | @Override 650 | public void onRewardedAdImpression(String adId) { 651 | emitSignal(SIGNAL_REWARDED_INTERSTITIAL_AD_IMPRESSION, adId); 652 | } 653 | 654 | @Override 655 | public void onRewarded(String adId, RewardItem reward) { 656 | emitSignal(SIGNAL_REWARDED_INTERSTITIAL_AD_USER_EARNED_REWARD, adId, convert(reward)); 657 | } 658 | }); 659 | ad.setServerSideVerificationOptions(createSSVO(adData)); 660 | rewardedInterstitialAds.put(adId, ad); 661 | Log.d(LOG_TAG, String.format("load_rewarded_interstitial_ad(): %s", adId)); 662 | ad.load(); 663 | }); 664 | } else { 665 | Log.e(LOG_TAG, "load_rewarded_interstitial_ad(): Error: Ad unit id is required!"); 666 | } 667 | } 668 | else { 669 | Log.e(LOG_TAG, "load_rewarded_interstitial_ad(): Error: Plugin is not initialized!"); 670 | } 671 | } 672 | 673 | @UsedByGodot 674 | public void show_rewarded_interstitial_ad(String adId) { 675 | activity.runOnUiThread(() -> { 676 | if (rewardedInterstitialAds.containsKey(adId)) { 677 | Log.d(LOG_TAG, String.format("show_rewarded_interstitial_ad(): %s", adId)); 678 | RewardedInterstitial ad = rewardedInterstitialAds.get(adId); 679 | ad.show(); 680 | } else { 681 | Log.e(LOG_TAG, String.format("show_rewarded_interstitial_ad(): Error: ad %s not found", adId)); 682 | } 683 | }); 684 | } 685 | 686 | @UsedByGodot 687 | public void remove_rewarded_interstitial_ad(String adId) { 688 | if (rewardedInterstitialAds.containsKey(adId)) { 689 | Log.d(LOG_TAG, String.format("remove_rewarded_interstitial_ad(): %s", adId)); 690 | rewardedInterstitialAds.remove(adId); 691 | } 692 | else { 693 | Log.e(LOG_TAG, String.format("remove_rewarded_interstitial_ad(): Error: ad %s not found", adId)); 694 | } 695 | } 696 | 697 | @UsedByGodot 698 | public void load_consent_form() { 699 | Log.d(LOG_TAG, "load_consent_form()"); 700 | activity.runOnUiThread(() -> { 701 | UserMessagingPlatform.loadConsentForm( 702 | activity, 703 | (UserMessagingPlatform.OnConsentFormLoadSuccessListener) loadedForm -> { 704 | consentForm = loadedForm; 705 | emitSignal(SIGNAL_CONSENT_FORM_LOADED); 706 | }, 707 | (UserMessagingPlatform.OnConsentFormLoadFailureListener) formError -> { 708 | emitSignal(SIGNAL_CONSENT_FORM_FAILED_TO_LOAD, convert(formError)); 709 | } 710 | ); 711 | }); 712 | } 713 | 714 | @UsedByGodot 715 | public void show_consent_form() { 716 | activity.runOnUiThread(() -> { 717 | if (consentForm == null) { 718 | Log.e(LOG_TAG, "show_consent_form(): Error: consent form not found!"); 719 | } else { 720 | Log.d(LOG_TAG, "show_consent_form()"); 721 | consentForm.show(activity, (ConsentForm.OnConsentFormDismissedListener) formError -> { 722 | emitSignal(SIGNAL_CONSENT_FORM_DISMISSED, convert(formError)); 723 | }); 724 | } 725 | }); 726 | } 727 | 728 | @UsedByGodot 729 | public int get_consent_status() { 730 | Log.d(LOG_TAG, "get_consent_status()"); 731 | return UserMessagingPlatform.getConsentInformation(activity).getConsentStatus(); 732 | } 733 | 734 | @UsedByGodot 735 | public boolean is_consent_form_available() { 736 | Log.d(LOG_TAG, "is_consent_form_available()"); 737 | return UserMessagingPlatform.getConsentInformation(activity).isConsentFormAvailable(); 738 | } 739 | 740 | @UsedByGodot 741 | public void update_consent_info(Dictionary consentRequestParameters) { 742 | Log.d(LOG_TAG, "update_consent_info()"); 743 | ConsentInformation consentInformation = UserMessagingPlatform.getConsentInformation(activity); 744 | 745 | consentInformation.requestConsentInfoUpdate( 746 | activity, 747 | createConsentRequestParameters(consentRequestParameters), 748 | (ConsentInformation.OnConsentInfoUpdateSuccessListener) () -> { 749 | emitSignal(SIGNAL_CONSENT_INFO_UPDATED); 750 | }, 751 | (ConsentInformation.OnConsentInfoUpdateFailureListener) requestConsentError -> { 752 | emitSignal(SIGNAL_CONSENT_INFO_UPDATE_FAILED, convert(requestConsentError)); 753 | Log.w(LOG_TAG, String.format("%s: %s", requestConsentError.getErrorCode(), requestConsentError.getMessage())); 754 | } 755 | ); 756 | } 757 | 758 | @UsedByGodot 759 | public void reset_consent_info() { 760 | Log.d(LOG_TAG, "reset_consent_info()"); 761 | UserMessagingPlatform.getConsentInformation(activity).reset(); 762 | } 763 | 764 | 765 | @Override 766 | public View onMainCreate(Activity activity) { 767 | this.activity = activity; 768 | this.activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING); 769 | this.layout = new FrameLayout(activity); // create and add a new layout to Godot 770 | return layout; 771 | } 772 | 773 | 774 | private Dictionary convert(InitializationStatus initializationStatus) { 775 | Dictionary dict = new Dictionary(); 776 | 777 | Map adapterMap = initializationStatus.getAdapterStatusMap(); 778 | for (String adapterClass : adapterMap.keySet()) { 779 | AdapterStatus adapterStatus = adapterMap.get(adapterClass); 780 | 781 | Dictionary statusDict = new Dictionary(); 782 | statusDict.put("latency", adapterStatus.getLatency()); 783 | statusDict.put("initializationState", adapterStatus.getInitializationState()); 784 | statusDict.put("description", adapterStatus.getDescription()); 785 | 786 | dict.put(adapterClass, statusDict); 787 | } 788 | 789 | return dict; 790 | } 791 | 792 | private Dictionary convert(AdSize size) { 793 | Dictionary dict = new Dictionary(); 794 | 795 | dict.put("width", size.getWidth()); 796 | dict.put("height", size.getHeight()); 797 | 798 | return dict; 799 | } 800 | 801 | private Dictionary convert(AdError error) { 802 | Dictionary dict = new Dictionary(); 803 | 804 | dict.put("code", error.getCode()); 805 | dict.put("domain", error.getDomain()); 806 | dict.put("message", error.getMessage()); 807 | dict.put("cause", error.getCause() == null ? new Dictionary() : convert(error.getCause())); 808 | 809 | return dict; 810 | } 811 | 812 | private Dictionary convert(LoadAdError error) { 813 | Dictionary dict = convert((AdError) error); 814 | 815 | dict.put("response_info", error.getResponseInfo().toString()); 816 | 817 | return dict; 818 | } 819 | 820 | private Dictionary convert(FormError error) { 821 | Dictionary dict = new Dictionary(); 822 | 823 | if (error == null) { 824 | dict.put("code", 0); 825 | dict.put("message", ""); 826 | } else { 827 | dict.put("code", error.getErrorCode()); 828 | dict.put("message", error.getMessage()); 829 | } 830 | 831 | return dict; 832 | } 833 | 834 | private Dictionary convert(RewardItem item) { 835 | Dictionary dict = new Dictionary(); 836 | 837 | dict.put("amount", item.getAmount()); 838 | dict.put("type", item.getType()); 839 | 840 | return dict; 841 | } 842 | 843 | private RequestConfiguration createRequestConfiguration(Dictionary data) { 844 | RequestConfiguration.Builder builder = MobileAds.getRequestConfiguration().toBuilder(); 845 | 846 | if (data.containsKey("max_ad_content_rating")) 847 | builder.setMaxAdContentRating((String) data.get("max_ad_content_rating")); 848 | 849 | if (data.containsKey("tag_for_child_directed_treatment")) 850 | builder.setTagForChildDirectedTreatment((int) data.get("tag_for_child_directed_treatment")); 851 | 852 | if (data.containsKey("tag_for_under_age_of_consent")) 853 | builder.setTagForUnderAgeOfConsent((int) data.get("tag_for_under_age_of_consent")); 854 | 855 | if (data.containsKey("personalization_state")) 856 | builder.setPublisherPrivacyPersonalizationState(getPublisherPrivacyPersonalizationState((int) data.get("personalization_state"))); 857 | 858 | ArrayList testDeviceIds = new ArrayList<>(); 859 | if (data.containsKey("test_device_ids")) 860 | testDeviceIds.addAll(Arrays.asList((String[]) data.get("test_device_ids"))); 861 | 862 | if (data.containsKey("is_real")) { 863 | if ((boolean) data.get("is_real") == false) { 864 | testDeviceIds.add(AdRequest.DEVICE_ID_EMULATOR); 865 | testDeviceIds.add(getAdMobDeviceId()); 866 | } 867 | } 868 | 869 | if (testDeviceIds.isEmpty() == false) 870 | builder.setTestDeviceIds(testDeviceIds); 871 | 872 | return builder.build(); 873 | } 874 | 875 | private AdRequest createAdRequest(Dictionary data) { 876 | AdRequest.Builder builder = new AdRequest.Builder(); 877 | 878 | if (data.containsKey("request_agent")) { 879 | String requestAgent = (String) data.get("request_agent"); 880 | if (requestAgent != null && !requestAgent.isEmpty()) { 881 | builder.setRequestAgent(requestAgent); 882 | } 883 | } 884 | 885 | // TODO: mediation support 886 | 887 | if (data.containsKey("keywords")) { 888 | for (String keyword : (String[]) data.get("keywords")) { 889 | builder.addKeyword(keyword); 890 | } 891 | } 892 | 893 | return builder.build(); 894 | } 895 | 896 | private ServerSideVerificationOptions createSSVO(Dictionary data) { 897 | ServerSideVerificationOptions.Builder builder = new ServerSideVerificationOptions.Builder(); 898 | 899 | if (data.containsKey("custom_data")) { 900 | builder.setCustomData((String) data.get("custom_data")); 901 | } 902 | 903 | if (data.containsKey("user_id")) { 904 | builder.setUserId((String) data.get("user_id")); 905 | } 906 | 907 | return builder.build(); 908 | } 909 | 910 | private ConsentRequestParameters createConsentRequestParameters(Dictionary data) { 911 | ConsentRequestParameters.Builder builder = new ConsentRequestParameters.Builder(); 912 | 913 | if (data.containsKey("tag_for_under_age_of_consent")) { 914 | builder.setTagForUnderAgeOfConsent((boolean) data.get("tag_for_under_age_of_consent")); 915 | } 916 | 917 | if (data.containsKey("is_real") && (boolean) data.get("is_real") == false) { 918 | Log.d(LOG_TAG, "Creating debug settings for user consent."); 919 | ConsentDebugSettings.Builder debugSettingsBuilder = new ConsentDebugSettings.Builder(activity); 920 | 921 | if (data.containsKey("debug_geography")) { 922 | debugSettingsBuilder.setDebugGeography((int) data.get("debug_geography")); 923 | } 924 | 925 | debugSettingsBuilder.addTestDeviceHashedId(getAdMobDeviceId()); 926 | 927 | builder.setConsentDebugSettings(debugSettingsBuilder.build()); 928 | } 929 | 930 | return builder.build(); 931 | } 932 | 933 | private RequestConfiguration.PublisherPrivacyPersonalizationState getPublisherPrivacyPersonalizationState(int intValue) { 934 | return switch (intValue) { 935 | case 1 -> RequestConfiguration.PublisherPrivacyPersonalizationState.ENABLED; 936 | case 2 -> RequestConfiguration.PublisherPrivacyPersonalizationState.DISABLED; 937 | default -> RequestConfiguration.PublisherPrivacyPersonalizationState.DEFAULT; 938 | }; 939 | } 940 | 941 | /** 942 | * Generate MD5 for the deviceID 943 | * 944 | * @param s The string for which to generate the MD5 945 | * @return String The generated MD5 946 | */ 947 | private static String md5(final String s) { 948 | try { 949 | // Create MD5 Hash 950 | MessageDigest digest = MessageDigest.getInstance("MD5"); 951 | digest.update(s.getBytes()); 952 | byte[] messageDigest = digest.digest(); 953 | 954 | // Create Hex String 955 | StringBuilder hexString = new StringBuilder(); 956 | for (byte b : messageDigest) { 957 | StringBuilder h = new StringBuilder(Integer.toHexString(0xFF & b)); 958 | while (h.length() < 2) 959 | h.insert(0, "0"); 960 | hexString.append(h); 961 | } 962 | return hexString.toString(); 963 | } catch (NoSuchAlgorithmException e) { 964 | Log.e(LOG_TAG, "md5() - no such algorithm"); 965 | } 966 | return ""; 967 | } 968 | 969 | /** 970 | * Get the Device ID for AdMob 971 | * 972 | * @return String Device ID 973 | */ 974 | private String getAdMobDeviceId() { 975 | @SuppressLint("HardwareIds") String androidId = Settings.Secure.getString(activity.getContentResolver(), Settings.Secure.ANDROID_ID); 976 | return md5(androidId).toUpperCase(Locale.US); 977 | } 978 | } 979 | -------------------------------------------------------------------------------- /admob/src/main/java/org/godotengine/plugin/android/admob/Banner.java: -------------------------------------------------------------------------------- 1 | // 2 | // © 2024-present https://github.com/cengiz-pz 3 | // 4 | 5 | package org.godotengine.plugin.android.admob; 6 | 7 | import android.app.Activity; 8 | import android.graphics.Color; 9 | import android.os.Build; 10 | import android.util.DisplayMetrics; 11 | import android.util.Log; 12 | import android.view.Display; 13 | import android.view.Gravity; 14 | import android.view.View; 15 | import android.widget.FrameLayout; 16 | 17 | import androidx.annotation.NonNull; 18 | 19 | import com.google.android.gms.ads.AdListener; 20 | import com.google.android.gms.ads.AdRequest; 21 | import com.google.android.gms.ads.AdSize; 22 | import com.google.android.gms.ads.AdView; 23 | import com.google.android.gms.ads.LoadAdError; 24 | 25 | import org.godotengine.godot.Dictionary; 26 | 27 | interface BannerListener { 28 | void onAdLoaded(String adId); 29 | void onAdRefreshed(String adId); 30 | void onAdFailedToLoad(String adId, LoadAdError loadAdError); 31 | void onAdImpression(String adId); 32 | void onAdClicked(String adId); 33 | void onAdOpened(String adId); 34 | void onAdClosed(String adId); 35 | } 36 | 37 | 38 | public class Banner { 39 | private static final String CLASS_NAME = Banner.class.getSimpleName(); 40 | private static final String LOG_TAG = "godot::" + AdmobPlugin.CLASS_NAME + "::" + CLASS_NAME; 41 | 42 | private static final String AD_SIZE_PROPERTY = "ad_size"; 43 | private static final String AD_POSITION_PROPERTY = "ad_position"; 44 | 45 | enum BannerSize { 46 | BANNER, 47 | LARGE_BANNER, 48 | MEDIUM_RECTANGLE, 49 | FULL_BANNER, 50 | LEADERBOARD, 51 | SKYSCRAPER, 52 | FLUID 53 | } 54 | 55 | enum AdPosition { 56 | TOP, 57 | BOTTOM, 58 | LEFT, 59 | RIGHT, 60 | TOP_LEFT, 61 | TOP_RIGHT, 62 | BOTTOM_LEFT, 63 | BOTTOM_RIGHT, 64 | CENTER 65 | } 66 | 67 | private final String adId; 68 | private final String adUnitId; 69 | private final AdRequest adRequest; 70 | private final Activity activity; 71 | private final FrameLayout layout; 72 | private final BannerSize bannerSize; 73 | private AdPosition adPosition; 74 | private AdView adView; // Banner view 75 | private FrameLayout.LayoutParams adParams; 76 | private AdListener adListener; 77 | 78 | private boolean firstLoad; 79 | 80 | 81 | Banner(final String adId, final String adUnitId, final Dictionary adData, final AdRequest adRequest, 82 | final Activity activity, final FrameLayout layout, BannerListener listener) { 83 | this.adId = adId; 84 | this.adUnitId = adUnitId; 85 | 86 | if (adData.containsKey(AD_SIZE_PROPERTY)) { 87 | this.bannerSize = BannerSize.valueOf((String) adData.get(AD_SIZE_PROPERTY)); 88 | } 89 | else { 90 | this.bannerSize = BannerSize.BANNER; 91 | Log.e(LOG_TAG, "Error: Banner size is required!"); 92 | } 93 | 94 | if (adData.containsKey(AD_POSITION_PROPERTY)) { 95 | this.adPosition = AdPosition.valueOf((String) adData.get(AD_POSITION_PROPERTY)); 96 | } 97 | else { 98 | this.adPosition = AdPosition.TOP; 99 | Log.w(LOG_TAG, "Warning: Ad position not specified."); 100 | } 101 | 102 | this.adRequest = adRequest; 103 | this.activity = activity; 104 | this.layout = layout; 105 | 106 | firstLoad = true; 107 | 108 | this.adListener = new AdListener() { 109 | @Override 110 | public void onAdLoaded() { 111 | if (Banner.this.firstLoad) { 112 | Banner.this.firstLoad = false; 113 | listener.onAdLoaded(Banner.this.adId); 114 | } 115 | else { 116 | listener.onAdRefreshed(Banner.this.adId); 117 | } 118 | } 119 | 120 | @Override 121 | public void onAdFailedToLoad(@NonNull LoadAdError error) { 122 | listener.onAdFailedToLoad(Banner.this.adId, error); 123 | } 124 | 125 | public void onAdImpression() { 126 | listener.onAdImpression(Banner.this.adId); 127 | } 128 | 129 | public void onAdClicked() { 130 | listener.onAdClicked(Banner.this.adId); 131 | } 132 | 133 | public void onAdOpened() { 134 | listener.onAdOpened(Banner.this.adId); 135 | } 136 | 137 | public void onAdClosed() { 138 | listener.onAdClosed(Banner.this.adId); 139 | } 140 | }; 141 | 142 | this.adView = null; 143 | this.adParams = null; 144 | } 145 | 146 | void load() { 147 | addBanner(getGravity(adPosition), getAdSize(bannerSize)); 148 | } 149 | 150 | void show() { 151 | if (adView == null) { 152 | Log.w(LOG_TAG, "show(): Warning: banner ad not loaded."); 153 | } 154 | else if (adView.getVisibility() == View.VISIBLE) { 155 | Log.w(LOG_TAG, "show(): Warning: banner ad already visible."); 156 | } 157 | else { 158 | Log.d(LOG_TAG, String.format("show(): %s", this.adId)); 159 | adView.setVisibility(View.VISIBLE); 160 | adView.resume(); 161 | 162 | // Add to layout and load ad 163 | layout.addView(adView, adParams); 164 | } 165 | } 166 | 167 | void move(final String position) { 168 | if (layout == null || adView == null || adParams == null) { 169 | Log.w(LOG_TAG, "move(): Warning: banner ad not loaded."); 170 | } 171 | else { 172 | Log.d(LOG_TAG, "banner ad moved"); 173 | 174 | layout.removeView(adView); // Remove the old view 175 | 176 | adPosition = AdPosition.valueOf(position); 177 | addBanner(getGravity(adPosition), adView.getAdSize()); 178 | 179 | // Add to layout and load ad 180 | layout.addView(adView, adParams); 181 | } 182 | } 183 | 184 | void resize() { 185 | if (layout == null || adView == null || adParams == null) { 186 | Log.w(LOG_TAG, "move(): Warning: banner ad not loaded."); 187 | } 188 | else { 189 | Log.d(LOG_TAG, String.format("resize(): %s", this.adId)); 190 | 191 | layout.removeView(adView); // Remove the old view 192 | 193 | addBanner(adParams.gravity, getAdSize(bannerSize)); 194 | 195 | // Add to layout and load ad 196 | layout.addView(adView, adParams); 197 | } 198 | } 199 | 200 | private void addBanner(final int gravity, final AdSize size) { 201 | adParams = new FrameLayout.LayoutParams( 202 | FrameLayout.LayoutParams.WRAP_CONTENT, 203 | FrameLayout.LayoutParams.WRAP_CONTENT 204 | ); 205 | adParams.gravity = gravity; 206 | 207 | // Create new view & set old params 208 | adView = new AdView(activity); 209 | adView.setAdUnitId(adUnitId); 210 | adView.setBackgroundColor(Color.TRANSPARENT); 211 | adView.setAdSize(size); 212 | adView.setAdListener(adListener); 213 | adView.setVisibility(View.GONE); 214 | adView.pause(); 215 | 216 | // Request 217 | adView.loadAd(adRequest); 218 | } 219 | 220 | public void remove() { 221 | if (adView == null) { 222 | Log.w(LOG_TAG, "remove(): Warning: adView is null."); 223 | } 224 | else { 225 | layout.removeView(adView); 226 | } 227 | } 228 | 229 | public void hide() { 230 | if (adView.getVisibility() != View.GONE) { 231 | adView.setVisibility(View.GONE); 232 | adView.pause(); 233 | layout.removeView(adView); 234 | } 235 | else { 236 | Log.e(LOG_TAG, "Error: can't hide banner ad. Ad is not visible."); 237 | } 238 | } 239 | 240 | static int getAdWidth(Activity activity) { 241 | DisplayMetrics outMetrics = activity.getApplicationContext().getResources().getDisplayMetrics(); 242 | return Math.round((float) outMetrics.widthPixels / outMetrics.density); 243 | } 244 | 245 | private AdSize getAdSize(final BannerSize bannerSize) { 246 | AdSize result; 247 | Log.d(LOG_TAG, String.format("getAdSize(): for value '%s'.", bannerSize.name())); 248 | result = switch (bannerSize) { 249 | case BANNER -> AdSize.BANNER; 250 | case LARGE_BANNER -> AdSize.LARGE_BANNER; 251 | case MEDIUM_RECTANGLE -> AdSize.MEDIUM_RECTANGLE; 252 | case FULL_BANNER -> AdSize.FULL_BANNER; 253 | case LEADERBOARD -> AdSize.LEADERBOARD; 254 | case SKYSCRAPER -> AdSize.WIDE_SKYSCRAPER; 255 | case FLUID -> AdSize.FLUID; 256 | default -> AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(activity, getAdWidth(activity)); 257 | }; 258 | Log.d(LOG_TAG, String.format("getAdSize(): ad size [width: %d; height: %d].", result.getWidth(), result.getHeight())); 259 | return result; 260 | } 261 | 262 | private int getGravity(final AdPosition position) { 263 | int result; 264 | Log.d(LOG_TAG, String.format("getGravity(): for value '%s'.", position.name())); 265 | result = switch (position) { 266 | case TOP -> Gravity.TOP | Gravity.CENTER_HORIZONTAL; 267 | case BOTTOM -> Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; 268 | case LEFT -> Gravity.START | Gravity.CENTER_VERTICAL; 269 | case RIGHT -> Gravity.END | Gravity.CENTER_VERTICAL; 270 | case TOP_LEFT -> Gravity.TOP | Gravity.START; 271 | case TOP_RIGHT -> Gravity.TOP | Gravity.END; 272 | case BOTTOM_LEFT -> Gravity.BOTTOM | Gravity.START; 273 | case BOTTOM_RIGHT -> Gravity.BOTTOM | Gravity.END; 274 | case CENTER -> Gravity.CENTER; 275 | }; 276 | Log.d(LOG_TAG, String.format("getGravity(): result = %d.", result)); 277 | return result; 278 | } 279 | 280 | public int getWidth() { 281 | return getAdSize(bannerSize).getWidth(); 282 | } 283 | 284 | public int getHeight() { 285 | return getAdSize(bannerSize).getHeight(); 286 | } 287 | 288 | public int getWidthInPixels() { 289 | return getAdSize(bannerSize).getWidthInPixels(activity); 290 | } 291 | 292 | public int getHeightInPixels() { 293 | return getAdSize(bannerSize).getHeightInPixels(activity); 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /admob/src/main/java/org/godotengine/plugin/android/admob/Interstitial.java: -------------------------------------------------------------------------------- 1 | // 2 | // © 2024-present https://github.com/cengiz-pz 3 | // 4 | 5 | package org.godotengine.plugin.android.admob; 6 | 7 | import android.app.Activity; 8 | import android.util.Log; 9 | 10 | import androidx.annotation.NonNull; 11 | 12 | import com.google.android.gms.ads.AdError; 13 | import com.google.android.gms.ads.AdRequest; 14 | import com.google.android.gms.ads.FullScreenContentCallback; 15 | import com.google.android.gms.ads.LoadAdError; 16 | import com.google.android.gms.ads.interstitial.InterstitialAd; 17 | import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback; 18 | 19 | interface InterstitialListener { 20 | void onInterstitialLoaded(String adId); 21 | void onInterstitialReloaded(String adId); 22 | void onInterstitialFailedToLoad(String adId, LoadAdError loadAdError); 23 | void onInterstitialFailedToShow(String adId, AdError adError); 24 | void onInterstitialOpened(String adId); 25 | void onInterstitialClosed(String adId); 26 | void onInterstitialClicked(String adId); 27 | void onInterstitialImpression(String adId); 28 | } 29 | 30 | public class Interstitial { 31 | private static final String CLASS_NAME = Interstitial.class.getSimpleName(); 32 | private static final String LOG_TAG = "godot::" + AdmobPlugin.CLASS_NAME + "::" + CLASS_NAME; 33 | 34 | private final String adId; 35 | private final String adUnitId; 36 | private final AdRequest adRequest; 37 | private final Activity activity; 38 | private final InterstitialListener listener; 39 | 40 | private InterstitialAd interstitialAd = null; 41 | 42 | boolean firstLoad; 43 | 44 | Interstitial(final String adId, final String adUnitId, final AdRequest adRequest, final Activity activity, 45 | final InterstitialListener listener) { 46 | this.adId = adId; 47 | this.adUnitId = adUnitId; 48 | this.adRequest = adRequest; 49 | this.activity = activity; 50 | this.listener = listener; 51 | this.firstLoad = true; 52 | } 53 | 54 | void show() { 55 | if (interstitialAd != null) { 56 | interstitialAd.show(activity); 57 | } 58 | else { 59 | Log.w(LOG_TAG, "show(): interstitial not loaded"); 60 | } 61 | } 62 | 63 | private void setAd(InterstitialAd interstitialAd) { 64 | if (interstitialAd == this.interstitialAd) { 65 | Log.w(LOG_TAG, "setAd(): interstitial already set"); 66 | } 67 | else { 68 | // Avoid memory leaks 69 | if (this.interstitialAd != null) { 70 | this.interstitialAd.setFullScreenContentCallback(null); 71 | this.interstitialAd.setOnPaidEventListener(null); 72 | } 73 | 74 | if (interstitialAd != null) { 75 | interstitialAd.setFullScreenContentCallback(new FullScreenContentCallback() { 76 | @Override 77 | public void onAdClicked() { 78 | super.onAdClicked(); 79 | Log.i(LOG_TAG, "interstitial ad clicked"); 80 | listener.onInterstitialClicked(Interstitial.this.adId); 81 | } 82 | 83 | @Override 84 | public void onAdDismissedFullScreenContent() { 85 | super.onAdDismissedFullScreenContent(); 86 | setAd(null); 87 | Log.w(LOG_TAG, "interstitial ad dismissed full screen content"); 88 | listener.onInterstitialClosed(Interstitial.this.adId); 89 | load(); 90 | } 91 | 92 | @Override 93 | public void onAdFailedToShowFullScreenContent(@NonNull AdError adError) { 94 | super.onAdFailedToShowFullScreenContent(adError); 95 | Log.e(LOG_TAG, "interstitial ad failed to show full screen content"); 96 | listener.onInterstitialFailedToShow(Interstitial.this.adId, adError); 97 | } 98 | 99 | @Override 100 | public void onAdShowedFullScreenContent() { 101 | super.onAdShowedFullScreenContent(); 102 | Log.i(LOG_TAG, "interstitial ad showed full screen content"); 103 | listener.onInterstitialOpened(Interstitial.this.adId); 104 | } 105 | 106 | @Override 107 | public void onAdImpression() { 108 | super.onAdImpression(); 109 | Log.i(LOG_TAG, "interstitial ad impression"); 110 | listener.onInterstitialImpression(Interstitial.this.adId); 111 | } 112 | }); 113 | } 114 | 115 | this.interstitialAd = interstitialAd; 116 | } 117 | } 118 | 119 | void load() { 120 | InterstitialAd.load(activity, adUnitId, adRequest, new InterstitialAdLoadCallback() { 121 | @Override 122 | public void onAdLoaded(@NonNull InterstitialAd interstitialAd) { 123 | super.onAdLoaded(interstitialAd); 124 | setAd(interstitialAd); 125 | if (firstLoad) { 126 | Log.i(LOG_TAG, "interstitial ad loaded"); 127 | firstLoad = false; 128 | listener.onInterstitialLoaded(Interstitial.this.adId); 129 | } 130 | else { 131 | Log.i(LOG_TAG, "interstitial ad refreshed"); 132 | listener.onInterstitialReloaded(Interstitial.this.adId); 133 | } 134 | } 135 | 136 | @Override 137 | public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) { 138 | super.onAdFailedToLoad(loadAdError); 139 | setAd(null); // safety 140 | Log.e(LOG_TAG, "interstitial ad failed to load - error code: " + loadAdError.getCode()); 141 | listener.onInterstitialFailedToLoad(Interstitial.this.adId, loadAdError); 142 | } 143 | }); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /admob/src/main/java/org/godotengine/plugin/android/admob/RewardedInterstitial.java: -------------------------------------------------------------------------------- 1 | // 2 | // © 2024-present https://github.com/cengiz-pz 3 | // 4 | 5 | package org.godotengine.plugin.android.admob; 6 | 7 | import android.app.Activity; 8 | import android.util.Log; 9 | 10 | import androidx.annotation.NonNull; 11 | 12 | import com.google.android.gms.ads.AdError; 13 | import com.google.android.gms.ads.AdRequest; 14 | import com.google.android.gms.ads.FullScreenContentCallback; 15 | import com.google.android.gms.ads.LoadAdError; 16 | import com.google.android.gms.ads.rewarded.RewardItem; 17 | import com.google.android.gms.ads.rewarded.ServerSideVerificationOptions; 18 | import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAd; 19 | import com.google.android.gms.ads.rewardedinterstitial.RewardedInterstitialAdLoadCallback; 20 | 21 | interface RewardedInterstitialListener { 22 | void onRewardedInterstitialLoaded(String adId); 23 | void onRewardedInterstitialFailedToLoad(String adId, LoadAdError loadAdError); 24 | void onRewardedInterstitialOpened(String adId); 25 | void onRewardedInterstitialFailedToShow(String adId, AdError adError); 26 | void onRewardedInterstitialClosed(String adId); 27 | void onRewardedClicked(String adId); 28 | void onRewardedAdImpression(String adId); 29 | void onRewarded(String adId, RewardItem reward); 30 | } 31 | 32 | public class RewardedInterstitial { 33 | private static final String CLASS_NAME = RewardedInterstitial.class.getSimpleName(); 34 | private static final String LOG_TAG = "godot::" + AdmobPlugin.CLASS_NAME + "::" + CLASS_NAME; 35 | 36 | private final String adId; 37 | private final String adUnitId; 38 | private final AdRequest adRequest; 39 | private final Activity activity; 40 | private final RewardedInterstitialListener listener; 41 | 42 | private RewardedInterstitialAd rewardedAd; 43 | private ServerSideVerificationOptions serverSideVerificationOptions; 44 | 45 | RewardedInterstitial(final String adId, final String adUnitId, final AdRequest adRequest, Activity activity, 46 | final RewardedInterstitialListener listener) { 47 | this.adId = adId; 48 | this.adUnitId = adUnitId; 49 | this.adRequest = adRequest; 50 | this.activity = activity; 51 | this.listener = listener; 52 | this.rewardedAd = null; 53 | this.serverSideVerificationOptions = null; 54 | } 55 | 56 | void load() { 57 | RewardedInterstitialAd.load(activity, adUnitId, adRequest, new RewardedInterstitialAdLoadCallback() { 58 | @Override 59 | public void onAdLoaded(@NonNull RewardedInterstitialAd rewardedAd) { 60 | super.onAdLoaded(rewardedAd); 61 | setAd(rewardedAd); 62 | Log.i(LOG_TAG, "rewarded interstitial ad loaded"); 63 | listener.onRewardedInterstitialLoaded(RewardedInterstitial.this.adId); 64 | } 65 | 66 | @Override 67 | public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) { 68 | super.onAdFailedToLoad(loadAdError); 69 | 70 | setAd(null); // safety 71 | Log.e(LOG_TAG, "rewarded interstitial ad failed to load. errorCode: " + loadAdError.getCode()); 72 | listener.onRewardedInterstitialFailedToLoad(RewardedInterstitial.this.adId, loadAdError); 73 | } 74 | }); 75 | } 76 | 77 | void show() { 78 | if (rewardedAd != null) { 79 | rewardedAd.show(activity, rewardItem -> { 80 | Log.i(LOG_TAG, String.format("rewarded interstitial ad rewarded! currency: %s amount: %d", rewardItem.getType(), rewardItem.getAmount())); 81 | listener.onRewarded(RewardedInterstitial.this.adId, rewardItem); 82 | }); 83 | } 84 | } 85 | 86 | private void setAd(RewardedInterstitialAd rewardedAd) { 87 | if (rewardedAd == this.rewardedAd) { 88 | Log.w(LOG_TAG, "setAd(): rewarded interstitial already set"); 89 | } 90 | else { 91 | // Avoid memory leaks. 92 | if (this.rewardedAd != null) 93 | this.rewardedAd.setFullScreenContentCallback(null); 94 | 95 | if (rewardedAd != null) { 96 | rewardedAd.setFullScreenContentCallback(new FullScreenContentCallback() { 97 | @Override 98 | public void onAdClicked() { 99 | super.onAdClicked(); 100 | Log.i(LOG_TAG, "rewarded interstitial ad clicked"); 101 | listener.onRewardedClicked(RewardedInterstitial.this.adId); 102 | } 103 | 104 | @Override 105 | public void onAdDismissedFullScreenContent() { 106 | super.onAdDismissedFullScreenContent(); 107 | Log.w(LOG_TAG, "rewarded interstitial ad dismissed full screen content"); 108 | listener.onRewardedInterstitialClosed(RewardedInterstitial.this.adId); 109 | } 110 | 111 | @Override 112 | public void onAdFailedToShowFullScreenContent(@NonNull AdError adError) { 113 | super.onAdFailedToShowFullScreenContent(adError); 114 | Log.e(LOG_TAG, "rewarded interstitial ad failed to show full screen content"); 115 | listener.onRewardedInterstitialFailedToShow(RewardedInterstitial.this.adId, adError); 116 | } 117 | 118 | @Override 119 | public void onAdImpression() { 120 | super.onAdImpression(); 121 | Log.i(LOG_TAG, "rewarded interstitial ad impression"); 122 | listener.onRewardedAdImpression(RewardedInterstitial.this.adId); 123 | } 124 | 125 | @Override 126 | public void onAdShowedFullScreenContent() { 127 | super.onAdShowedFullScreenContent(); 128 | Log.i(LOG_TAG, "rewarded interstitial ad showed full screen content"); 129 | listener.onRewardedInterstitialOpened(RewardedInterstitial.this.adId); 130 | } 131 | }); 132 | } 133 | 134 | if (serverSideVerificationOptions != null) { 135 | rewardedAd.setServerSideVerificationOptions(serverSideVerificationOptions); 136 | } 137 | 138 | this.rewardedAd = rewardedAd; 139 | } 140 | } 141 | 142 | void setServerSideVerificationOptions(ServerSideVerificationOptions ssvo) { 143 | this.serverSideVerificationOptions = ssvo; 144 | 145 | if (rewardedAd == null) { 146 | Log.w(LOG_TAG, "setServerSideVerificationOptions(): Ad is null. SSVO will be set when ad is loaded."); 147 | } 148 | else { 149 | rewardedAd.setServerSideVerificationOptions(ssvo); 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /admob/src/main/java/org/godotengine/plugin/android/admob/RewardedVideo.java: -------------------------------------------------------------------------------- 1 | // 2 | // © 2024-present https://github.com/cengiz-pz 3 | // 4 | 5 | package org.godotengine.plugin.android.admob; 6 | 7 | import android.app.Activity; 8 | import android.util.Log; 9 | 10 | import androidx.annotation.NonNull; 11 | 12 | import com.google.android.gms.ads.AdError; 13 | import com.google.android.gms.ads.AdRequest; 14 | import com.google.android.gms.ads.FullScreenContentCallback; 15 | import com.google.android.gms.ads.LoadAdError; 16 | import com.google.android.gms.ads.rewarded.RewardItem; 17 | import com.google.android.gms.ads.rewarded.RewardedAd; 18 | import com.google.android.gms.ads.rewarded.RewardedAdLoadCallback; 19 | import com.google.android.gms.ads.rewarded.ServerSideVerificationOptions; 20 | 21 | interface RewardedVideoListener { 22 | void onRewardedVideoLoaded(String adId); 23 | void onRewardedVideoFailedToLoad(String adId, LoadAdError loadAdError); 24 | void onRewardedVideoOpened(String adId); 25 | void onRewardedVideoFailedToShow(String adId, AdError adError); 26 | void onRewardedVideoClosed(String adId); 27 | void onRewardedClicked(String adId); 28 | void onRewardedAdImpression(String adId); 29 | void onRewarded(String adId, RewardItem reward); 30 | } 31 | 32 | public class RewardedVideo { 33 | private static final String CLASS_NAME = RewardedVideo.class.getSimpleName(); 34 | private static final String LOG_TAG = "godot::" + AdmobPlugin.CLASS_NAME + "::" + CLASS_NAME; 35 | 36 | private final String adId; 37 | private final String adUnitId; 38 | private final AdRequest adRequest; 39 | private final Activity activity; 40 | private final RewardedVideoListener listener; 41 | 42 | private RewardedAd rewardedAd; 43 | private ServerSideVerificationOptions serverSideVerificationOptions; 44 | 45 | RewardedVideo(final String adId, final String adUnitId, final AdRequest adRequest, Activity activity, 46 | final RewardedVideoListener listener) { 47 | this.adId = adId; 48 | this.adUnitId = adUnitId; 49 | this.adRequest = adRequest; 50 | this.activity = activity; 51 | this.listener = listener; 52 | this.rewardedAd = null; 53 | this.serverSideVerificationOptions = null; 54 | } 55 | 56 | void load() { 57 | RewardedAd.load(activity, adUnitId, adRequest, new RewardedAdLoadCallback() { 58 | @Override 59 | public void onAdLoaded(@NonNull RewardedAd rewardedAd) { 60 | super.onAdLoaded(rewardedAd); 61 | setAd(rewardedAd); 62 | Log.i(LOG_TAG, "rewarded video ad loaded"); 63 | listener.onRewardedVideoLoaded(RewardedVideo.this.adId); 64 | } 65 | 66 | @Override 67 | public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) { 68 | super.onAdFailedToLoad(loadAdError); 69 | // safety 70 | setAd(null); 71 | Log.e(LOG_TAG, "rewarded video ad failed to load. errorCode: " + loadAdError.getCode()); 72 | listener.onRewardedVideoFailedToLoad(RewardedVideo.this.adId, loadAdError); 73 | } 74 | }); 75 | } 76 | 77 | void show() { 78 | if (rewardedAd != null) { 79 | rewardedAd.show(activity, rewardItem -> { 80 | Log.i(LOG_TAG, String.format("rewarded video ad reward received! currency: %s amount: %d", rewardItem.getType(), rewardItem.getAmount())); 81 | listener.onRewarded(RewardedVideo.this.adId, rewardItem); 82 | }); 83 | } 84 | } 85 | 86 | private void setAd(RewardedAd rewardedAd) { 87 | if (rewardedAd == this.rewardedAd) { 88 | Log.w(LOG_TAG, "setAd(): rewarded already set"); 89 | } 90 | else { 91 | // Avoid memory leaks. 92 | if (this.rewardedAd != null) 93 | this.rewardedAd.setFullScreenContentCallback(null); 94 | 95 | if (rewardedAd != null) { 96 | rewardedAd.setFullScreenContentCallback(new FullScreenContentCallback() { 97 | @Override 98 | public void onAdClicked() { 99 | super.onAdClicked(); 100 | Log.i(LOG_TAG, "rewarded video ad clicked"); 101 | listener.onRewardedClicked(RewardedVideo.this.adId); 102 | } 103 | 104 | @Override 105 | public void onAdDismissedFullScreenContent() { 106 | super.onAdDismissedFullScreenContent(); 107 | Log.w(LOG_TAG, "rewarded video ad dismissed full screen content"); 108 | listener.onRewardedVideoClosed(RewardedVideo.this.adId); 109 | } 110 | 111 | @Override 112 | public void onAdFailedToShowFullScreenContent(@NonNull AdError adError) { 113 | super.onAdFailedToShowFullScreenContent(adError); 114 | Log.e(LOG_TAG, "rewarded video ad failed to show full screen content"); 115 | listener.onRewardedVideoFailedToShow(RewardedVideo.this.adId, adError); 116 | } 117 | 118 | @Override 119 | public void onAdImpression() { 120 | super.onAdImpression(); 121 | Log.i(LOG_TAG, "rewarded video ad impression"); 122 | listener.onRewardedAdImpression(RewardedVideo.this.adId); 123 | } 124 | 125 | @Override 126 | public void onAdShowedFullScreenContent() { 127 | super.onAdShowedFullScreenContent(); 128 | Log.i(LOG_TAG, "rewarded video ad showed full screen content"); 129 | listener.onRewardedVideoOpened(RewardedVideo.this.adId); 130 | } 131 | }); 132 | } 133 | 134 | if (serverSideVerificationOptions != null) { 135 | rewardedAd.setServerSideVerificationOptions(serverSideVerificationOptions); 136 | } 137 | 138 | this.rewardedAd = rewardedAd; 139 | } 140 | } 141 | 142 | void setServerSideVerificationOptions(ServerSideVerificationOptions ssvo) { 143 | this.serverSideVerificationOptions = ssvo; 144 | 145 | if (rewardedAd == null) { 146 | Log.w(LOG_TAG, "setServerSideVerificationOptions(): Ad is null. SSVO will be set when ad is loaded."); 147 | } 148 | else { 149 | rewardedAd.setServerSideVerificationOptions(ssvo); 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | // 2 | // © 2024-present https://github.com/cengiz-pz 3 | // 4 | 5 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 6 | plugins { 7 | id("com.android.library") version "8.4.0" apply false 8 | } 9 | 10 | val cleanBuildDir by tasks.registering(Delete::class) { 11 | delete(rootProject.buildDir) 12 | } 13 | 14 | allprojects { 15 | tasks.withType { 16 | options.compilerArgs.add("-Xlint:unchecked") 17 | options.compilerArgs.add("-Xlint:deprecation") 18 | } 19 | } -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | # Godot 4+ specific ignores 2 | .godot/ 3 | 4 | # Godot-specific ignores 5 | .import/ 6 | export.cfg 7 | export_presets.cfg 8 | 9 | # Dummy HTML5 export presets file for continuous integration 10 | !.github/dist/export_presets.cfg 11 | 12 | # Android builds 13 | android/ 14 | 15 | # Imported translations (automatically generated from CSV files) 16 | *.translation 17 | 18 | # Mono-specific ignores 19 | .mono/ 20 | data_*/ 21 | mono_crash.*.json 22 | 23 | # System/tool-specific ignores 24 | .directory 25 | .DS_Store 26 | *~ 27 | *.blend1 28 | 29 | # Logs 30 | *.log 31 | -------------------------------------------------------------------------------- /demo/Main.gd: -------------------------------------------------------------------------------- 1 | # 2 | # © 2024-present https://github.com/cengiz-pz 3 | # 4 | 5 | extends Node 6 | 7 | @onready var admob: Admob = $Admob as Admob 8 | @onready var show_banner_button: Button = $CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/ButtonsHBoxContainer/ShowBannerButton 9 | @onready var hide_banner_button: Button = $CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/ButtonsHBoxContainer/HideBannerButton 10 | @onready var reload_banner_button: Button = $CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/ButtonsHBoxContainer/ReloadBannerButton 11 | @onready var banner_position_option_button: OptionButton = $CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/PositionHBoxContainer/OptionButton 12 | @onready var banner_size_option_button: OptionButton = $CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/SizeHBoxContainer/OptionButton 13 | @onready var interstitial_button: Button = $CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Other/InterstitialButton 14 | @onready var rewarded_button: Button = $CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Other/RewardedButton 15 | @onready var rewarded_interstitial_button: Button = $CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Other/RewardedInterstitialButton 16 | @onready var _label: RichTextLabel = $CanvasLayer/CenterContainer/VBoxContainer/RichTextLabel as RichTextLabel 17 | @onready var _geography_option_button: OptionButton = $CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/GeographyHBoxContainer/OptionButton 18 | 19 | var _is_banner_loaded: bool = false 20 | var _is_interstitial_loaded: bool = false 21 | var _is_rewarded_video_loaded: bool = false 22 | var _is_rewarded_interstitial_loaded: bool = false 23 | 24 | 25 | func _ready() -> void: 26 | admob.initialize() 27 | 28 | var __index: int = 0 29 | for __item: String in LoadAdRequest.AdPosition.keys(): 30 | banner_position_option_button.add_item(__item) 31 | if __item.casecmp_to(LoadAdRequest.AdPosition.keys()[admob.banner_position]) == 0: 32 | banner_position_option_button.select(__index) 33 | __index += 1 34 | 35 | __index = 0 36 | for __item in LoadAdRequest.AdSize.keys(): 37 | banner_size_option_button.add_item(__item) 38 | if __item.casecmp_to(LoadAdRequest.AdSize.keys()[admob.banner_size]) == 0: 39 | banner_size_option_button.select(__index) 40 | __index += 1 41 | 42 | 43 | func _on_admob_initialization_completed(_status_data: InitializationStatus) -> void: 44 | _process_consent_status(admob.get_consent_status()) 45 | admob.load_banner_ad() 46 | admob.load_interstitial_ad() 47 | admob.load_rewarded_ad() 48 | admob.load_rewarded_interstitial_ad() 49 | 50 | 51 | func _on_size_button_pressed() -> void: 52 | print(" ------- Get banner size button PRESSED") 53 | if _is_banner_loaded: 54 | _print_to_screen("Banner size: " + str(admob.get_banner_dimension())) 55 | 56 | 57 | func _on_pixel_size_button_pressed() -> void: 58 | print(" ------- Get banner pixel size button PRESSED") 59 | if _is_banner_loaded: 60 | _print_to_screen("Banner size in pixels: " + str(admob.get_banner_dimension_in_pixels())) 61 | 62 | 63 | func _on_show_banner_button_pressed() -> void: 64 | print(" ------- Show banner button PRESSED") 65 | if _is_banner_loaded: 66 | show_banner_button.disabled = true 67 | hide_banner_button.disabled = false 68 | admob.show_banner_ad() 69 | 70 | 71 | func _on_hide_banner_button_pressed() -> void: 72 | print(" ------- Hide banner button PRESSED") 73 | if _is_banner_loaded: 74 | show_banner_button.disabled = false 75 | hide_banner_button.disabled = true 76 | admob.hide_banner_ad() 77 | 78 | 79 | func _on_reload_banner_button_pressed() -> void: 80 | print(" ------- Reload banner button PRESSED") 81 | if _is_banner_loaded: 82 | _is_banner_loaded = false 83 | show_banner_button.disabled = true 84 | reload_banner_button.disabled = true 85 | admob.remove_banner_ad(admob._active_banner_ads[0]) 86 | admob.banner_position = LoadAdRequest.AdPosition[banner_position_option_button.get_item_text(banner_position_option_button.selected)] 87 | admob.banner_size = LoadAdRequest.AdSize[banner_size_option_button.get_item_text(banner_size_option_button.selected)] 88 | admob.load_banner_ad() 89 | 90 | 91 | func _on_interstitial_button_pressed() -> void: 92 | print(" ------- Interstitial button PRESSED") 93 | if _is_interstitial_loaded: 94 | _is_interstitial_loaded = false 95 | interstitial_button.disabled = true 96 | admob.show_interstitial_ad() 97 | else: 98 | admob.load_interstitial_ad() 99 | 100 | 101 | func _on_rewarded_video_button_pressed() -> void: 102 | print(" ------- Rewarded button PRESSED") 103 | if _is_rewarded_video_loaded: 104 | _is_rewarded_video_loaded = false 105 | rewarded_button.disabled = true 106 | admob.show_rewarded_ad() 107 | else: 108 | admob.load_rewarded_ad() 109 | 110 | 111 | func _on_rewarded_interstitial_button_pressed() -> void: 112 | print(" ------- Rewarded interstitial button PRESSED") 113 | if _is_rewarded_interstitial_loaded: 114 | _is_rewarded_interstitial_loaded = false 115 | rewarded_interstitial_button.disabled = true 116 | admob.show_rewarded_interstitial_ad() 117 | else: 118 | admob.load_rewarded_interstitial_ad() 119 | 120 | 121 | func _on_reset_consent_button_pressed() -> void: 122 | admob.reset_consent_info() 123 | 124 | 125 | func _on_admob_banner_ad_loaded(ad_id: String) -> void: 126 | _is_banner_loaded = true 127 | show_banner_button.disabled = false 128 | reload_banner_button.disabled = false 129 | _print_to_screen("banner loaded: %s" % ad_id) 130 | 131 | 132 | func _on_admob_banner_ad_refreshed(ad_id: String) -> void: 133 | _print_to_screen("banner refreshed: %s" % ad_id) 134 | 135 | 136 | func _on_admob_banner_ad_failed_to_load(ad_id: String, error_data: LoadAdError) -> void: 137 | _print_to_screen("banner %s failed to load. error: %d, message: %s" % 138 | [ad_id, error_data.get_code(), error_data.get_message()], true) 139 | 140 | 141 | func _on_admob_interstitial_ad_loaded(ad_id: String) -> void: 142 | _is_interstitial_loaded = true 143 | interstitial_button.disabled = false 144 | _print_to_screen("interstitial loaded: %s" % ad_id) 145 | 146 | 147 | func _on_admob_interstitial_ad_failed_to_load(ad_id: String, error_data: LoadAdError) -> void: 148 | _print_to_screen("interstitial %s failed to load. error: %d, message: %s" % 149 | [ad_id, error_data.get_code(), error_data.get_message()], true) 150 | 151 | 152 | func _on_admob_interstitial_ad_refreshed(ad_id: String) -> void: 153 | _print_to_screen("interstitial refreshed: %s" % ad_id) 154 | 155 | 156 | func _on_admob_interstitial_ad_dismissed_full_screen_content(ad_id: String) -> void: 157 | _print_to_screen("interstitial closed: %s" % ad_id) 158 | 159 | 160 | func _on_admob_rewarded_ad_loaded(ad_id: String) -> void: 161 | _is_rewarded_video_loaded = true 162 | rewarded_button.disabled = false 163 | _print_to_screen("rewarded video loaded: %s" % ad_id) 164 | 165 | 166 | func _on_admob_rewarded_ad_failed_to_load(ad_id: String, error_data: LoadAdError) -> void: 167 | _print_to_screen("rewarded video %s failed to load. error: %d, message: %s" % 168 | [ad_id, error_data.get_code(), error_data.get_message()], true) 169 | 170 | 171 | func _on_admob_rewarded_ad_user_earned_reward(ad_id: String, reward_data: RewardItem) -> void: 172 | _print_to_screen("user rewarded for rewarded ad '%s' with %d %s" % 173 | [ad_id, reward_data.get_amount(), reward_data.get_type()]) 174 | 175 | 176 | func _on_admob_rewarded_interstitial_ad_loaded(ad_id: String) -> void: 177 | _is_rewarded_interstitial_loaded = true 178 | rewarded_interstitial_button.disabled = false 179 | _print_to_screen("rewarded interstitial loaded: %s" % ad_id) 180 | 181 | 182 | func _on_admob_rewarded_interstitial_ad_failed_to_load(ad_id: String, error_data: LoadAdError) -> void: 183 | _print_to_screen("rewarded interstitial %s failed to load. error: %d, message: %s" % 184 | [ad_id, error_data.get_code(), error_data.get_message()], true) 185 | 186 | 187 | func _on_admob_rewarded_interstitial_ad_user_earned_reward(ad_id: String, reward_data: RewardItem) -> void: 188 | _print_to_screen("user rewarded for rewarded interstitial ad '%s' with %d %s" % 189 | [ad_id, reward_data.get_amount(), reward_data.get_type()]) 190 | 191 | 192 | func _on_admob_consent_info_updated() -> void: 193 | _print_to_screen("consent info updated") 194 | _process_consent_status(admob.get_consent_status()) 195 | 196 | 197 | func _on_admob_consent_info_update_failed(a_error_data: FormError) -> void: 198 | _print_to_screen("consent info failed to update: %s" % a_error_data.get_message()) 199 | 200 | 201 | func _process_consent_status(a_consent_status: int) -> void: 202 | _print_to_screen("_process_consent_status(): consent status = %d" % a_consent_status) 203 | match a_consent_status: 204 | ConsentInformation.ConsentStatus.UNKNOWN: 205 | _print_to_screen("consent status is unknown") 206 | admob.update_consent_info(ConsentRequestParameters.new() 207 | .set_debug_geography(ConsentRequestParameters.DebugGeography.values()[_geography_option_button.selected]) 208 | .add_test_device_hashed_id(OS.get_unique_id())) 209 | ConsentInformation.ConsentStatus.NOT_REQUIRED: 210 | _print_to_screen("consent is not required") 211 | ConsentInformation.ConsentStatus.REQUIRED: 212 | _print_to_screen("consent is required") 213 | admob.load_consent_form() 214 | ConsentInformation.ConsentStatus.OBTAINED: 215 | _print_to_screen("consent has already been obtained") 216 | 217 | 218 | func _on_admob_consent_form_loaded() -> void: 219 | _print_to_screen("consent form has been loaded") 220 | admob.show_consent_form() 221 | 222 | 223 | func _on_admob_consent_form_failed_to_load(a_error_data: FormError) -> void: 224 | _print_to_screen("consent form failed to load %s" % a_error_data.get_message()) 225 | 226 | 227 | func _on_admob_consent_form_dismissed(a_error_data: FormError) -> void: 228 | _print_to_screen("consent form has been dismissed %s" % a_error_data.get_message()) 229 | 230 | 231 | func _on_update_consent_info_button_pressed() -> void: 232 | print("selected consent geography: %d" % _geography_option_button.selected) 233 | admob.update_consent_info(ConsentRequestParameters.new() 234 | .set_debug_geography(ConsentRequestParameters.DebugGeography.values()[_geography_option_button.selected]) 235 | .add_test_device_hashed_id(OS.get_unique_id())) 236 | 237 | 238 | func _on_scene_2_button_pressed() -> void: 239 | get_tree().change_scene_to_file("res://scene/Scene2.tscn") 240 | 241 | 242 | func _print_to_screen(a_message: String, a_is_error: bool = false) -> void: 243 | _label.add_text("%s\n\n" % a_message) 244 | if a_is_error: 245 | printerr(a_message) 246 | else: 247 | print(a_message) 248 | -------------------------------------------------------------------------------- /demo/Main.gd.uid: -------------------------------------------------------------------------------- 1 | uid://bcqb5hwnjbtob 2 | -------------------------------------------------------------------------------- /demo/Main.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://daro5x6n3pd26"] 2 | 3 | [ext_resource type="Script" uid="uid://bcqb5hwnjbtob" path="res://Main.gd" id="1_wt50o"] 4 | [ext_resource type="Script" uid="uid://b8ayp8io5uvmn" path="res://addons/AdmobPlugin/Admob.gd" id="2_4x6et"] 5 | [ext_resource type="Texture2D" uid="uid://bdjwuj5g73wl3" path="res://admob.png" id="2_qti4s"] 6 | 7 | [node name="Main" type="Node"] 8 | script = ExtResource("1_wt50o") 9 | 10 | [node name="CanvasLayer" type="CanvasLayer" parent="."] 11 | 12 | [node name="CenterContainer" type="CenterContainer" parent="CanvasLayer"] 13 | anchors_preset = 15 14 | anchor_right = 1.0 15 | anchor_bottom = 1.0 16 | grow_horizontal = 2 17 | grow_vertical = 2 18 | size_flags_horizontal = 3 19 | size_flags_vertical = 3 20 | 21 | [node name="VBoxContainer" type="VBoxContainer" parent="CanvasLayer/CenterContainer"] 22 | custom_minimum_size = Vector2(400, 0) 23 | layout_mode = 2 24 | theme_override_constants/separation = 20 25 | 26 | [node name="TextureRect" type="TextureRect" parent="CanvasLayer/CenterContainer/VBoxContainer"] 27 | custom_minimum_size = Vector2(0, 128) 28 | layout_mode = 2 29 | texture = ExtResource("2_qti4s") 30 | expand_mode = 3 31 | stretch_mode = 5 32 | 33 | [node name="HBoxContainer" type="HBoxContainer" parent="CanvasLayer/CenterContainer/VBoxContainer"] 34 | layout_mode = 2 35 | 36 | [node name="Label" type="Label" parent="CanvasLayer/CenterContainer/VBoxContainer/HBoxContainer"] 37 | layout_mode = 2 38 | size_flags_horizontal = 3 39 | theme_override_font_sizes/font_size = 24 40 | text = "Admob Demo (Main Scene)" 41 | horizontal_alignment = 1 42 | 43 | [node name="Scene2Button" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/HBoxContainer"] 44 | layout_mode = 2 45 | text = "Switch" 46 | 47 | [node name="VBoxContainer" type="VBoxContainer" parent="CanvasLayer/CenterContainer/VBoxContainer"] 48 | layout_mode = 2 49 | theme_override_constants/separation = 11 50 | 51 | [node name="TabContainer" type="TabContainer" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer"] 52 | custom_minimum_size = Vector2(0, 165) 53 | layout_mode = 2 54 | current_tab = 0 55 | 56 | [node name="Banner" type="VBoxContainer" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer"] 57 | layout_mode = 2 58 | theme_override_constants/separation = 6 59 | metadata/_tab_index = 0 60 | 61 | [node name="PositionHBoxContainer" type="HBoxContainer" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner"] 62 | layout_mode = 2 63 | theme_override_constants/separation = 10 64 | 65 | [node name="Label" type="Label" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/PositionHBoxContainer"] 66 | layout_mode = 2 67 | text = "Banner Position:" 68 | 69 | [node name="OptionButton" type="OptionButton" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/PositionHBoxContainer"] 70 | layout_mode = 2 71 | size_flags_horizontal = 3 72 | 73 | [node name="SizeHBoxContainer" type="HBoxContainer" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner"] 74 | layout_mode = 2 75 | theme_override_constants/separation = 10 76 | 77 | [node name="Label" type="Label" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/SizeHBoxContainer"] 78 | layout_mode = 2 79 | text = "Banner Size:" 80 | 81 | [node name="OptionButton" type="OptionButton" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/SizeHBoxContainer"] 82 | layout_mode = 2 83 | size_flags_horizontal = 3 84 | 85 | [node name="SizeButtonHBoxContainer" type="HBoxContainer" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner"] 86 | layout_mode = 2 87 | theme_override_constants/separation = 30 88 | 89 | [node name="SizeButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/SizeButtonHBoxContainer"] 90 | layout_mode = 2 91 | size_flags_horizontal = 3 92 | text = "Get Size" 93 | 94 | [node name="PixelSizeButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/SizeButtonHBoxContainer"] 95 | layout_mode = 2 96 | size_flags_horizontal = 3 97 | text = "Get Size In Pixels" 98 | 99 | [node name="ButtonsHBoxContainer" type="HBoxContainer" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner"] 100 | layout_mode = 2 101 | theme_override_constants/separation = 45 102 | 103 | [node name="ShowBannerButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/ButtonsHBoxContainer"] 104 | custom_minimum_size = Vector2(100, 0) 105 | layout_mode = 2 106 | disabled = true 107 | text = "Show" 108 | 109 | [node name="HideBannerButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/ButtonsHBoxContainer"] 110 | custom_minimum_size = Vector2(100, 0) 111 | layout_mode = 2 112 | disabled = true 113 | text = "Hide" 114 | 115 | [node name="ReloadBannerButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/ButtonsHBoxContainer"] 116 | custom_minimum_size = Vector2(100, 0) 117 | layout_mode = 2 118 | disabled = true 119 | text = "Reload" 120 | 121 | [node name="Other" type="VBoxContainer" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer"] 122 | visible = false 123 | layout_mode = 2 124 | theme_override_constants/separation = 15 125 | metadata/_tab_index = 1 126 | 127 | [node name="InterstitialButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Other"] 128 | layout_mode = 2 129 | disabled = true 130 | text = "Show Interstitial Ad" 131 | 132 | [node name="RewardedButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Other"] 133 | layout_mode = 2 134 | disabled = true 135 | text = "Show Rewarded Ad" 136 | 137 | [node name="RewardedInterstitialButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Other"] 138 | layout_mode = 2 139 | disabled = true 140 | text = "Show Rewarded Interstitial Ad" 141 | 142 | [node name="GeographyHBoxContainer" type="HBoxContainer" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer"] 143 | layout_mode = 2 144 | 145 | [node name="Label" type="Label" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/GeographyHBoxContainer"] 146 | layout_mode = 2 147 | text = "Debug Geography: " 148 | 149 | [node name="OptionButton" type="OptionButton" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/GeographyHBoxContainer"] 150 | layout_mode = 2 151 | size_flags_horizontal = 3 152 | selected = 1 153 | item_count = 3 154 | popup/item_0/text = "DISABLED" 155 | popup/item_0/id = 0 156 | popup/item_1/text = "EEA" 157 | popup/item_1/id = 1 158 | popup/item_2/text = "NOT EEA" 159 | popup/item_2/id = 2 160 | 161 | [node name="HBoxContainer" type="HBoxContainer" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer"] 162 | layout_mode = 2 163 | theme_override_constants/separation = 10 164 | 165 | [node name="Label" type="Label" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/HBoxContainer"] 166 | layout_mode = 2 167 | text = "Consent Info: " 168 | 169 | [node name="UpdateConsentInfoButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/HBoxContainer"] 170 | layout_mode = 2 171 | size_flags_horizontal = 3 172 | text = "Update" 173 | 174 | [node name="ResetConsentButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/HBoxContainer"] 175 | layout_mode = 2 176 | size_flags_horizontal = 3 177 | text = "Reset" 178 | 179 | [node name="RichTextLabel" type="RichTextLabel" parent="CanvasLayer/CenterContainer/VBoxContainer"] 180 | custom_minimum_size = Vector2(0, 210) 181 | layout_mode = 2 182 | theme_override_font_sizes/normal_font_size = 14 183 | scroll_following = true 184 | 185 | [node name="Admob" type="Node" parent="."] 186 | script = ExtResource("2_4x6et") 187 | debug_application_id = "ca-app-pub-3940256099942544~3347511713" 188 | debug_banner_id = "ca-app-pub-3940256099942544/6300978111" 189 | debug_interstitial_id = "ca-app-pub-3940256099942544/1033173712" 190 | debug_rewarded_id = "ca-app-pub-3940256099942544/5224354917" 191 | debug_rewarded_interstitial_id = "ca-app-pub-3940256099942544/5354046379" 192 | 193 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/HBoxContainer/Scene2Button" to="." method="_on_scene_2_button_pressed"] 194 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/SizeButtonHBoxContainer/SizeButton" to="." method="_on_size_button_pressed"] 195 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/SizeButtonHBoxContainer/PixelSizeButton" to="." method="_on_pixel_size_button_pressed"] 196 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/ButtonsHBoxContainer/ShowBannerButton" to="." method="_on_show_banner_button_pressed"] 197 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/ButtonsHBoxContainer/HideBannerButton" to="." method="_on_hide_banner_button_pressed"] 198 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/ButtonsHBoxContainer/ReloadBannerButton" to="." method="_on_reload_banner_button_pressed"] 199 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Other/InterstitialButton" to="." method="_on_interstitial_button_pressed"] 200 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Other/RewardedButton" to="." method="_on_rewarded_video_button_pressed"] 201 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Other/RewardedInterstitialButton" to="." method="_on_rewarded_interstitial_button_pressed"] 202 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/HBoxContainer/UpdateConsentInfoButton" to="." method="_on_update_consent_info_button_pressed"] 203 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/HBoxContainer/ResetConsentButton" to="." method="_on_reset_consent_button_pressed"] 204 | [connection signal="banner_ad_failed_to_load" from="Admob" to="." method="_on_admob_banner_ad_failed_to_load"] 205 | [connection signal="banner_ad_loaded" from="Admob" to="." method="_on_admob_banner_ad_loaded"] 206 | [connection signal="banner_ad_refreshed" from="Admob" to="." method="_on_admob_banner_ad_refreshed"] 207 | [connection signal="consent_form_dismissed" from="Admob" to="." method="_on_admob_consent_form_dismissed"] 208 | [connection signal="consent_form_failed_to_load" from="Admob" to="." method="_on_admob_consent_form_failed_to_load"] 209 | [connection signal="consent_form_loaded" from="Admob" to="." method="_on_admob_consent_form_loaded"] 210 | [connection signal="consent_info_update_failed" from="Admob" to="." method="_on_admob_consent_info_update_failed"] 211 | [connection signal="consent_info_updated" from="Admob" to="." method="_on_admob_consent_info_updated"] 212 | [connection signal="initialization_completed" from="Admob" to="." method="_on_admob_initialization_completed"] 213 | [connection signal="interstitial_ad_dismissed_full_screen_content" from="Admob" to="." method="_on_admob_interstitial_ad_dismissed_full_screen_content"] 214 | [connection signal="interstitial_ad_failed_to_load" from="Admob" to="." method="_on_admob_interstitial_ad_failed_to_load"] 215 | [connection signal="interstitial_ad_loaded" from="Admob" to="." method="_on_admob_interstitial_ad_loaded"] 216 | [connection signal="interstitial_ad_refreshed" from="Admob" to="." method="_on_admob_interstitial_ad_refreshed"] 217 | [connection signal="rewarded_ad_failed_to_load" from="Admob" to="." method="_on_admob_rewarded_ad_failed_to_load"] 218 | [connection signal="rewarded_ad_loaded" from="Admob" to="." method="_on_admob_rewarded_ad_loaded"] 219 | [connection signal="rewarded_ad_user_earned_reward" from="Admob" to="." method="_on_admob_rewarded_ad_user_earned_reward"] 220 | [connection signal="rewarded_interstitial_ad_failed_to_load" from="Admob" to="." method="_on_admob_rewarded_interstitial_ad_failed_to_load"] 221 | [connection signal="rewarded_interstitial_ad_loaded" from="Admob" to="." method="_on_admob_rewarded_interstitial_ad_loaded"] 222 | [connection signal="rewarded_interstitial_ad_user_earned_reward" from="Admob" to="." method="_on_admob_rewarded_interstitial_ad_user_earned_reward"] 223 | -------------------------------------------------------------------------------- /demo/admob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cengiz-pz/godot-android-admob-plugin/febdd2292550c666577141f27f4a87f2f3bd5562/demo/admob.png -------------------------------------------------------------------------------- /demo/android/.build_version: -------------------------------------------------------------------------------- 1 | 4.4.1.stable 2 | -------------------------------------------------------------------------------- /demo/export_presets.cfg: -------------------------------------------------------------------------------- 1 | [preset.0] 2 | 3 | name="Android" 4 | platform="Android" 5 | runnable=true 6 | dedicated_server=false 7 | custom_features="" 8 | export_filter="all_resources" 9 | include_filter="" 10 | exclude_filter="" 11 | export_path="" 12 | encryption_include_filters="" 13 | encryption_exclude_filters="" 14 | encrypt_pck=false 15 | encrypt_directory=false 16 | 17 | [preset.0.options] 18 | 19 | custom_template/debug="" 20 | custom_template/release="" 21 | gradle_build/use_gradle_build=true 22 | gradle_build/export_format=0 23 | gradle_build/min_sdk="" 24 | gradle_build/target_sdk="" 25 | architectures/armeabi-v7a=false 26 | architectures/arm64-v8a=true 27 | architectures/x86=false 28 | architectures/x86_64=false 29 | version/code=1 30 | version/name="" 31 | package/unique_name="org.godotengine.admobdemo" 32 | package/name="" 33 | package/signed=true 34 | package/app_category=2 35 | package/retain_data_on_uninstall=false 36 | package/exclude_from_recents=false 37 | package/show_in_android_tv=false 38 | package/show_in_app_library=true 39 | package/show_as_launcher_app=false 40 | launcher_icons/main_192x192="" 41 | launcher_icons/adaptive_foreground_432x432="" 42 | launcher_icons/adaptive_background_432x432="" 43 | graphics/opengl_debug=false 44 | xr_features/xr_mode=0 45 | screen/immersive_mode=true 46 | screen/support_small=true 47 | screen/support_normal=true 48 | screen/support_large=true 49 | screen/support_xlarge=true 50 | user_data_backup/allow=false 51 | command_line/extra_args="" 52 | apk_expansion/enable=false 53 | apk_expansion/SALT="" 54 | apk_expansion/public_key="" 55 | permissions/custom_permissions=PackedStringArray() 56 | permissions/access_checkin_properties=false 57 | permissions/access_coarse_location=false 58 | permissions/access_fine_location=false 59 | permissions/access_location_extra_commands=false 60 | permissions/access_mock_location=false 61 | permissions/access_network_state=false 62 | permissions/access_surface_flinger=false 63 | permissions/access_wifi_state=false 64 | permissions/account_manager=false 65 | permissions/add_voicemail=false 66 | permissions/authenticate_accounts=false 67 | permissions/battery_stats=false 68 | permissions/bind_accessibility_service=false 69 | permissions/bind_appwidget=false 70 | permissions/bind_device_admin=false 71 | permissions/bind_input_method=false 72 | permissions/bind_nfc_service=false 73 | permissions/bind_notification_listener_service=false 74 | permissions/bind_print_service=false 75 | permissions/bind_remoteviews=false 76 | permissions/bind_text_service=false 77 | permissions/bind_vpn_service=false 78 | permissions/bind_wallpaper=false 79 | permissions/bluetooth=false 80 | permissions/bluetooth_admin=false 81 | permissions/bluetooth_privileged=false 82 | permissions/brick=false 83 | permissions/broadcast_package_removed=false 84 | permissions/broadcast_sms=false 85 | permissions/broadcast_sticky=false 86 | permissions/broadcast_wap_push=false 87 | permissions/call_phone=false 88 | permissions/call_privileged=false 89 | permissions/camera=false 90 | permissions/capture_audio_output=false 91 | permissions/capture_secure_video_output=false 92 | permissions/capture_video_output=false 93 | permissions/change_component_enabled_state=false 94 | permissions/change_configuration=false 95 | permissions/change_network_state=false 96 | permissions/change_wifi_multicast_state=false 97 | permissions/change_wifi_state=false 98 | permissions/clear_app_cache=false 99 | permissions/clear_app_user_data=false 100 | permissions/control_location_updates=false 101 | permissions/delete_cache_files=false 102 | permissions/delete_packages=false 103 | permissions/device_power=false 104 | permissions/diagnostic=false 105 | permissions/disable_keyguard=false 106 | permissions/dump=false 107 | permissions/expand_status_bar=false 108 | permissions/factory_test=false 109 | permissions/flashlight=false 110 | permissions/force_back=false 111 | permissions/get_accounts=false 112 | permissions/get_package_size=false 113 | permissions/get_tasks=false 114 | permissions/get_top_activity_info=false 115 | permissions/global_search=false 116 | permissions/hardware_test=false 117 | permissions/inject_events=false 118 | permissions/install_location_provider=false 119 | permissions/install_packages=false 120 | permissions/install_shortcut=false 121 | permissions/internal_system_window=false 122 | permissions/internet=true 123 | permissions/kill_background_processes=false 124 | permissions/location_hardware=false 125 | permissions/manage_accounts=false 126 | permissions/manage_app_tokens=false 127 | permissions/manage_documents=false 128 | permissions/manage_external_storage=false 129 | permissions/master_clear=false 130 | permissions/media_content_control=false 131 | permissions/modify_audio_settings=false 132 | permissions/modify_phone_state=false 133 | permissions/mount_format_filesystems=false 134 | permissions/mount_unmount_filesystems=false 135 | permissions/nfc=false 136 | permissions/persistent_activity=false 137 | permissions/process_outgoing_calls=false 138 | permissions/read_calendar=false 139 | permissions/read_call_log=false 140 | permissions/read_contacts=false 141 | permissions/read_external_storage=false 142 | permissions/read_frame_buffer=false 143 | permissions/read_history_bookmarks=false 144 | permissions/read_input_state=false 145 | permissions/read_logs=false 146 | permissions/read_phone_state=false 147 | permissions/read_profile=false 148 | permissions/read_sms=false 149 | permissions/read_social_stream=false 150 | permissions/read_sync_settings=false 151 | permissions/read_sync_stats=false 152 | permissions/read_user_dictionary=false 153 | permissions/reboot=false 154 | permissions/receive_boot_completed=false 155 | permissions/receive_mms=false 156 | permissions/receive_sms=false 157 | permissions/receive_wap_push=false 158 | permissions/record_audio=false 159 | permissions/reorder_tasks=false 160 | permissions/restart_packages=false 161 | permissions/send_respond_via_message=false 162 | permissions/send_sms=false 163 | permissions/set_activity_watcher=false 164 | permissions/set_alarm=false 165 | permissions/set_always_finish=false 166 | permissions/set_animation_scale=false 167 | permissions/set_debug_app=false 168 | permissions/set_orientation=false 169 | permissions/set_pointer_speed=false 170 | permissions/set_preferred_applications=false 171 | permissions/set_process_limit=false 172 | permissions/set_time=false 173 | permissions/set_time_zone=false 174 | permissions/set_wallpaper=false 175 | permissions/set_wallpaper_hints=false 176 | permissions/signal_persistent_processes=false 177 | permissions/status_bar=false 178 | permissions/subscribed_feeds_read=false 179 | permissions/subscribed_feeds_write=false 180 | permissions/system_alert_window=false 181 | permissions/transmit_ir=false 182 | permissions/uninstall_shortcut=false 183 | permissions/update_device_stats=false 184 | permissions/use_credentials=false 185 | permissions/use_sip=false 186 | permissions/vibrate=false 187 | permissions/wake_lock=false 188 | permissions/write_apn_settings=false 189 | permissions/write_calendar=false 190 | permissions/write_call_log=false 191 | permissions/write_contacts=false 192 | permissions/write_external_storage=false 193 | permissions/write_gservices=false 194 | permissions/write_history_bookmarks=false 195 | permissions/write_profile=false 196 | permissions/write_secure_settings=false 197 | permissions/write_settings=false 198 | permissions/write_sms=false 199 | permissions/write_social_stream=false 200 | permissions/write_sync_settings=false 201 | permissions/write_user_dictionary=false 202 | -------------------------------------------------------------------------------- /demo/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="Admob Demo" 14 | run/main_scene="res://Main.tscn" 15 | config/features=PackedStringArray("4.4", "GL Compatibility") 16 | config/icon="res://admob.png" 17 | 18 | [debug] 19 | 20 | settings/stdout/verbose_stdout=true 21 | 22 | [display] 23 | 24 | window/size/viewport_width=450 25 | window/size/viewport_height=800 26 | window/stretch/mode="canvas_items" 27 | window/stretch/aspect="ignore" 28 | window/handheld/orientation=1 29 | 30 | [editor_plugins] 31 | 32 | enabled=PackedStringArray("res://addons/AdmobPlugin/plugin.cfg") 33 | 34 | [rendering] 35 | 36 | renderer/rendering_method="gl_compatibility" 37 | renderer/rendering_method.mobile="gl_compatibility" 38 | textures/vram_compression/import_etc2_astc=true 39 | -------------------------------------------------------------------------------- /demo/scene/Scene2.gd: -------------------------------------------------------------------------------- 1 | # 2 | # © 2024-present https://github.com/cengiz-pz 3 | # 4 | 5 | extends Node 6 | 7 | @onready var admob: Admob = $Admob as Admob 8 | @onready var show_banner_button: Button = $CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/ButtonsHBoxContainer/ShowBannerButton 9 | @onready var hide_banner_button: Button = $CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/ButtonsHBoxContainer/HideBannerButton 10 | @onready var reload_banner_button: Button = $CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/ButtonsHBoxContainer/ReloadBannerButton 11 | @onready var banner_position_option_button: OptionButton = $CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/PositionHBoxContainer/OptionButton 12 | @onready var banner_size_option_button: OptionButton = $CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/SizeHBoxContainer/OptionButton 13 | @onready var interstitial_button: Button = $CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Other/InterstitialButton 14 | @onready var rewarded_button: Button = $CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Other/RewardedButton 15 | @onready var rewarded_interstitial_button: Button = $CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Other/RewardedInterstitialButton 16 | @onready var _label: RichTextLabel = $CanvasLayer/CenterContainer/VBoxContainer/RichTextLabel as RichTextLabel 17 | @onready var _geography_option_button: OptionButton = $CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/GeographyHBoxContainer/OptionButton 18 | 19 | var _is_banner_loaded: bool = false 20 | var _is_interstitial_loaded: bool = false 21 | var _is_rewarded_video_loaded: bool = false 22 | var _is_rewarded_interstitial_loaded: bool = false 23 | 24 | 25 | func _ready() -> void: 26 | admob.initialize() 27 | 28 | var __index: int = 0 29 | for __item: String in LoadAdRequest.AdPosition.keys(): 30 | banner_position_option_button.add_item(__item) 31 | if __item.casecmp_to(LoadAdRequest.AdPosition.keys()[admob.banner_position]) == 0: 32 | banner_position_option_button.select(__index) 33 | __index += 1 34 | 35 | __index = 0 36 | for __item in LoadAdRequest.AdSize.keys(): 37 | banner_size_option_button.add_item(__item) 38 | if __item.casecmp_to(LoadAdRequest.AdSize.keys()[admob.banner_size]) == 0: 39 | banner_size_option_button.select(__index) 40 | __index += 1 41 | 42 | 43 | func _on_admob_initialization_completed(_status_data: InitializationStatus) -> void: 44 | _process_consent_status(admob.get_consent_status()) 45 | admob.load_banner_ad() 46 | admob.load_interstitial_ad() 47 | admob.load_rewarded_ad() 48 | admob.load_rewarded_interstitial_ad() 49 | 50 | 51 | func _on_size_button_pressed() -> void: 52 | print(" ------- Get banner size button PRESSED") 53 | if _is_banner_loaded: 54 | _print_to_screen("Banner size: " + str(admob.get_banner_dimension())) 55 | 56 | 57 | func _on_pixel_size_button_pressed() -> void: 58 | print(" ------- Get banner pixel size button PRESSED") 59 | if _is_banner_loaded: 60 | _print_to_screen("Banner size in pixels: " + str(admob.get_banner_dimension_in_pixels())) 61 | 62 | 63 | func _on_show_banner_button_pressed() -> void: 64 | print(" ------- Show banner button PRESSED") 65 | if _is_banner_loaded: 66 | show_banner_button.disabled = true 67 | hide_banner_button.disabled = false 68 | admob.show_banner_ad() 69 | 70 | 71 | func _on_hide_banner_button_pressed() -> void: 72 | print(" ------- Hide banner button PRESSED") 73 | if _is_banner_loaded: 74 | show_banner_button.disabled = false 75 | hide_banner_button.disabled = true 76 | admob.hide_banner_ad() 77 | 78 | 79 | func _on_reload_banner_button_pressed() -> void: 80 | print(" ------- Reload banner button PRESSED") 81 | if _is_banner_loaded: 82 | _is_banner_loaded = false 83 | show_banner_button.disabled = true 84 | reload_banner_button.disabled = true 85 | admob.remove_banner_ad(admob._active_banner_ads[0]) 86 | admob.banner_position = LoadAdRequest.AdPosition[banner_position_option_button.get_item_text(banner_position_option_button.selected)] 87 | admob.banner_size = LoadAdRequest.AdSize[banner_size_option_button.get_item_text(banner_size_option_button.selected)] 88 | admob.load_banner_ad() 89 | 90 | 91 | func _on_interstitial_button_pressed() -> void: 92 | print(" ------- Interstitial button PRESSED") 93 | if _is_interstitial_loaded: 94 | _is_interstitial_loaded = false 95 | interstitial_button.disabled = true 96 | admob.show_interstitial_ad() 97 | else: 98 | admob.load_interstitial_ad() 99 | 100 | 101 | func _on_rewarded_video_button_pressed() -> void: 102 | print(" ------- Rewarded button PRESSED") 103 | if _is_rewarded_video_loaded: 104 | _is_rewarded_video_loaded = false 105 | rewarded_button.disabled = true 106 | admob.show_rewarded_ad() 107 | else: 108 | admob.load_rewarded_ad() 109 | 110 | 111 | func _on_rewarded_interstitial_button_pressed() -> void: 112 | print(" ------- Rewarded interstitial button PRESSED") 113 | if _is_rewarded_interstitial_loaded: 114 | _is_rewarded_interstitial_loaded = false 115 | rewarded_interstitial_button.disabled = true 116 | admob.show_rewarded_interstitial_ad() 117 | else: 118 | admob.load_rewarded_interstitial_ad() 119 | 120 | 121 | func _on_reset_consent_button_pressed() -> void: 122 | admob.reset_consent_info() 123 | 124 | 125 | func _on_admob_banner_ad_loaded(ad_id: String) -> void: 126 | _is_banner_loaded = true 127 | show_banner_button.disabled = false 128 | reload_banner_button.disabled = false 129 | _print_to_screen("banner loaded: %s" % ad_id) 130 | 131 | 132 | func _on_admob_banner_ad_refreshed(ad_id: String) -> void: 133 | _print_to_screen("banner refreshed: %s" % ad_id) 134 | 135 | 136 | func _on_admob_banner_ad_failed_to_load(ad_id: String, error_data: LoadAdError) -> void: 137 | _print_to_screen("banner %s failed to load. error: %d, message: %s" % 138 | [ad_id, error_data.get_code(), error_data.get_message()], true) 139 | 140 | 141 | func _on_admob_interstitial_ad_loaded(ad_id: String) -> void: 142 | _is_interstitial_loaded = true 143 | interstitial_button.disabled = false 144 | _print_to_screen("interstitial loaded: %s" % ad_id) 145 | 146 | 147 | func _on_admob_interstitial_ad_failed_to_load(ad_id: String, error_data: LoadAdError) -> void: 148 | _print_to_screen("interstitial %s failed to load. error: %d, message: %s" % 149 | [ad_id, error_data.get_code(), error_data.get_message()], true) 150 | 151 | 152 | func _on_admob_interstitial_ad_refreshed(ad_id: String) -> void: 153 | _print_to_screen("interstitial refreshed: %s" % ad_id) 154 | 155 | 156 | func _on_admob_interstitial_ad_dismissed_full_screen_content(ad_id: String) -> void: 157 | _print_to_screen("interstitial closed: %s" % ad_id) 158 | 159 | 160 | func _on_admob_rewarded_ad_loaded(ad_id: String) -> void: 161 | _is_rewarded_video_loaded = true 162 | rewarded_button.disabled = false 163 | _print_to_screen("rewarded video loaded: %s" % ad_id) 164 | 165 | 166 | func _on_admob_rewarded_ad_failed_to_load(ad_id: String, error_data: LoadAdError) -> void: 167 | _print_to_screen("rewarded video %s failed to load. error: %d, message: %s" % 168 | [ad_id, error_data.get_code(), error_data.get_message()], true) 169 | 170 | 171 | func _on_admob_rewarded_ad_user_earned_reward(ad_id: String, reward_data: RewardItem) -> void: 172 | _print_to_screen("user rewarded for rewarded ad '%s' with %d %s" % 173 | [ad_id, reward_data.get_amount(), reward_data.get_type()]) 174 | 175 | 176 | func _on_admob_rewarded_interstitial_ad_loaded(ad_id: String) -> void: 177 | _is_rewarded_interstitial_loaded = true 178 | rewarded_interstitial_button.disabled = false 179 | _print_to_screen("rewarded interstitial loaded: %s" % ad_id) 180 | 181 | 182 | func _on_admob_rewarded_interstitial_ad_failed_to_load(ad_id: String, error_data: LoadAdError) -> void: 183 | _print_to_screen("rewarded interstitial %s failed to load. error: %d, message: %s" % 184 | [ad_id, error_data.get_code(), error_data.get_message()], true) 185 | 186 | 187 | func _on_admob_rewarded_interstitial_ad_user_earned_reward(ad_id: String, reward_data: RewardItem) -> void: 188 | _print_to_screen("user rewarded for rewarded interstitial ad '%s' with %d %s" % 189 | [ad_id, reward_data.get_amount(), reward_data.get_type()]) 190 | 191 | 192 | func _on_admob_consent_info_updated() -> void: 193 | _print_to_screen("consent info updated") 194 | _process_consent_status(admob.get_consent_status()) 195 | 196 | 197 | func _on_admob_consent_info_update_failed(a_error_data: FormError) -> void: 198 | _print_to_screen("consent info failed to update: %s" % a_error_data.get_message()) 199 | 200 | 201 | func _process_consent_status(a_consent_status: int) -> void: 202 | _print_to_screen("_process_consent_status(): consent status = %d" % a_consent_status) 203 | match a_consent_status: 204 | ConsentInformation.ConsentStatus.UNKNOWN: 205 | _print_to_screen("consent status is unknown") 206 | admob.update_consent_info(ConsentRequestParameters.new() 207 | .set_debug_geography(ConsentRequestParameters.DebugGeography.values()[_geography_option_button.selected]) 208 | .add_test_device_hashed_id(OS.get_unique_id())) 209 | ConsentInformation.ConsentStatus.NOT_REQUIRED: 210 | _print_to_screen("consent is not required") 211 | ConsentInformation.ConsentStatus.REQUIRED: 212 | _print_to_screen("consent is required") 213 | admob.load_consent_form() 214 | ConsentInformation.ConsentStatus.OBTAINED: 215 | _print_to_screen("consent has already been obtained") 216 | 217 | 218 | func _on_admob_consent_form_loaded() -> void: 219 | _print_to_screen("consent form has been loaded") 220 | admob.show_consent_form() 221 | 222 | 223 | func _on_admob_consent_form_failed_to_load(a_error_data: FormError) -> void: 224 | _print_to_screen("consent form failed to load %s" % a_error_data.get_message()) 225 | 226 | 227 | func _on_admob_consent_form_dismissed(a_error_data: FormError) -> void: 228 | _print_to_screen("consent form has been dismissed %s" % a_error_data.get_message()) 229 | 230 | 231 | func _on_update_consent_info_button_pressed() -> void: 232 | print("selected consent geography: %d" % _geography_option_button.selected) 233 | admob.update_consent_info(ConsentRequestParameters.new() 234 | .set_debug_geography(ConsentRequestParameters.DebugGeography.values()[_geography_option_button.selected]) 235 | .add_test_device_hashed_id(OS.get_unique_id())) 236 | 237 | 238 | func _on_main_scene_button_pressed() -> void: 239 | get_tree().change_scene_to_file("res://Main.tscn") 240 | 241 | 242 | func _print_to_screen(a_message: String, a_is_error: bool = false) -> void: 243 | _label.add_text("%s\n\n" % a_message) 244 | if a_is_error: 245 | printerr(a_message) 246 | else: 247 | print(a_message) 248 | -------------------------------------------------------------------------------- /demo/scene/Scene2.gd.uid: -------------------------------------------------------------------------------- 1 | uid://d1sj8uyx056mr 2 | -------------------------------------------------------------------------------- /demo/scene/Scene2.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=3 uid="uid://ob3wnlx8tywe"] 2 | 3 | [ext_resource type="Script" uid="uid://d1sj8uyx056mr" path="res://scene/Scene2.gd" id="1_vwd77"] 4 | [ext_resource type="Texture2D" uid="uid://bdjwuj5g73wl3" path="res://admob.png" id="2_uma2v"] 5 | [ext_resource type="Script" uid="uid://b8ayp8io5uvmn" path="res://addons/AdmobPlugin/Admob.gd" id="3_2ewgo"] 6 | 7 | [node name="Main" type="Node"] 8 | script = ExtResource("1_vwd77") 9 | 10 | [node name="CanvasLayer" type="CanvasLayer" parent="."] 11 | 12 | [node name="CenterContainer" type="CenterContainer" parent="CanvasLayer"] 13 | anchors_preset = 15 14 | anchor_right = 1.0 15 | anchor_bottom = 1.0 16 | grow_horizontal = 2 17 | grow_vertical = 2 18 | size_flags_horizontal = 3 19 | size_flags_vertical = 3 20 | 21 | [node name="VBoxContainer" type="VBoxContainer" parent="CanvasLayer/CenterContainer"] 22 | custom_minimum_size = Vector2(400, 0) 23 | layout_mode = 2 24 | theme_override_constants/separation = 20 25 | 26 | [node name="TextureRect" type="TextureRect" parent="CanvasLayer/CenterContainer/VBoxContainer"] 27 | custom_minimum_size = Vector2(0, 128) 28 | layout_mode = 2 29 | texture = ExtResource("2_uma2v") 30 | expand_mode = 3 31 | stretch_mode = 5 32 | 33 | [node name="HBoxContainer" type="HBoxContainer" parent="CanvasLayer/CenterContainer/VBoxContainer"] 34 | layout_mode = 2 35 | 36 | [node name="Label" type="Label" parent="CanvasLayer/CenterContainer/VBoxContainer/HBoxContainer"] 37 | layout_mode = 2 38 | size_flags_horizontal = 3 39 | theme_override_font_sizes/font_size = 24 40 | text = "Admob Demo (Scene 2)" 41 | horizontal_alignment = 1 42 | 43 | [node name="MainSceneButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/HBoxContainer"] 44 | layout_mode = 2 45 | text = "Switch" 46 | 47 | [node name="VBoxContainer" type="VBoxContainer" parent="CanvasLayer/CenterContainer/VBoxContainer"] 48 | layout_mode = 2 49 | theme_override_constants/separation = 11 50 | 51 | [node name="TabContainer" type="TabContainer" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer"] 52 | custom_minimum_size = Vector2(0, 165) 53 | layout_mode = 2 54 | current_tab = 0 55 | 56 | [node name="Banner" type="VBoxContainer" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer"] 57 | layout_mode = 2 58 | theme_override_constants/separation = 6 59 | metadata/_tab_index = 0 60 | 61 | [node name="PositionHBoxContainer" type="HBoxContainer" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner"] 62 | layout_mode = 2 63 | theme_override_constants/separation = 10 64 | 65 | [node name="Label" type="Label" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/PositionHBoxContainer"] 66 | layout_mode = 2 67 | text = "Banner Position:" 68 | 69 | [node name="OptionButton" type="OptionButton" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/PositionHBoxContainer"] 70 | layout_mode = 2 71 | size_flags_horizontal = 3 72 | 73 | [node name="SizeHBoxContainer" type="HBoxContainer" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner"] 74 | layout_mode = 2 75 | theme_override_constants/separation = 10 76 | 77 | [node name="Label" type="Label" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/SizeHBoxContainer"] 78 | layout_mode = 2 79 | text = "Banner Size:" 80 | 81 | [node name="OptionButton" type="OptionButton" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/SizeHBoxContainer"] 82 | layout_mode = 2 83 | size_flags_horizontal = 3 84 | 85 | [node name="SizeButtonHBoxContainer" type="HBoxContainer" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner"] 86 | layout_mode = 2 87 | theme_override_constants/separation = 30 88 | 89 | [node name="SizeButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/SizeButtonHBoxContainer"] 90 | layout_mode = 2 91 | size_flags_horizontal = 3 92 | text = "Get Size" 93 | 94 | [node name="PixelSizeButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/SizeButtonHBoxContainer"] 95 | layout_mode = 2 96 | size_flags_horizontal = 3 97 | text = "Get Size In Pixels" 98 | 99 | [node name="ButtonsHBoxContainer" type="HBoxContainer" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner"] 100 | layout_mode = 2 101 | theme_override_constants/separation = 45 102 | 103 | [node name="ShowBannerButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/ButtonsHBoxContainer"] 104 | custom_minimum_size = Vector2(100, 0) 105 | layout_mode = 2 106 | disabled = true 107 | text = "Show" 108 | 109 | [node name="HideBannerButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/ButtonsHBoxContainer"] 110 | custom_minimum_size = Vector2(100, 0) 111 | layout_mode = 2 112 | disabled = true 113 | text = "Hide" 114 | 115 | [node name="ReloadBannerButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/ButtonsHBoxContainer"] 116 | custom_minimum_size = Vector2(100, 0) 117 | layout_mode = 2 118 | disabled = true 119 | text = "Reload" 120 | 121 | [node name="Other" type="VBoxContainer" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer"] 122 | visible = false 123 | layout_mode = 2 124 | theme_override_constants/separation = 15 125 | metadata/_tab_index = 1 126 | 127 | [node name="InterstitialButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Other"] 128 | layout_mode = 2 129 | disabled = true 130 | text = "Show Interstitial Ad" 131 | 132 | [node name="RewardedButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Other"] 133 | layout_mode = 2 134 | disabled = true 135 | text = "Show Rewarded Ad" 136 | 137 | [node name="RewardedInterstitialButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Other"] 138 | layout_mode = 2 139 | disabled = true 140 | text = "Show Rewarded Interstitial Ad" 141 | 142 | [node name="GeographyHBoxContainer" type="HBoxContainer" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer"] 143 | layout_mode = 2 144 | 145 | [node name="Label" type="Label" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/GeographyHBoxContainer"] 146 | layout_mode = 2 147 | text = "Debug Geography: " 148 | 149 | [node name="OptionButton" type="OptionButton" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/GeographyHBoxContainer"] 150 | layout_mode = 2 151 | size_flags_horizontal = 3 152 | selected = 1 153 | item_count = 3 154 | popup/item_0/text = "DISABLED" 155 | popup/item_0/id = 0 156 | popup/item_1/text = "EEA" 157 | popup/item_1/id = 1 158 | popup/item_2/text = "NOT EEA" 159 | popup/item_2/id = 2 160 | 161 | [node name="HBoxContainer" type="HBoxContainer" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer"] 162 | layout_mode = 2 163 | theme_override_constants/separation = 10 164 | 165 | [node name="Label" type="Label" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/HBoxContainer"] 166 | layout_mode = 2 167 | text = "Consent Info: " 168 | 169 | [node name="UpdateConsentInfoButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/HBoxContainer"] 170 | layout_mode = 2 171 | size_flags_horizontal = 3 172 | text = "Update" 173 | 174 | [node name="ResetConsentButton" type="Button" parent="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/HBoxContainer"] 175 | layout_mode = 2 176 | size_flags_horizontal = 3 177 | text = "Reset" 178 | 179 | [node name="RichTextLabel" type="RichTextLabel" parent="CanvasLayer/CenterContainer/VBoxContainer"] 180 | custom_minimum_size = Vector2(0, 210) 181 | layout_mode = 2 182 | theme_override_font_sizes/normal_font_size = 14 183 | scroll_following = true 184 | 185 | [node name="Admob" type="Node" parent="."] 186 | script = ExtResource("3_2ewgo") 187 | debug_application_id = "ca-app-pub-3940256099942544~3347511713" 188 | debug_banner_id = "ca-app-pub-3940256099942544/6300978111" 189 | debug_interstitial_id = "ca-app-pub-3940256099942544/1033173712" 190 | debug_rewarded_id = "ca-app-pub-3940256099942544/5224354917" 191 | debug_rewarded_interstitial_id = "ca-app-pub-3940256099942544/5354046379" 192 | 193 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/HBoxContainer/MainSceneButton" to="." method="_on_main_scene_button_pressed"] 194 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/SizeButtonHBoxContainer/SizeButton" to="." method="_on_size_button_pressed"] 195 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/SizeButtonHBoxContainer/PixelSizeButton" to="." method="_on_pixel_size_button_pressed"] 196 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/ButtonsHBoxContainer/ShowBannerButton" to="." method="_on_show_banner_button_pressed"] 197 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/ButtonsHBoxContainer/HideBannerButton" to="." method="_on_hide_banner_button_pressed"] 198 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Banner/ButtonsHBoxContainer/ReloadBannerButton" to="." method="_on_reload_banner_button_pressed"] 199 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Other/InterstitialButton" to="." method="_on_interstitial_button_pressed"] 200 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Other/RewardedButton" to="." method="_on_rewarded_video_button_pressed"] 201 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/TabContainer/Other/RewardedInterstitialButton" to="." method="_on_rewarded_interstitial_button_pressed"] 202 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/HBoxContainer/UpdateConsentInfoButton" to="." method="_on_update_consent_info_button_pressed"] 203 | [connection signal="pressed" from="CanvasLayer/CenterContainer/VBoxContainer/VBoxContainer/HBoxContainer/ResetConsentButton" to="." method="_on_reset_consent_button_pressed"] 204 | [connection signal="banner_ad_failed_to_load" from="Admob" to="." method="_on_admob_banner_ad_failed_to_load"] 205 | [connection signal="banner_ad_loaded" from="Admob" to="." method="_on_admob_banner_ad_loaded"] 206 | [connection signal="banner_ad_refreshed" from="Admob" to="." method="_on_admob_banner_ad_refreshed"] 207 | [connection signal="consent_form_dismissed" from="Admob" to="." method="_on_admob_consent_form_dismissed"] 208 | [connection signal="consent_form_failed_to_load" from="Admob" to="." method="_on_admob_consent_form_failed_to_load"] 209 | [connection signal="consent_form_loaded" from="Admob" to="." method="_on_admob_consent_form_loaded"] 210 | [connection signal="consent_info_update_failed" from="Admob" to="." method="_on_admob_consent_info_update_failed"] 211 | [connection signal="consent_info_updated" from="Admob" to="." method="_on_admob_consent_info_updated"] 212 | [connection signal="initialization_completed" from="Admob" to="." method="_on_admob_initialization_completed"] 213 | [connection signal="interstitial_ad_dismissed_full_screen_content" from="Admob" to="." method="_on_admob_interstitial_ad_dismissed_full_screen_content"] 214 | [connection signal="interstitial_ad_failed_to_load" from="Admob" to="." method="_on_admob_interstitial_ad_failed_to_load"] 215 | [connection signal="interstitial_ad_loaded" from="Admob" to="." method="_on_admob_interstitial_ad_loaded"] 216 | [connection signal="interstitial_ad_refreshed" from="Admob" to="." method="_on_admob_interstitial_ad_refreshed"] 217 | [connection signal="rewarded_ad_failed_to_load" from="Admob" to="." method="_on_admob_rewarded_ad_failed_to_load"] 218 | [connection signal="rewarded_ad_loaded" from="Admob" to="." method="_on_admob_rewarded_ad_loaded"] 219 | [connection signal="rewarded_ad_user_earned_reward" from="Admob" to="." method="_on_admob_rewarded_ad_user_earned_reward"] 220 | [connection signal="rewarded_interstitial_ad_failed_to_load" from="Admob" to="." method="_on_admob_rewarded_interstitial_ad_failed_to_load"] 221 | [connection signal="rewarded_interstitial_ad_loaded" from="Admob" to="." method="_on_admob_rewarded_interstitial_ad_loaded"] 222 | [connection signal="rewarded_interstitial_ad_user_earned_reward" from="Admob" to="." method="_on_admob_rewarded_interstitial_ad_user_earned_reward"] 223 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | // 2 | // © 2024-present https://github.com/cengiz-pz 3 | // 4 | 5 | pluginManagement { 6 | repositories { 7 | gradlePluginPortal() 8 | google() 9 | mavenCentral() 10 | } 11 | } 12 | 13 | dependencyResolutionManagement { 14 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.name = "godot-android-admob-plugin" 22 | 23 | include(":admob") 24 | --------------------------------------------------------------------------------